2011-07-17 08:05:43 +00:00
|
|
|
|
'use strict';
|
|
|
|
|
|
|
2013-06-05 22:30:31 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @ngdoc overview
|
|
|
|
|
|
* @name ngRoute
|
|
|
|
|
|
* @description
|
|
|
|
|
|
*
|
2013-08-22 19:32:42 +00:00
|
|
|
|
* # ngRoute
|
2013-08-14 12:18:28 +00:00
|
|
|
|
*
|
2013-08-22 19:32:42 +00:00
|
|
|
|
* The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
|
2013-08-14 12:18:28 +00:00
|
|
|
|
*
|
2013-08-22 19:32:42 +00:00
|
|
|
|
* {@installModule route}
|
2013-08-13 16:49:29 +00:00
|
|
|
|
*
|
2013-10-17 02:48:32 +00:00
|
|
|
|
* <div doc-module-components="ngRoute"></div>
|
2013-06-05 22:30:31 +00:00
|
|
|
|
*/
|
2013-10-22 21:41:21 +00:00
|
|
|
|
/* global -ngRouteModule */
|
2013-06-05 22:30:31 +00:00
|
|
|
|
var ngRouteModule = angular.module('ngRoute', ['ng']).
|
|
|
|
|
|
provider('$route', $RouteProvider);
|
2012-02-29 22:56:29 +00:00
|
|
|
|
|
2011-02-15 06:12:45 +00:00
|
|
|
|
/**
|
2011-11-10 06:23:36 +00:00
|
|
|
|
* @ngdoc object
|
2013-06-05 22:30:31 +00:00
|
|
|
|
* @name ngRoute.$routeProvider
|
2012-02-29 22:56:29 +00:00
|
|
|
|
* @function
|
2011-02-15 06:12:45 +00:00
|
|
|
|
*
|
|
|
|
|
|
* @description
|
|
|
|
|
|
*
|
2013-06-05 22:30:31 +00:00
|
|
|
|
* Used for configuring routes. See {@link ngRoute.$route $route} for an example.
|
2013-08-22 19:32:42 +00:00
|
|
|
|
*
|
|
|
|
|
|
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
2011-02-15 06:12:45 +00:00
|
|
|
|
*/
|
2011-11-02 23:32:46 +00:00
|
|
|
|
function $RouteProvider(){
|
2013-10-10 08:11:21 +00:00
|
|
|
|
function inherit(parent, extra) {
|
|
|
|
|
|
return angular.extend(new (angular.extend(function() {}, {prototype:parent}))(), extra);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-02-15 23:45:07 +00:00
|
|
|
|
var routes = {};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @ngdoc method
|
2013-06-05 22:30:31 +00:00
|
|
|
|
* @name ngRoute.$routeProvider#when
|
|
|
|
|
|
* @methodOf ngRoute.$routeProvider
|
2012-02-15 23:45:07 +00:00
|
|
|
|
*
|
2012-03-20 07:38:08 +00:00
|
|
|
|
* @param {string} path Route path (matched against `$location.path`). If `$location.path`
|
2012-05-23 04:12:19 +00:00
|
|
|
|
* contains redundant trailing slash or is missing one, the route will still match and the
|
2013-01-14 19:01:51 +00:00
|
|
|
|
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
|
2012-03-20 07:38:08 +00:00
|
|
|
|
* route definition.
|
2013-01-14 19:01:51 +00:00
|
|
|
|
*
|
2012-11-12 21:09:53 +00:00
|
|
|
|
* * `path` can contain named groups starting with a colon (`:name`). All characters up
|
|
|
|
|
|
* to the next slash are matched and stored in `$routeParams` under the given `name`
|
2012-12-27 05:45:11 +00:00
|
|
|
|
* when the route matches.
|
2013-08-13 16:49:29 +00:00
|
|
|
|
* * `path` can contain named groups starting with a colon and ending with a star (`:name*`).
|
|
|
|
|
|
* All characters are eagerly stored in `$routeParams` under the given `name`
|
2012-12-27 05:45:11 +00:00
|
|
|
|
* when the route matches.
|
|
|
|
|
|
* * `path` can contain optional named groups with a question mark (`:name?`).
|
2012-11-12 21:09:53 +00:00
|
|
|
|
*
|
2012-12-27 05:45:11 +00:00
|
|
|
|
* For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
|
2012-11-12 21:09:53 +00:00
|
|
|
|
* `/color/brown/largecode/code/with/slashs/edit` and extract:
|
|
|
|
|
|
*
|
|
|
|
|
|
* * `color: brown`
|
|
|
|
|
|
* * `largecode: code/with/slashs`.
|
|
|
|
|
|
*
|
2013-01-14 19:01:51 +00:00
|
|
|
|
*
|
2012-02-15 23:45:07 +00:00
|
|
|
|
* @param {Object} route Mapping information to be assigned to `$route.current` on route
|
|
|
|
|
|
* match.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Object properties:
|
|
|
|
|
|
*
|
2013-10-22 21:41:21 +00:00
|
|
|
|
* - `controller` – `{(string|function()=}` – Controller fn that should be associated with
|
|
|
|
|
|
* newly created scope or the name of a {@link angular.Module#controller registered
|
|
|
|
|
|
* controller} if passed as a string.
|
2013-04-29 18:27:45 +00:00
|
|
|
|
* - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be
|
|
|
|
|
|
* published to scope under the `controllerAs` name.
|
feat($sce): new $sce service for Strict Contextual Escaping.
$sce is a service that provides Strict Contextual Escaping services to AngularJS.
Strict Contextual Escaping
--------------------------
Strict Contextual Escaping (SCE) is a mode in which AngularJS requires
bindings in certain contexts to result in a value that is marked as safe
to use for that context One example of such a context is binding
arbitrary html controlled by the user via ng-bind-html-unsafe. We
refer to these contexts as privileged or SCE contexts.
As of version 1.2, Angular ships with SCE enabled by default.
Note: When enabled (the default), IE8 in quirks mode is not supported.
In this mode, IE8 allows one to execute arbitrary javascript by the use
of the expression() syntax. Refer
http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx
to learn more about them. You can ensure your document is in standards
mode and not quirks mode by adding <!doctype html> to the top of your
HTML document.
SCE assists in writing code in way that (a) is secure by default and (b)
makes auditing for security vulnerabilities such as XSS, clickjacking,
etc. a lot easier.
Here's an example of a binding in a privileged context:
<input ng-model="userHtml">
<div ng-bind-html-unsafe="{{userHtml}}">
Notice that ng-bind-html-unsafe is bound to {{userHtml}} controlled by
the user. With SCE disabled, this application allows the user to render
arbitrary HTML into the DIV. In a more realistic example, one may be
rendering user comments, blog articles, etc. via bindings. (HTML is
just one example of a context where rendering user controlled input
creates security vulnerabilities.)
For the case of HTML, you might use a library, either on the client side, or on the server side,
to sanitize unsafe HTML before binding to the value and rendering it in the document.
How would you ensure that every place that used these types of bindings was bound to a value that
was sanitized by your library (or returned as safe for rendering by your server?) How can you
ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
properties/fields and forgot to update the binding to the sanitized value?
To be secure by default, you want to ensure that any such bindings are disallowed unless you can
determine that something explicitly says it's safe to use a value for binding in that
context. You can then audit your code (a simple grep would do) to ensure that this is only done
for those values that you can easily tell are safe - because they were received from your server,
sanitized by your library, etc. You can organize your codebase to help with this - perhaps
allowing only the files in a specific directory to do this. Ensuring that the internal API
exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
In the case of AngularJS' SCE service, one uses $sce.trustAs (and
shorthand methods such as $sce.trustAsHtml, etc.) to obtain values that
will be accepted by SCE / privileged contexts.
In privileged contexts, directives and code will bind to the result of
$sce.getTrusted(context, value) rather than to the value directly.
Directives use $sce.parseAs rather than $parse to watch attribute
bindings, which performs the $sce.getTrusted behind the scenes on
non-constant literals.
As an example, ngBindHtmlUnsafe uses $sce.parseAsHtml(binding
expression). Here's the actual code (slightly simplified):
var ngBindHtmlUnsafeDirective = ['$sce', function($sce) {
return function(scope, element, attr) {
scope.$watch($sce.parseAsHtml(attr.ngBindHtmlUnsafe), function(value) {
element.html(value || '');
});
};
}];
Impact on loading templates
---------------------------
This applies both to the ng-include directive as well as templateUrl's
specified by directives.
By default, Angular only loads templates from the same domain and
protocol as the application document. This is done by calling
$sce.getTrustedResourceUrl on the template URL. To load templates from
other domains and/or protocols, you may either either whitelist them or
wrap it into a trusted value.
*Please note*:
The browser's Same Origin Policy and Cross-Origin Resource Sharing
(CORS) policy apply in addition to this and may further restrict whether
the template is successfully loaded. This means that without the right
CORS policy, loading templates from a different domain won't work on all
browsers. Also, loading templates from file:// URL does not work on
some browsers.
This feels like too much overhead for the developer?
----------------------------------------------------
It's important to remember that SCE only applies to interpolation expressions.
If your expressions are constant literals, they're automatically trusted
and you don't need to call $sce.trustAs on them.
e.g. <div ng-html-bind-unsafe="'<b>implicitly trusted</b>'"></div> just works.
Additionally, a[href] and img[src] automatically sanitize their URLs and
do not pass them through $sce.getTrusted. SCE doesn't play a role here.
The included $sceDelegate comes with sane defaults to allow you to load
templates in ng-include from your application's domain without having to
even know about SCE. It blocks loading templates from other domains or
loading templates over http from an https served document. You can
change these by setting your own custom whitelists and blacklists for
matching such URLs.
This significantly reduces the overhead. It is far easier to pay the
small overhead and have an application that's secure and can be audited
to verify that with much more ease than bolting security onto an
application later.
2013-05-14 21:51:39 +00:00
|
|
|
|
* - `template` – `{string=|function()=}` – html template as a string or a function that
|
|
|
|
|
|
* returns an html template as a string which should be used by {@link
|
|
|
|
|
|
* ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
|
2012-11-03 20:11:07 +00:00
|
|
|
|
* This property takes precedence over `templateUrl`.
|
|
|
|
|
|
*
|
|
|
|
|
|
* If `template` is a function, it will be called with the following parameters:
|
|
|
|
|
|
*
|
|
|
|
|
|
* - `{Array.<Object>}` - route parameters extracted from the current
|
|
|
|
|
|
* `$location.path()` by applying the current route
|
|
|
|
|
|
*
|
|
|
|
|
|
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
|
2013-06-05 22:30:31 +00:00
|
|
|
|
* template that should be used by {@link ngRoute.directive:ngView ngView}.
|
2012-11-03 20:11:07 +00:00
|
|
|
|
*
|
|
|
|
|
|
* If `templateUrl` is a function, it will be called with the following parameters:
|
|
|
|
|
|
*
|
|
|
|
|
|
* - `{Array.<Object>}` - route parameters extracted from the current
|
|
|
|
|
|
* `$location.path()` by applying the current route
|
|
|
|
|
|
*
|
2012-05-23 04:12:19 +00:00
|
|
|
|
* - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
|
2013-10-24 21:36:05 +00:00
|
|
|
|
* be injected into the controller. If any of these dependencies are promises, the router
|
|
|
|
|
|
* will wait for them all to be resolved or one to be rejected before the controller is
|
|
|
|
|
|
* instantiated.
|
|
|
|
|
|
* If all the promises are resolved successfully, the values of the resolved promises are
|
|
|
|
|
|
* injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is
|
|
|
|
|
|
* fired. If any of the promises are rejected the
|
|
|
|
|
|
* {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object
|
|
|
|
|
|
* is:
|
2012-05-23 04:12:19 +00:00
|
|
|
|
*
|
|
|
|
|
|
* - `key` – `{string}`: a name of a dependency to be injected into the controller.
|
|
|
|
|
|
* - `factory` - `{string|function}`: If `string` then it is an alias for a service.
|
2012-06-12 06:49:24 +00:00
|
|
|
|
* Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected}
|
2013-10-22 21:41:21 +00:00
|
|
|
|
* and the return value is treated as the dependency. If the result is a promise, it is
|
|
|
|
|
|
* resolved before its value is injected into the controller. Be aware that
|
|
|
|
|
|
* `ngRoute.$routeParams` will still refer to the previous route within these resolve
|
|
|
|
|
|
* functions. Use `$route.current.params` to access the new route parameters, instead.
|
2012-05-23 04:12:19 +00:00
|
|
|
|
*
|
2012-02-15 23:45:07 +00:00
|
|
|
|
* - `redirectTo` – {(string|function())=} – value to update
|
2012-06-12 06:49:24 +00:00
|
|
|
|
* {@link ng.$location $location} path with and trigger route redirection.
|
2012-02-15 23:45:07 +00:00
|
|
|
|
*
|
|
|
|
|
|
* If `redirectTo` is a function, it will be called with the following parameters:
|
|
|
|
|
|
*
|
|
|
|
|
|
* - `{Object.<string>}` - route parameters extracted from the current
|
2012-05-17 23:36:18 +00:00
|
|
|
|
* `$location.path()` by applying the current route templateUrl.
|
2012-02-15 23:45:07 +00:00
|
|
|
|
* - `{string}` - current `$location.path()`
|
|
|
|
|
|
* - `{Object}` - current `$location.search()`
|
|
|
|
|
|
*
|
|
|
|
|
|
* The custom `redirectTo` function is expected to return a string which will be used
|
|
|
|
|
|
* to update `$location.path()` and `$location.search()`.
|
|
|
|
|
|
*
|
2013-09-06 18:16:00 +00:00
|
|
|
|
* - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()`
|
|
|
|
|
|
* or `$location.hash()` changes.
|
2012-02-15 23:45:07 +00:00
|
|
|
|
*
|
2012-02-29 22:56:29 +00:00
|
|
|
|
* If the option is set to `false` and url in the browser changes, then
|
|
|
|
|
|
* `$routeUpdate` event is broadcasted on the root scope.
|
2012-02-15 23:45:07 +00:00
|
|
|
|
*
|
2013-02-23 20:45:48 +00:00
|
|
|
|
* - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive
|
|
|
|
|
|
*
|
|
|
|
|
|
* If the option is set to `true`, then the particular route can be matched without being
|
|
|
|
|
|
* case sensitive
|
|
|
|
|
|
*
|
2012-04-03 22:28:09 +00:00
|
|
|
|
* @returns {Object} self
|
2012-02-15 23:45:07 +00:00
|
|
|
|
*
|
|
|
|
|
|
* @description
|
|
|
|
|
|
* Adds a new route definition to the `$route` service.
|
|
|
|
|
|
*/
|
|
|
|
|
|
this.when = function(path, route) {
|
2013-10-10 08:11:21 +00:00
|
|
|
|
routes[path] = angular.extend(
|
2012-12-27 05:45:11 +00:00
|
|
|
|
{reloadOnSearch: true},
|
|
|
|
|
|
route,
|
|
|
|
|
|
path && pathRegExp(path, route)
|
|
|
|
|
|
);
|
2012-03-20 07:38:08 +00:00
|
|
|
|
|
|
|
|
|
|
// create redirection for trailing slashes
|
|
|
|
|
|
if (path) {
|
|
|
|
|
|
var redirectPath = (path[path.length-1] == '/')
|
2013-08-13 16:49:29 +00:00
|
|
|
|
? path.substr(0, path.length-1)
|
|
|
|
|
|
: path +'/';
|
2012-03-20 07:38:08 +00:00
|
|
|
|
|
2013-10-10 08:11:21 +00:00
|
|
|
|
routes[redirectPath] = angular.extend(
|
2012-12-27 05:45:11 +00:00
|
|
|
|
{redirectTo: path},
|
|
|
|
|
|
pathRegExp(redirectPath, route)
|
|
|
|
|
|
);
|
2012-03-20 07:38:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-04-03 22:28:09 +00:00
|
|
|
|
return this;
|
2012-02-15 23:45:07 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
2012-12-27 05:45:11 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @param path {string} path
|
|
|
|
|
|
* @param opts {Object} options
|
|
|
|
|
|
* @return {?Object}
|
|
|
|
|
|
*
|
|
|
|
|
|
* @description
|
|
|
|
|
|
* Normalizes the given path, returning a regular expression
|
|
|
|
|
|
* and the original path.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Inspired by pathRexp in visionmedia/express/lib/utils.js.
|
|
|
|
|
|
*/
|
|
|
|
|
|
function pathRegExp(path, opts) {
|
|
|
|
|
|
var insensitive = opts.caseInsensitiveMatch,
|
|
|
|
|
|
ret = {
|
|
|
|
|
|
originalPath: path,
|
|
|
|
|
|
regexp: path
|
|
|
|
|
|
},
|
|
|
|
|
|
keys = ret.keys = [];
|
|
|
|
|
|
|
|
|
|
|
|
path = path
|
|
|
|
|
|
.replace(/([().])/g, '\\$1')
|
|
|
|
|
|
.replace(/(\/)?:(\w+)([\?|\*])?/g, function(_, slash, key, option){
|
|
|
|
|
|
var optional = option === '?' ? option : null;
|
|
|
|
|
|
var star = option === '*' ? option : null;
|
|
|
|
|
|
keys.push({ name: key, optional: !!optional });
|
|
|
|
|
|
slash = slash || '';
|
|
|
|
|
|
return ''
|
|
|
|
|
|
+ (optional ? '' : slash)
|
|
|
|
|
|
+ '(?:'
|
|
|
|
|
|
+ (optional ? slash : '')
|
2013-08-06 15:35:48 +00:00
|
|
|
|
+ (star && '(.+?)' || '([^/]+)')
|
|
|
|
|
|
+ (optional || '')
|
|
|
|
|
|
+ ')'
|
2012-12-27 05:45:11 +00:00
|
|
|
|
+ (optional || '');
|
|
|
|
|
|
})
|
|
|
|
|
|
.replace(/([\/$\*])/g, '\\$1');
|
|
|
|
|
|
|
|
|
|
|
|
ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-02-15 23:45:07 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @ngdoc method
|
2013-06-05 22:30:31 +00:00
|
|
|
|
* @name ngRoute.$routeProvider#otherwise
|
|
|
|
|
|
* @methodOf ngRoute.$routeProvider
|
2012-02-15 23:45:07 +00:00
|
|
|
|
*
|
|
|
|
|
|
* @description
|
|
|
|
|
|
* Sets route definition that will be used on route change when no other route definition
|
|
|
|
|
|
* is matched.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {Object} params Mapping information to be assigned to `$route.current`.
|
2012-04-03 22:28:09 +00:00
|
|
|
|
* @returns {Object} self
|
2012-02-15 23:45:07 +00:00
|
|
|
|
*/
|
|
|
|
|
|
this.otherwise = function(params) {
|
|
|
|
|
|
this.when(null, params);
|
2012-04-03 22:28:09 +00:00
|
|
|
|
return this;
|
2012-02-15 23:45:07 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-10-22 21:41:21 +00:00
|
|
|
|
this.$get = ['$rootScope',
|
|
|
|
|
|
'$location',
|
|
|
|
|
|
'$routeParams',
|
|
|
|
|
|
'$q',
|
|
|
|
|
|
'$injector',
|
|
|
|
|
|
'$http',
|
|
|
|
|
|
'$templateCache',
|
|
|
|
|
|
'$sce',
|
|
|
|
|
|
function($rootScope, $location, $routeParams, $q, $injector, $http, $templateCache, $sce) {
|
2012-02-29 22:56:29 +00:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @ngdoc object
|
2013-06-05 22:30:31 +00:00
|
|
|
|
* @name ngRoute.$route
|
2012-02-29 22:56:29 +00:00
|
|
|
|
* @requires $location
|
|
|
|
|
|
* @requires $routeParams
|
|
|
|
|
|
*
|
|
|
|
|
|
* @property {Object} current Reference to the current route definition.
|
2012-05-23 04:12:19 +00:00
|
|
|
|
* The route definition contains:
|
|
|
|
|
|
*
|
|
|
|
|
|
* - `controller`: The controller constructor as define in route definition.
|
2012-06-12 06:49:24 +00:00
|
|
|
|
* - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
|
2012-05-23 04:12:19 +00:00
|
|
|
|
* controller instantiation. The `locals` contain
|
|
|
|
|
|
* the resolved values of the `resolve` map. Additionally the `locals` also contain:
|
|
|
|
|
|
*
|
|
|
|
|
|
* - `$scope` - The current route scope.
|
|
|
|
|
|
* - `$template` - The current route template HTML.
|
|
|
|
|
|
*
|
2012-02-29 22:56:29 +00:00
|
|
|
|
* @property {Array.<Object>} routes Array of all configured routes.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @description
|
2013-08-22 19:32:42 +00:00
|
|
|
|
* `$route` is used for deep-linking URLs to controllers and views (HTML partials).
|
2012-02-29 22:56:29 +00:00
|
|
|
|
* It watches `$location.url()` and tries to map the path to an existing route definition.
|
|
|
|
|
|
*
|
2013-08-22 19:32:42 +00:00
|
|
|
|
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
|
|
|
|
|
*
|
2013-06-05 22:30:31 +00:00
|
|
|
|
* You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API.
|
2012-02-29 22:56:29 +00:00
|
|
|
|
*
|
2013-10-22 21:41:21 +00:00
|
|
|
|
* The `$route` service is typically used in conjunction with the
|
|
|
|
|
|
* {@link ngRoute.directive:ngView `ngView`} directive and the
|
|
|
|
|
|
* {@link ngRoute.$routeParams `$routeParams`} service.
|
2012-02-29 22:56:29 +00:00
|
|
|
|
*
|
|
|
|
|
|
* @example
|
|
|
|
|
|
This example shows how changing the URL hash causes the `$route` to match a route against the
|
2012-04-06 23:35:17 +00:00
|
|
|
|
URL, and the `ngView` pulls in the partial.
|
2012-02-29 22:56:29 +00:00
|
|
|
|
|
2012-06-12 06:49:24 +00:00
|
|
|
|
Note that this example is using {@link ng.directive:script inlined templates}
|
2012-02-29 22:56:29 +00:00
|
|
|
|
to get it working on jsfiddle as well.
|
|
|
|
|
|
|
2013-06-05 22:30:31 +00:00
|
|
|
|
<example module="ngView" deps="angular-route.js">
|
2012-04-29 05:45:28 +00:00
|
|
|
|
<file name="index.html">
|
|
|
|
|
|
<div ng-controller="MainCntl">
|
|
|
|
|
|
Choose:
|
|
|
|
|
|
<a href="Book/Moby">Moby</a> |
|
|
|
|
|
|
<a href="Book/Moby/ch/1">Moby: Ch1</a> |
|
|
|
|
|
|
<a href="Book/Gatsby">Gatsby</a> |
|
|
|
|
|
|
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
|
|
|
|
|
|
<a href="Book/Scarlet">Scarlet Letter</a><br/>
|
|
|
|
|
|
|
|
|
|
|
|
<div ng-view></div>
|
|
|
|
|
|
<hr />
|
|
|
|
|
|
|
|
|
|
|
|
<pre>$location.path() = {{$location.path()}}</pre>
|
2012-05-17 23:36:18 +00:00
|
|
|
|
<pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
|
2012-04-29 05:45:28 +00:00
|
|
|
|
<pre>$route.current.params = {{$route.current.params}}</pre>
|
|
|
|
|
|
<pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
|
|
|
|
|
|
<pre>$routeParams = {{$routeParams}}</pre>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</file>
|
|
|
|
|
|
|
|
|
|
|
|
<file name="book.html">
|
|
|
|
|
|
controller: {{name}}<br />
|
|
|
|
|
|
Book Id: {{params.bookId}}<br />
|
|
|
|
|
|
</file>
|
|
|
|
|
|
|
|
|
|
|
|
<file name="chapter.html">
|
|
|
|
|
|
controller: {{name}}<br />
|
|
|
|
|
|
Book Id: {{params.bookId}}<br />
|
|
|
|
|
|
Chapter Id: {{params.chapterId}}
|
|
|
|
|
|
</file>
|
|
|
|
|
|
|
|
|
|
|
|
<file name="script.js">
|
2013-07-30 16:20:42 +00:00
|
|
|
|
angular.module('ngView', ['ngRoute']).config(function($routeProvider, $locationProvider) {
|
2012-04-29 05:45:28 +00:00
|
|
|
|
$routeProvider.when('/Book/:bookId', {
|
2012-05-17 23:36:18 +00:00
|
|
|
|
templateUrl: 'book.html',
|
2012-05-23 04:12:19 +00:00
|
|
|
|
controller: BookCntl,
|
|
|
|
|
|
resolve: {
|
|
|
|
|
|
// I will cause a 1 second delay
|
|
|
|
|
|
delay: function($q, $timeout) {
|
|
|
|
|
|
var delay = $q.defer();
|
|
|
|
|
|
$timeout(delay.resolve, 1000);
|
|
|
|
|
|
return delay.promise;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2012-04-29 05:45:28 +00:00
|
|
|
|
});
|
|
|
|
|
|
$routeProvider.when('/Book/:bookId/ch/:chapterId', {
|
2012-05-17 23:36:18 +00:00
|
|
|
|
templateUrl: 'chapter.html',
|
2012-04-29 05:45:28 +00:00
|
|
|
|
controller: ChapterCntl
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// configure html5 to get links working on jsfiddle
|
|
|
|
|
|
$locationProvider.html5Mode(true);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
function MainCntl($scope, $route, $routeParams, $location) {
|
|
|
|
|
|
$scope.$route = $route;
|
|
|
|
|
|
$scope.$location = $location;
|
|
|
|
|
|
$scope.$routeParams = $routeParams;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function BookCntl($scope, $routeParams) {
|
|
|
|
|
|
$scope.name = "BookCntl";
|
|
|
|
|
|
$scope.params = $routeParams;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function ChapterCntl($scope, $routeParams) {
|
|
|
|
|
|
$scope.name = "ChapterCntl";
|
|
|
|
|
|
$scope.params = $routeParams;
|
|
|
|
|
|
}
|
|
|
|
|
|
</file>
|
|
|
|
|
|
|
|
|
|
|
|
<file name="scenario.js">
|
|
|
|
|
|
it('should load and compile correct template', function() {
|
|
|
|
|
|
element('a:contains("Moby: Ch1")').click();
|
|
|
|
|
|
var content = element('.doc-example-live [ng-view]').text();
|
|
|
|
|
|
expect(content).toMatch(/controller\: ChapterCntl/);
|
|
|
|
|
|
expect(content).toMatch(/Book Id\: Moby/);
|
|
|
|
|
|
expect(content).toMatch(/Chapter Id\: 1/);
|
|
|
|
|
|
|
|
|
|
|
|
element('a:contains("Scarlet")').click();
|
2012-05-23 04:12:19 +00:00
|
|
|
|
sleep(2); // promises are not part of scenario waiting
|
2012-04-29 05:45:28 +00:00
|
|
|
|
content = element('.doc-example-live [ng-view]').text();
|
|
|
|
|
|
expect(content).toMatch(/controller\: BookCntl/);
|
|
|
|
|
|
expect(content).toMatch(/Book Id\: Scarlet/);
|
|
|
|
|
|
});
|
|
|
|
|
|
</file>
|
|
|
|
|
|
</example>
|
2012-02-29 22:56:29 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
2011-11-02 23:32:46 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @ngdoc event
|
2013-06-05 22:30:31 +00:00
|
|
|
|
* @name ngRoute.$route#$routeChangeStart
|
|
|
|
|
|
* @eventOf ngRoute.$route
|
2011-11-02 23:32:46 +00:00
|
|
|
|
* @eventType broadcast on root scope
|
|
|
|
|
|
* @description
|
2012-05-23 04:12:19 +00:00
|
|
|
|
* Broadcasted before a route change. At this point the route services starts
|
|
|
|
|
|
* resolving all of the dependencies needed for the route change to occurs.
|
|
|
|
|
|
* Typically this involves fetching the view template as well as any dependencies
|
|
|
|
|
|
* defined in `resolve` route property. Once all of the dependencies are resolved
|
2012-06-01 18:31:10 +00:00
|
|
|
|
* `$routeChangeSuccess` is fired.
|
2011-11-02 23:32:46 +00:00
|
|
|
|
*
|
2013-09-23 18:55:41 +00:00
|
|
|
|
* @param {Object} angularEvent Synthetic event object.
|
2011-11-02 23:32:46 +00:00
|
|
|
|
* @param {Route} next Future route information.
|
|
|
|
|
|
* @param {Route} current Current route information.
|
|
|
|
|
|
*/
|
2011-08-24 05:30:14 +00:00
|
|
|
|
|
2011-11-02 23:32:46 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @ngdoc event
|
2013-06-05 22:30:31 +00:00
|
|
|
|
* @name ngRoute.$route#$routeChangeSuccess
|
|
|
|
|
|
* @eventOf ngRoute.$route
|
2011-11-02 23:32:46 +00:00
|
|
|
|
* @eventType broadcast on root scope
|
|
|
|
|
|
* @description
|
2012-05-23 04:12:19 +00:00
|
|
|
|
* Broadcasted after a route dependencies are resolved.
|
2013-06-05 22:30:31 +00:00
|
|
|
|
* {@link ngRoute.directive:ngView ngView} listens for the directive
|
2012-05-23 04:12:19 +00:00
|
|
|
|
* to instantiate the controller and render the view.
|
2011-11-02 23:32:46 +00:00
|
|
|
|
*
|
2013-03-15 09:24:52 +00:00
|
|
|
|
* @param {Object} angularEvent Synthetic event object.
|
2011-11-02 23:32:46 +00:00
|
|
|
|
* @param {Route} current Current route information.
|
2013-10-22 21:41:21 +00:00
|
|
|
|
* @param {Route|Undefined} previous Previous route information, or undefined if current is
|
|
|
|
|
|
* first route entered.
|
2011-11-02 23:32:46 +00:00
|
|
|
|
*/
|
2011-08-24 05:30:14 +00:00
|
|
|
|
|
2012-05-23 04:12:19 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @ngdoc event
|
2013-06-05 22:30:31 +00:00
|
|
|
|
* @name ngRoute.$route#$routeChangeError
|
|
|
|
|
|
* @eventOf ngRoute.$route
|
2012-05-23 04:12:19 +00:00
|
|
|
|
* @eventType broadcast on root scope
|
|
|
|
|
|
* @description
|
|
|
|
|
|
* Broadcasted if any of the resolve promises are rejected.
|
|
|
|
|
|
*
|
2013-09-12 17:02:59 +00:00
|
|
|
|
* @param {Object} angularEvent Synthetic event object
|
2012-05-23 04:12:19 +00:00
|
|
|
|
* @param {Route} current Current route information.
|
|
|
|
|
|
* @param {Route} previous Previous route information.
|
|
|
|
|
|
* @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2011-11-02 23:32:46 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @ngdoc event
|
2013-06-05 22:30:31 +00:00
|
|
|
|
* @name ngRoute.$route#$routeUpdate
|
|
|
|
|
|
* @eventOf ngRoute.$route
|
2012-02-19 20:59:10 +00:00
|
|
|
|
* @eventType broadcast on root scope
|
2011-11-02 23:32:46 +00:00
|
|
|
|
* @description
|
|
|
|
|
|
*
|
|
|
|
|
|
* The `reloadOnSearch` property has been set to false, and we are reusing the same
|
|
|
|
|
|
* instance of the Controller.
|
|
|
|
|
|
*/
|
2011-08-24 05:30:14 +00:00
|
|
|
|
|
2013-01-14 19:01:51 +00:00
|
|
|
|
var forceReload = false,
|
2011-11-02 23:32:46 +00:00
|
|
|
|
$route = {
|
|
|
|
|
|
routes: routes,
|
2011-02-15 06:12:45 +00:00
|
|
|
|
|
2011-11-02 23:32:46 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @ngdoc method
|
2013-06-05 22:30:31 +00:00
|
|
|
|
* @name ngRoute.$route#reload
|
|
|
|
|
|
* @methodOf ngRoute.$route
|
2011-11-02 23:32:46 +00:00
|
|
|
|
*
|
|
|
|
|
|
* @description
|
2012-05-23 04:12:19 +00:00
|
|
|
|
* Causes `$route` service to reload the current route even if
|
2012-06-12 06:49:24 +00:00
|
|
|
|
* {@link ng.$location $location} hasn't changed.
|
2012-02-29 22:56:29 +00:00
|
|
|
|
*
|
2013-06-05 22:30:31 +00:00
|
|
|
|
* As a result of that, {@link ngRoute.directive:ngView ngView}
|
2012-02-29 22:56:29 +00:00
|
|
|
|
* creates new scope, reinstantiates the controller.
|
2011-11-02 23:32:46 +00:00
|
|
|
|
*/
|
|
|
|
|
|
reload: function() {
|
|
|
|
|
|
forceReload = true;
|
2012-05-22 23:45:56 +00:00
|
|
|
|
$rootScope.$evalAsync(updateRoute);
|
2011-11-02 23:32:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
};
|
2011-02-15 06:12:45 +00:00
|
|
|
|
|
2012-05-22 23:45:56 +00:00
|
|
|
|
$rootScope.$on('$locationChangeSuccess', updateRoute);
|
2011-08-24 05:30:14 +00:00
|
|
|
|
|
2011-11-02 23:32:46 +00:00
|
|
|
|
return $route;
|
2011-08-24 05:30:14 +00:00
|
|
|
|
|
2011-11-02 23:32:46 +00:00
|
|
|
|
/////////////////////////////////////////////////////
|
2011-08-24 05:30:14 +00:00
|
|
|
|
|
2013-01-14 19:01:51 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @param on {string} current url
|
2012-12-27 05:45:11 +00:00
|
|
|
|
* @param route {Object} route regexp to match the url against
|
2013-01-14 19:01:51 +00:00
|
|
|
|
* @return {?Object}
|
2012-12-27 05:45:11 +00:00
|
|
|
|
*
|
|
|
|
|
|
* @description
|
|
|
|
|
|
* Check if the route matches the current url.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Inspired by match in
|
|
|
|
|
|
* visionmedia/express/lib/router/router.js.
|
2013-01-14 19:01:51 +00:00
|
|
|
|
*/
|
2012-12-27 05:45:11 +00:00
|
|
|
|
function switchRouteMatcher(on, route) {
|
|
|
|
|
|
var keys = route.keys,
|
|
|
|
|
|
params = {};
|
|
|
|
|
|
|
|
|
|
|
|
if (!route.regexp) return null;
|
|
|
|
|
|
|
|
|
|
|
|
var m = route.regexp.exec(on);
|
|
|
|
|
|
if (!m) return null;
|
|
|
|
|
|
|
|
|
|
|
|
for (var i = 1, len = m.length; i < len; ++i) {
|
|
|
|
|
|
var key = keys[i - 1];
|
|
|
|
|
|
|
|
|
|
|
|
var val = 'string' == typeof m[i]
|
2013-08-13 16:49:29 +00:00
|
|
|
|
? decodeURIComponent(m[i])
|
|
|
|
|
|
: m[i];
|
2012-12-27 05:45:11 +00:00
|
|
|
|
|
|
|
|
|
|
if (key && val) {
|
|
|
|
|
|
params[key.name] = val;
|
2012-11-12 21:09:53 +00:00
|
|
|
|
}
|
2011-11-02 23:32:46 +00:00
|
|
|
|
}
|
2012-12-27 05:45:11 +00:00
|
|
|
|
return params;
|
2011-02-15 06:12:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2011-11-02 23:32:46 +00:00
|
|
|
|
function updateRoute() {
|
|
|
|
|
|
var next = parseRoute(),
|
2012-02-19 20:31:11 +00:00
|
|
|
|
last = $route.current;
|
2011-02-15 06:12:45 +00:00
|
|
|
|
|
2013-03-08 19:43:37 +00:00
|
|
|
|
if (next && last && next.$$route === last.$$route
|
2013-10-22 21:41:21 +00:00
|
|
|
|
&& angular.equals(next.pathParams, last.pathParams)
|
|
|
|
|
|
&& !next.reloadOnSearch && !forceReload) {
|
2012-02-19 20:31:11 +00:00
|
|
|
|
last.params = next.params;
|
2013-10-10 08:11:21 +00:00
|
|
|
|
angular.copy(last.params, $routeParams);
|
2012-02-19 20:31:11 +00:00
|
|
|
|
$rootScope.$broadcast('$routeUpdate', last);
|
|
|
|
|
|
} else if (next || last) {
|
2011-11-02 23:32:46 +00:00
|
|
|
|
forceReload = false;
|
2012-06-01 18:31:10 +00:00
|
|
|
|
$rootScope.$broadcast('$routeChangeStart', next, last);
|
2011-11-02 23:32:46 +00:00
|
|
|
|
$route.current = next;
|
|
|
|
|
|
if (next) {
|
|
|
|
|
|
if (next.redirectTo) {
|
2013-10-10 08:11:21 +00:00
|
|
|
|
if (angular.isString(next.redirectTo)) {
|
2011-11-02 23:32:46 +00:00
|
|
|
|
$location.path(interpolate(next.redirectTo, next.params)).search(next.params)
|
|
|
|
|
|
.replace();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$location.url(next.redirectTo(next.pathParams, $location.path(), $location.search()))
|
|
|
|
|
|
.replace();
|
|
|
|
|
|
}
|
2011-07-12 00:18:17 +00:00
|
|
|
|
}
|
2011-08-24 05:30:14 +00:00
|
|
|
|
}
|
2012-05-23 04:12:19 +00:00
|
|
|
|
|
|
|
|
|
|
$q.when(next).
|
|
|
|
|
|
then(function() {
|
|
|
|
|
|
if (next) {
|
2013-10-10 08:11:21 +00:00
|
|
|
|
var locals = angular.extend({}, next.resolve),
|
feat($sce): new $sce service for Strict Contextual Escaping.
$sce is a service that provides Strict Contextual Escaping services to AngularJS.
Strict Contextual Escaping
--------------------------
Strict Contextual Escaping (SCE) is a mode in which AngularJS requires
bindings in certain contexts to result in a value that is marked as safe
to use for that context One example of such a context is binding
arbitrary html controlled by the user via ng-bind-html-unsafe. We
refer to these contexts as privileged or SCE contexts.
As of version 1.2, Angular ships with SCE enabled by default.
Note: When enabled (the default), IE8 in quirks mode is not supported.
In this mode, IE8 allows one to execute arbitrary javascript by the use
of the expression() syntax. Refer
http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx
to learn more about them. You can ensure your document is in standards
mode and not quirks mode by adding <!doctype html> to the top of your
HTML document.
SCE assists in writing code in way that (a) is secure by default and (b)
makes auditing for security vulnerabilities such as XSS, clickjacking,
etc. a lot easier.
Here's an example of a binding in a privileged context:
<input ng-model="userHtml">
<div ng-bind-html-unsafe="{{userHtml}}">
Notice that ng-bind-html-unsafe is bound to {{userHtml}} controlled by
the user. With SCE disabled, this application allows the user to render
arbitrary HTML into the DIV. In a more realistic example, one may be
rendering user comments, blog articles, etc. via bindings. (HTML is
just one example of a context where rendering user controlled input
creates security vulnerabilities.)
For the case of HTML, you might use a library, either on the client side, or on the server side,
to sanitize unsafe HTML before binding to the value and rendering it in the document.
How would you ensure that every place that used these types of bindings was bound to a value that
was sanitized by your library (or returned as safe for rendering by your server?) How can you
ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
properties/fields and forgot to update the binding to the sanitized value?
To be secure by default, you want to ensure that any such bindings are disallowed unless you can
determine that something explicitly says it's safe to use a value for binding in that
context. You can then audit your code (a simple grep would do) to ensure that this is only done
for those values that you can easily tell are safe - because they were received from your server,
sanitized by your library, etc. You can organize your codebase to help with this - perhaps
allowing only the files in a specific directory to do this. Ensuring that the internal API
exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
In the case of AngularJS' SCE service, one uses $sce.trustAs (and
shorthand methods such as $sce.trustAsHtml, etc.) to obtain values that
will be accepted by SCE / privileged contexts.
In privileged contexts, directives and code will bind to the result of
$sce.getTrusted(context, value) rather than to the value directly.
Directives use $sce.parseAs rather than $parse to watch attribute
bindings, which performs the $sce.getTrusted behind the scenes on
non-constant literals.
As an example, ngBindHtmlUnsafe uses $sce.parseAsHtml(binding
expression). Here's the actual code (slightly simplified):
var ngBindHtmlUnsafeDirective = ['$sce', function($sce) {
return function(scope, element, attr) {
scope.$watch($sce.parseAsHtml(attr.ngBindHtmlUnsafe), function(value) {
element.html(value || '');
});
};
}];
Impact on loading templates
---------------------------
This applies both to the ng-include directive as well as templateUrl's
specified by directives.
By default, Angular only loads templates from the same domain and
protocol as the application document. This is done by calling
$sce.getTrustedResourceUrl on the template URL. To load templates from
other domains and/or protocols, you may either either whitelist them or
wrap it into a trusted value.
*Please note*:
The browser's Same Origin Policy and Cross-Origin Resource Sharing
(CORS) policy apply in addition to this and may further restrict whether
the template is successfully loaded. This means that without the right
CORS policy, loading templates from a different domain won't work on all
browsers. Also, loading templates from file:// URL does not work on
some browsers.
This feels like too much overhead for the developer?
----------------------------------------------------
It's important to remember that SCE only applies to interpolation expressions.
If your expressions are constant literals, they're automatically trusted
and you don't need to call $sce.trustAs on them.
e.g. <div ng-html-bind-unsafe="'<b>implicitly trusted</b>'"></div> just works.
Additionally, a[href] and img[src] automatically sanitize their URLs and
do not pass them through $sce.getTrusted. SCE doesn't play a role here.
The included $sceDelegate comes with sane defaults to allow you to load
templates in ng-include from your application's domain without having to
even know about SCE. It blocks loading templates from other domains or
loading templates over http from an https served document. You can
change these by setting your own custom whitelists and blacklists for
matching such URLs.
This significantly reduces the overhead. It is far easier to pay the
small overhead and have an application that's secure and can be audited
to verify that with much more ease than bolting security onto an
application later.
2013-05-14 21:51:39 +00:00
|
|
|
|
template, templateUrl;
|
2012-05-23 04:12:19 +00:00
|
|
|
|
|
2013-10-10 08:11:21 +00:00
|
|
|
|
angular.forEach(locals, function(value, key) {
|
2013-10-22 21:41:21 +00:00
|
|
|
|
locals[key] = angular.isString(value) ?
|
|
|
|
|
|
$injector.get(value) : $injector.invoke(value);
|
2012-05-23 04:12:19 +00:00
|
|
|
|
});
|
2013-02-24 08:11:25 +00:00
|
|
|
|
|
2013-10-10 08:11:21 +00:00
|
|
|
|
if (angular.isDefined(template = next.template)) {
|
|
|
|
|
|
if (angular.isFunction(template)) {
|
2012-11-03 20:11:07 +00:00
|
|
|
|
template = template(next.params);
|
|
|
|
|
|
}
|
2013-10-10 08:11:21 +00:00
|
|
|
|
} else if (angular.isDefined(templateUrl = next.templateUrl)) {
|
|
|
|
|
|
if (angular.isFunction(templateUrl)) {
|
feat($sce): new $sce service for Strict Contextual Escaping.
$sce is a service that provides Strict Contextual Escaping services to AngularJS.
Strict Contextual Escaping
--------------------------
Strict Contextual Escaping (SCE) is a mode in which AngularJS requires
bindings in certain contexts to result in a value that is marked as safe
to use for that context One example of such a context is binding
arbitrary html controlled by the user via ng-bind-html-unsafe. We
refer to these contexts as privileged or SCE contexts.
As of version 1.2, Angular ships with SCE enabled by default.
Note: When enabled (the default), IE8 in quirks mode is not supported.
In this mode, IE8 allows one to execute arbitrary javascript by the use
of the expression() syntax. Refer
http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx
to learn more about them. You can ensure your document is in standards
mode and not quirks mode by adding <!doctype html> to the top of your
HTML document.
SCE assists in writing code in way that (a) is secure by default and (b)
makes auditing for security vulnerabilities such as XSS, clickjacking,
etc. a lot easier.
Here's an example of a binding in a privileged context:
<input ng-model="userHtml">
<div ng-bind-html-unsafe="{{userHtml}}">
Notice that ng-bind-html-unsafe is bound to {{userHtml}} controlled by
the user. With SCE disabled, this application allows the user to render
arbitrary HTML into the DIV. In a more realistic example, one may be
rendering user comments, blog articles, etc. via bindings. (HTML is
just one example of a context where rendering user controlled input
creates security vulnerabilities.)
For the case of HTML, you might use a library, either on the client side, or on the server side,
to sanitize unsafe HTML before binding to the value and rendering it in the document.
How would you ensure that every place that used these types of bindings was bound to a value that
was sanitized by your library (or returned as safe for rendering by your server?) How can you
ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
properties/fields and forgot to update the binding to the sanitized value?
To be secure by default, you want to ensure that any such bindings are disallowed unless you can
determine that something explicitly says it's safe to use a value for binding in that
context. You can then audit your code (a simple grep would do) to ensure that this is only done
for those values that you can easily tell are safe - because they were received from your server,
sanitized by your library, etc. You can organize your codebase to help with this - perhaps
allowing only the files in a specific directory to do this. Ensuring that the internal API
exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
In the case of AngularJS' SCE service, one uses $sce.trustAs (and
shorthand methods such as $sce.trustAsHtml, etc.) to obtain values that
will be accepted by SCE / privileged contexts.
In privileged contexts, directives and code will bind to the result of
$sce.getTrusted(context, value) rather than to the value directly.
Directives use $sce.parseAs rather than $parse to watch attribute
bindings, which performs the $sce.getTrusted behind the scenes on
non-constant literals.
As an example, ngBindHtmlUnsafe uses $sce.parseAsHtml(binding
expression). Here's the actual code (slightly simplified):
var ngBindHtmlUnsafeDirective = ['$sce', function($sce) {
return function(scope, element, attr) {
scope.$watch($sce.parseAsHtml(attr.ngBindHtmlUnsafe), function(value) {
element.html(value || '');
});
};
}];
Impact on loading templates
---------------------------
This applies both to the ng-include directive as well as templateUrl's
specified by directives.
By default, Angular only loads templates from the same domain and
protocol as the application document. This is done by calling
$sce.getTrustedResourceUrl on the template URL. To load templates from
other domains and/or protocols, you may either either whitelist them or
wrap it into a trusted value.
*Please note*:
The browser's Same Origin Policy and Cross-Origin Resource Sharing
(CORS) policy apply in addition to this and may further restrict whether
the template is successfully loaded. This means that without the right
CORS policy, loading templates from a different domain won't work on all
browsers. Also, loading templates from file:// URL does not work on
some browsers.
This feels like too much overhead for the developer?
----------------------------------------------------
It's important to remember that SCE only applies to interpolation expressions.
If your expressions are constant literals, they're automatically trusted
and you don't need to call $sce.trustAs on them.
e.g. <div ng-html-bind-unsafe="'<b>implicitly trusted</b>'"></div> just works.
Additionally, a[href] and img[src] automatically sanitize their URLs and
do not pass them through $sce.getTrusted. SCE doesn't play a role here.
The included $sceDelegate comes with sane defaults to allow you to load
templates in ng-include from your application's domain without having to
even know about SCE. It blocks loading templates from other domains or
loading templates over http from an https served document. You can
change these by setting your own custom whitelists and blacklists for
matching such URLs.
This significantly reduces the overhead. It is far easier to pay the
small overhead and have an application that's secure and can be audited
to verify that with much more ease than bolting security onto an
application later.
2013-05-14 21:51:39 +00:00
|
|
|
|
templateUrl = templateUrl(next.params);
|
2012-11-03 20:11:07 +00:00
|
|
|
|
}
|
feat($sce): new $sce service for Strict Contextual Escaping.
$sce is a service that provides Strict Contextual Escaping services to AngularJS.
Strict Contextual Escaping
--------------------------
Strict Contextual Escaping (SCE) is a mode in which AngularJS requires
bindings in certain contexts to result in a value that is marked as safe
to use for that context One example of such a context is binding
arbitrary html controlled by the user via ng-bind-html-unsafe. We
refer to these contexts as privileged or SCE contexts.
As of version 1.2, Angular ships with SCE enabled by default.
Note: When enabled (the default), IE8 in quirks mode is not supported.
In this mode, IE8 allows one to execute arbitrary javascript by the use
of the expression() syntax. Refer
http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx
to learn more about them. You can ensure your document is in standards
mode and not quirks mode by adding <!doctype html> to the top of your
HTML document.
SCE assists in writing code in way that (a) is secure by default and (b)
makes auditing for security vulnerabilities such as XSS, clickjacking,
etc. a lot easier.
Here's an example of a binding in a privileged context:
<input ng-model="userHtml">
<div ng-bind-html-unsafe="{{userHtml}}">
Notice that ng-bind-html-unsafe is bound to {{userHtml}} controlled by
the user. With SCE disabled, this application allows the user to render
arbitrary HTML into the DIV. In a more realistic example, one may be
rendering user comments, blog articles, etc. via bindings. (HTML is
just one example of a context where rendering user controlled input
creates security vulnerabilities.)
For the case of HTML, you might use a library, either on the client side, or on the server side,
to sanitize unsafe HTML before binding to the value and rendering it in the document.
How would you ensure that every place that used these types of bindings was bound to a value that
was sanitized by your library (or returned as safe for rendering by your server?) How can you
ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
properties/fields and forgot to update the binding to the sanitized value?
To be secure by default, you want to ensure that any such bindings are disallowed unless you can
determine that something explicitly says it's safe to use a value for binding in that
context. You can then audit your code (a simple grep would do) to ensure that this is only done
for those values that you can easily tell are safe - because they were received from your server,
sanitized by your library, etc. You can organize your codebase to help with this - perhaps
allowing only the files in a specific directory to do this. Ensuring that the internal API
exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
In the case of AngularJS' SCE service, one uses $sce.trustAs (and
shorthand methods such as $sce.trustAsHtml, etc.) to obtain values that
will be accepted by SCE / privileged contexts.
In privileged contexts, directives and code will bind to the result of
$sce.getTrusted(context, value) rather than to the value directly.
Directives use $sce.parseAs rather than $parse to watch attribute
bindings, which performs the $sce.getTrusted behind the scenes on
non-constant literals.
As an example, ngBindHtmlUnsafe uses $sce.parseAsHtml(binding
expression). Here's the actual code (slightly simplified):
var ngBindHtmlUnsafeDirective = ['$sce', function($sce) {
return function(scope, element, attr) {
scope.$watch($sce.parseAsHtml(attr.ngBindHtmlUnsafe), function(value) {
element.html(value || '');
});
};
}];
Impact on loading templates
---------------------------
This applies both to the ng-include directive as well as templateUrl's
specified by directives.
By default, Angular only loads templates from the same domain and
protocol as the application document. This is done by calling
$sce.getTrustedResourceUrl on the template URL. To load templates from
other domains and/or protocols, you may either either whitelist them or
wrap it into a trusted value.
*Please note*:
The browser's Same Origin Policy and Cross-Origin Resource Sharing
(CORS) policy apply in addition to this and may further restrict whether
the template is successfully loaded. This means that without the right
CORS policy, loading templates from a different domain won't work on all
browsers. Also, loading templates from file:// URL does not work on
some browsers.
This feels like too much overhead for the developer?
----------------------------------------------------
It's important to remember that SCE only applies to interpolation expressions.
If your expressions are constant literals, they're automatically trusted
and you don't need to call $sce.trustAs on them.
e.g. <div ng-html-bind-unsafe="'<b>implicitly trusted</b>'"></div> just works.
Additionally, a[href] and img[src] automatically sanitize their URLs and
do not pass them through $sce.getTrusted. SCE doesn't play a role here.
The included $sceDelegate comes with sane defaults to allow you to load
templates in ng-include from your application's domain without having to
even know about SCE. It blocks loading templates from other domains or
loading templates over http from an https served document. You can
change these by setting your own custom whitelists and blacklists for
matching such URLs.
This significantly reduces the overhead. It is far easier to pay the
small overhead and have an application that's secure and can be audited
to verify that with much more ease than bolting security onto an
application later.
2013-05-14 21:51:39 +00:00
|
|
|
|
templateUrl = $sce.getTrustedResourceUrl(templateUrl);
|
2013-10-10 08:11:21 +00:00
|
|
|
|
if (angular.isDefined(templateUrl)) {
|
feat($sce): new $sce service for Strict Contextual Escaping.
$sce is a service that provides Strict Contextual Escaping services to AngularJS.
Strict Contextual Escaping
--------------------------
Strict Contextual Escaping (SCE) is a mode in which AngularJS requires
bindings in certain contexts to result in a value that is marked as safe
to use for that context One example of such a context is binding
arbitrary html controlled by the user via ng-bind-html-unsafe. We
refer to these contexts as privileged or SCE contexts.
As of version 1.2, Angular ships with SCE enabled by default.
Note: When enabled (the default), IE8 in quirks mode is not supported.
In this mode, IE8 allows one to execute arbitrary javascript by the use
of the expression() syntax. Refer
http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx
to learn more about them. You can ensure your document is in standards
mode and not quirks mode by adding <!doctype html> to the top of your
HTML document.
SCE assists in writing code in way that (a) is secure by default and (b)
makes auditing for security vulnerabilities such as XSS, clickjacking,
etc. a lot easier.
Here's an example of a binding in a privileged context:
<input ng-model="userHtml">
<div ng-bind-html-unsafe="{{userHtml}}">
Notice that ng-bind-html-unsafe is bound to {{userHtml}} controlled by
the user. With SCE disabled, this application allows the user to render
arbitrary HTML into the DIV. In a more realistic example, one may be
rendering user comments, blog articles, etc. via bindings. (HTML is
just one example of a context where rendering user controlled input
creates security vulnerabilities.)
For the case of HTML, you might use a library, either on the client side, or on the server side,
to sanitize unsafe HTML before binding to the value and rendering it in the document.
How would you ensure that every place that used these types of bindings was bound to a value that
was sanitized by your library (or returned as safe for rendering by your server?) How can you
ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
properties/fields and forgot to update the binding to the sanitized value?
To be secure by default, you want to ensure that any such bindings are disallowed unless you can
determine that something explicitly says it's safe to use a value for binding in that
context. You can then audit your code (a simple grep would do) to ensure that this is only done
for those values that you can easily tell are safe - because they were received from your server,
sanitized by your library, etc. You can organize your codebase to help with this - perhaps
allowing only the files in a specific directory to do this. Ensuring that the internal API
exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
In the case of AngularJS' SCE service, one uses $sce.trustAs (and
shorthand methods such as $sce.trustAsHtml, etc.) to obtain values that
will be accepted by SCE / privileged contexts.
In privileged contexts, directives and code will bind to the result of
$sce.getTrusted(context, value) rather than to the value directly.
Directives use $sce.parseAs rather than $parse to watch attribute
bindings, which performs the $sce.getTrusted behind the scenes on
non-constant literals.
As an example, ngBindHtmlUnsafe uses $sce.parseAsHtml(binding
expression). Here's the actual code (slightly simplified):
var ngBindHtmlUnsafeDirective = ['$sce', function($sce) {
return function(scope, element, attr) {
scope.$watch($sce.parseAsHtml(attr.ngBindHtmlUnsafe), function(value) {
element.html(value || '');
});
};
}];
Impact on loading templates
---------------------------
This applies both to the ng-include directive as well as templateUrl's
specified by directives.
By default, Angular only loads templates from the same domain and
protocol as the application document. This is done by calling
$sce.getTrustedResourceUrl on the template URL. To load templates from
other domains and/or protocols, you may either either whitelist them or
wrap it into a trusted value.
*Please note*:
The browser's Same Origin Policy and Cross-Origin Resource Sharing
(CORS) policy apply in addition to this and may further restrict whether
the template is successfully loaded. This means that without the right
CORS policy, loading templates from a different domain won't work on all
browsers. Also, loading templates from file:// URL does not work on
some browsers.
This feels like too much overhead for the developer?
----------------------------------------------------
It's important to remember that SCE only applies to interpolation expressions.
If your expressions are constant literals, they're automatically trusted
and you don't need to call $sce.trustAs on them.
e.g. <div ng-html-bind-unsafe="'<b>implicitly trusted</b>'"></div> just works.
Additionally, a[href] and img[src] automatically sanitize their URLs and
do not pass them through $sce.getTrusted. SCE doesn't play a role here.
The included $sceDelegate comes with sane defaults to allow you to load
templates in ng-include from your application's domain without having to
even know about SCE. It blocks loading templates from other domains or
loading templates over http from an https served document. You can
change these by setting your own custom whitelists and blacklists for
matching such URLs.
This significantly reduces the overhead. It is far easier to pay the
small overhead and have an application that's secure and can be audited
to verify that with much more ease than bolting security onto an
application later.
2013-05-14 21:51:39 +00:00
|
|
|
|
next.loadedTemplateUrl = templateUrl;
|
|
|
|
|
|
template = $http.get(templateUrl, {cache: $templateCache}).
|
2012-11-03 20:11:07 +00:00
|
|
|
|
then(function(response) { return response.data; });
|
|
|
|
|
|
}
|
2012-05-17 23:36:18 +00:00
|
|
|
|
}
|
2013-10-10 08:11:21 +00:00
|
|
|
|
if (angular.isDefined(template)) {
|
2013-02-24 08:11:25 +00:00
|
|
|
|
locals['$template'] = template;
|
2012-05-23 04:12:19 +00:00
|
|
|
|
}
|
2013-02-24 08:11:25 +00:00
|
|
|
|
return $q.all(locals);
|
2012-05-23 04:12:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
}).
|
|
|
|
|
|
// after route change
|
|
|
|
|
|
then(function(locals) {
|
|
|
|
|
|
if (next == $route.current) {
|
|
|
|
|
|
if (next) {
|
|
|
|
|
|
next.locals = locals;
|
2013-10-10 08:11:21 +00:00
|
|
|
|
angular.copy(next.params, $routeParams);
|
2012-05-23 04:12:19 +00:00
|
|
|
|
}
|
2012-06-01 18:31:10 +00:00
|
|
|
|
$rootScope.$broadcast('$routeChangeSuccess', next, last);
|
2012-05-23 04:12:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
}, function(error) {
|
|
|
|
|
|
if (next == $route.current) {
|
|
|
|
|
|
$rootScope.$broadcast('$routeChangeError', next, last, error);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2011-08-15 15:34:11 +00:00
|
|
|
|
}
|
2011-03-23 16:33:29 +00:00
|
|
|
|
}
|
2011-08-24 05:30:14 +00:00
|
|
|
|
|
2011-08-15 15:34:11 +00:00
|
|
|
|
|
2011-11-02 23:32:46 +00:00
|
|
|
|
/**
|
|
|
|
|
|
* @returns the current active route, by matching it against the URL
|
|
|
|
|
|
*/
|
|
|
|
|
|
function parseRoute() {
|
|
|
|
|
|
// Match a route
|
|
|
|
|
|
var params, match;
|
2013-10-10 08:11:21 +00:00
|
|
|
|
angular.forEach(routes, function(route, path) {
|
2012-12-27 05:45:11 +00:00
|
|
|
|
if (!match && (params = switchRouteMatcher($location.path(), route))) {
|
2011-11-02 23:32:46 +00:00
|
|
|
|
match = inherit(route, {
|
2013-10-10 08:11:21 +00:00
|
|
|
|
params: angular.extend({}, $location.search(), params),
|
2011-11-02 23:32:46 +00:00
|
|
|
|
pathParams: params});
|
2013-03-08 19:43:37 +00:00
|
|
|
|
match.$$route = route;
|
2011-11-02 23:32:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
// No route matched; fallback to "otherwise" route
|
|
|
|
|
|
return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
|
|
|
|
|
|
}
|
2011-02-15 06:12:45 +00:00
|
|
|
|
|
2011-11-02 23:32:46 +00:00
|
|
|
|
/**
|
2013-03-21 19:09:47 +00:00
|
|
|
|
* @returns interpolation of the redirect path with the parameters
|
2011-11-02 23:32:46 +00:00
|
|
|
|
*/
|
|
|
|
|
|
function interpolate(string, params) {
|
|
|
|
|
|
var result = [];
|
2013-10-10 08:11:21 +00:00
|
|
|
|
angular.forEach((string||'').split(':'), function(segment, i) {
|
2013-08-13 16:49:29 +00:00
|
|
|
|
if (i === 0) {
|
2011-11-02 23:32:46 +00:00
|
|
|
|
result.push(segment);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
var segmentMatch = segment.match(/(\w+)(.*)/);
|
|
|
|
|
|
var key = segmentMatch[1];
|
|
|
|
|
|
result.push(params[key]);
|
|
|
|
|
|
result.push(segmentMatch[2] || '');
|
|
|
|
|
|
delete params[key];
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
return result.join('');
|
|
|
|
|
|
}
|
|
|
|
|
|
}];
|
2013-10-17 02:48:32 +00:00
|
|
|
|
}
|