diff --git a/NON-STANDARD-APIS.md b/NON-STANDARD-APIS.md
index 8cca25bdd..eb0d65a5c 100644
--- a/NON-STANDARD-APIS.md
+++ b/NON-STANDARD-APIS.md
@@ -1,14 +1,14 @@
 # Zone.js's support for non standard apis
 
 Zone.js patched most standard APIs such as DOM event listeners, XMLHttpRequest in Browser, EventEmitter and fs API in Node.js so they can be in zone.
-  
-But there are still a lot of non standard APIs that are not patched by default, such as MediaQuery, Notification, 
- WebAudio and so on. We are adding support to those APIs, and our progress is updated here.
- 
-## Currently supported non standard Web APIs 
+
+But there are still a lot of non standard APIs that are not patched by default, such as MediaQuery, Notification,
+WebAudio and so on. We are adding support to those APIs, and our progress is updated here.
+
+## Currently supported non standard Web APIs
 
 * MediaQuery
-* Notification 
+* Notification
 
 ## Currently supported polyfills
 
@@ -28,7 +28,7 @@ Usage:
 
 * bluebird promise
 
-Browser Usage: 
+Browser Usage:
 
 ```
   <script src="zone.js"></script>
@@ -75,11 +75,11 @@ remove the comment of the following line
 
 ## Others
 
-* Cordova 
+* Cordova
 
 patch `cordova.exec` API
 
-`cordova.exec(success, error, service, action, args);` 
+`cordova.exec(success, error, service, action, args);`
 
 `success` and `error` will be patched with `Zone.wrap`.
 
@@ -96,12 +96,12 @@ to load the patch, you should load in the following order.
 By default, those APIs' support will not be loaded in zone.js or zone-node.js,
 so if you want to load those API's support, you should load those files by yourself.
 
-For example, if you want to add MediaQuery patch, you should do like this: 
+For example, if you want to add MediaQuery patch, you should do like this:
 
 ```
-  <script src="path/zone.js"></script> 
-  <script src="path/webapis-media-query.js"></script> 
-```  
+  <script src="path/zone.js"></script>
+  <script src="path/webapis-media-query.js"></script>
+```
 
 * rxjs
 
@@ -127,28 +127,28 @@ constructorZone.run(() => {
 
 subscriptionZone.run(() => {
   observable.subscribe(() => {
-    console.log('current zone when subscription next', Zone.current.name); // will output subscription. 
+    console.log('current zone when subscription next', Zone.current.name); // will output subscription.
   }, () => {
-    console.log('current zone when subscription error', Zone.current.name); // will output subscription. 
+    console.log('current zone when subscription error', Zone.current.name); // will output subscription.
   }, () => {
-    console.log('current zone when subscription complete', Zone.current.name); // will output subscription. 
+    console.log('current zone when subscription complete', Zone.current.name); // will output subscription.
   });
 });
 
 operatorZone.run(() => {
   observable.map(() => {
-    console.log('current zone when map operator', Zone.current.name); // will output operator. 
+    console.log('current zone when map operator', Zone.current.name); // will output operator.
   });
 });
 ```
 
 Currently basically everything the `rxjs` API includes
 
-- Observable
-- Subscription
-- Subscriber
-- Operators 
-- Scheduler 
+* Observable
+* Subscription
+* Subscriber
+* Operators
+* Scheduler
 
 is patched, so each asynchronous call will run in the correct zone.
 
@@ -194,7 +194,7 @@ user need to patch `io` themselves just like following code.
     </script>
 ```
 
-please reference the sample repo [zone-socketio](https://github.com/JiaLiPassion/zone-socketio) about 
+please reference the sample repo [zone-socketio](https://github.com/JiaLiPassion/zone-socketio) about
 detail usage.
 
 * jsonp
@@ -208,14 +208,15 @@ there is a sampel repo [zone-jsonp](https://github.com/JiaLiPassion/test-zone-js
 sample usage is:
 
 ```javascript
-import 'zone.js/dist/zone-patch-jsonp';
-Zone['__zone_symbol__jsonp']({
+import "zone.js/dist/zone-patch-jsonp";
+Zone["__zone_symbol__jsonp"]({
   jsonp: getJSONP,
-  sendFuncName: 'send',
-  successFuncName: 'jsonpSuccessCallback', 
-  failedFuncName: 'jsonpFailedCallback'
+  sendFuncName: "send",
+  successFuncName: "jsonpSuccessCallback",
+  failedFuncName: "jsonpFailedCallback"
 });
 ```
+
 * ResizeObserver
 
 Currently only `Chrome 64` native support this feature.
@@ -227,3 +228,28 @@ import 'zone.js/dist/zone-patch-resize-observer';
 ```
 
 there is a sample repo [zone-resize-observer](https://github.com/JiaLiPassion/zone-resize-observer) here
+
+* customize root zone
+
+In some application, especially for performance test or async tracing, user may want a predefined root zone.
+For example,i when you include `google map` into `index.html` by adding `script tag` such as
+`<script src="https://maps.googleapis.com/maps/api/js?key=KEY"></script>`, `google map js` will add some event
+listeners or do some async operations during loading the js file, all those operations happens in `root zone`,
+if we want to intercept those async operations, we need to modify root zone.
+
+You can do like this:
+
+```javascript
+<script src="folder/zone.js" />
+<script>
+  var customizeRootZone = Zone.current.fork({
+    name: 'myRoot'
+  });
+  window.__zone_symbol__customizeRootZone = customizeRootZone;
+</script>
+<script src="folder/zone-patch-customize-root-zone.js" />
+```
+
+** Remind, this is not a public api or formal feature of zone.js, it will not be included in future tc39 proposal,
+and it is not by design, it is just a helper method to let you customize rootZone for some test and suppose to change
+in the future, please use it on your own risk**
diff --git a/gulpfile.js b/gulpfile.js
index b3f63b29a..acb35e729 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -244,6 +244,14 @@ gulp.task('build/bluebird.min.js', ['compile-esm'], function(cb) {
   return generateScript('./lib/extra/bluebird.ts', 'zone-bluebird.min.js', true, cb);
 });
 
+gulp.task('build/zone-patch-customize-root.js', ['compile-esm'], function(cb) {
+  return generateScript('./lib/extra/customize-root.ts', 'zone-patch-customize-root.js', false, cb);
+});
+
+gulp.task('build/zone-patch-customize-root.min.js', ['compile-esm'], function(cb) {
+  return generateScript('./lib/extra/customize-root.ts', 'zone-patch-customize-root.min.js', true, cb);
+});
+
 gulp.task('build/zone-patch-jsonp.js', ['compile-esm'], function(cb) {
     return generateScript('./lib/extra/jsonp.ts', 'zone-patch-jsonp.js', false, cb);
 });
@@ -367,6 +375,8 @@ gulp.task('build', [
   'build/zone-mix.js',
   'build/bluebird.js',
   'build/bluebird.min.js',
+  'build/zone-patch-customize-root.js',
+  'build/zone-patch-customize-root.min.js',
   'build/zone-patch-jsonp.js',
   'build/zone-patch-jsonp.min.js',
   'build/jasmine-patch.js',
diff --git a/lib/extra/customize-root.ts b/lib/extra/customize-root.ts
new file mode 100644
index 000000000..bc69b7bab
--- /dev/null
+++ b/lib/extra/customize-root.ts
@@ -0,0 +1,39 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+Zone.__load_patch(
+  "customize-root",
+  (global: any, Zone: ZoneType, api: _ZonePrivate) => {
+    // add a helper utility to let user to customize root zone
+    // this is not by design, and will not be included in future
+    // tc39 zone proposal, it just for test and may change in the future
+    const customizeRootZone = global[api.symbol("customizeRootZone")];
+    if (!customizeRootZone) {
+      return;
+    }
+    const currentDesc = Object.getOwnPropertyDescriptor(Zone, "current");
+    if (!currentDesc) {
+      return;
+    }
+    const currentDescGet = currentDesc.get;
+    if (!currentDescGet) {
+      return;
+    }
+    const rootZone = Zone.root;
+    Object.defineProperty(Zone, "current", {
+      configurable: true,
+      enumerable: true,
+      get: function() {
+        const currentZone = currentDescGet.apply(this, arguments);
+        if (currentZone === rootZone) {
+          return customizeRootZone;
+        }
+        return currentZone;
+      }
+    });
+  }
+);