diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js
index 27f8ab992aec..dc25e9d4d41a 100644
--- a/src/ng/rootScope.js
+++ b/src/ng/rootScope.js
@@ -1151,8 +1151,8 @@ function $RootScopeProvider() {
        *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
        *     event propagates through the scope hierarchy, this property is set to null.
        *   - `name` - `{string}`: name of the event.
-       *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
-       *     further event propagation (available only for events that were `$emit`-ed).
+       *   - `stopPropagation` - `{function}`: calling `stopPropagation` function will cancel
+       *     further event propagation.
        *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
        *     to true.
        *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
@@ -1272,7 +1272,9 @@ function $RootScopeProvider() {
        * The event life cycle starts at the scope on which `$broadcast` was called. All
        * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
        * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
-       * scope and calls all registered listeners along the way. The event cannot be canceled.
+       * scope and calls all registered listeners along the way. If a scope requests the event stop propagation
+       * then it will not be propagated to any children of that scope, but will continue to propagate to siblings of the
+       * that scope and children of those siblings unless each sibling independently stops the event.
        *
        * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
        * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
@@ -1284,10 +1286,14 @@ function $RootScopeProvider() {
       $broadcast: function(name, args) {
         var target = this,
             current = target,
+            stopPropagation = false,
             next = target,
             event = {
               name: name,
               targetScope: target,
+              stopPropagation: function() {
+                stopPropagation = true;
+              },
               preventDefault: function() {
                 event.defaultPrevented = true;
               },
@@ -1301,6 +1307,7 @@ function $RootScopeProvider() {
 
         //down while you can, then up and next sibling or up and next sibling until back at root
         while ((current = next)) {
+          stopPropagation = false;
           event.currentScope = current;
           listeners = current.$$listeners[name] || [];
           for (i = 0, length = listeners.length; i < length; i++) {
@@ -1322,8 +1329,8 @@ function $RootScopeProvider() {
           // Insanity Warning: scope depth-first traversal
           // yes, this code is a bit crazy, but it works and we have tests to prove it!
           // this piece should be kept in sync with the traversal in $digest
-          // (though it differs due to having the extra check for $$listenerCount)
-          if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
+          // (though it differs due to having the extra check for $$listenerCount and stopPropagation)
+          if (!(next = ((current.$$listenerCount[name] && !stopPropagation && current.$$childHead) ||
               (current !== target && current.$$nextSibling)))) {
             while (current !== target && !(next = current.$$nextSibling)) {
               current = current.$parent;
diff --git a/test/ng/rootScopeSpec.js b/test/ng/rootScopeSpec.js
index 364196a94b2b..46091a2cb332 100644
--- a/test/ng/rootScopeSpec.js
+++ b/test/ng/rootScopeSpec.js
@@ -2326,6 +2326,38 @@ describe('Scope', function() {
           expect(result.name).toBe('some');
           expect(result.targetScope).toBe(child1);
         });
+
+
+        it('should not descend past scopes that stop propagation', inject(function($rootScope) {
+          child1.$on('myEvent', function(event) {
+            event.stopPropagation();
+          });
+
+          $rootScope.$broadcast('myEvent');
+          expect(log).toBe('0>1>2>21>211>22>23>3>');
+        }));
+
+        it('should continue to pass through and past sibling scopes to one that stopped propagation',
+            inject(function($rootScope) {
+          grandChild21.$on('myEvent', function(event) {
+            event.stopPropagation();
+          });
+
+          $rootScope.$broadcast('myEvent');
+          expect(log).toBe('0>1>11>2>21>22>23>3>');
+        }));
+
+        it('should allow multiple separate subtrees to stop propagation independently', inject(function($rootScope) {
+          var stopPropagation = function(event) {
+            event.stopPropagation();
+          };
+          child1.$on('myEvent', stopPropagation);
+          grandChild21.$on('myEvent', stopPropagation);
+          grandChild22.$on('myEvent', stopPropagation);
+
+          $rootScope.$broadcast('myEvent');
+          expect(log).toBe('0>1>2>21>22>23>3>');
+        }));
       });