Bug 1314861: Lazily load most SDK module imports. r?rpl draft
authorKris Maglione <maglione.k@gmail.com>
Fri, 07 Apr 2017 18:11:32 -0700
changeset 558819 91669a46f8db7873c26449a6f510b5549b4564b2
parent 558818 e744f8b7716b7ff682e99fc981220b4592052b9c
child 623267 52c21085d7af899a908e04fd2126b79f4e23b0c4
push id52953
push usermaglione.k@gmail.com
push dateSat, 08 Apr 2017 01:32:07 +0000
reviewersrpl
bugs1314861
milestone55.0a1
Bug 1314861: Lazily load most SDK module imports. r?rpl MozReview-Commit-ID: 3mneEkzljiU
addon-sdk/source/lib/sdk/addon/bootstrap.js
addon-sdk/source/lib/sdk/addon/runner.js
addon-sdk/source/lib/sdk/console/traceback.js
addon-sdk/source/lib/sdk/content/events.js
addon-sdk/source/lib/sdk/content/page-mod.js
addon-sdk/source/lib/sdk/content/page-worker.js
addon-sdk/source/lib/sdk/content/sandbox.js
addon-sdk/source/lib/sdk/content/utils.js
addon-sdk/source/lib/sdk/content/worker-child.js
addon-sdk/source/lib/sdk/content/worker.js
addon-sdk/source/lib/sdk/context-menu.js
addon-sdk/source/lib/sdk/context-menu/context.js
addon-sdk/source/lib/sdk/core/disposable.js
addon-sdk/source/lib/sdk/core/observer.js
addon-sdk/source/lib/sdk/deprecated/unit-test.js
addon-sdk/source/lib/sdk/event/dom.js
addon-sdk/source/lib/sdk/event/utils.js
addon-sdk/source/lib/sdk/frame/hidden-frame.js
addon-sdk/source/lib/sdk/io/buffer.js
addon-sdk/source/lib/sdk/io/file.js
addon-sdk/source/lib/sdk/io/fs.js
addon-sdk/source/lib/sdk/io/stream.js
addon-sdk/source/lib/sdk/lang/functional/concurrent.js
addon-sdk/source/lib/sdk/net/url.js
addon-sdk/source/lib/sdk/page-mod.js
addon-sdk/source/lib/sdk/page-worker.js
addon-sdk/source/lib/sdk/panel.js
addon-sdk/source/lib/sdk/panel/events.js
addon-sdk/source/lib/sdk/panel/utils.js
addon-sdk/source/lib/sdk/platform/xpcom.js
addon-sdk/source/lib/sdk/preferences/native-options.js
addon-sdk/source/lib/sdk/private-browsing/utils.js
addon-sdk/source/lib/sdk/remote/parent.js
addon-sdk/source/lib/sdk/remote/utils.js
addon-sdk/source/lib/sdk/selection.js
addon-sdk/source/lib/sdk/self.js
addon-sdk/source/lib/sdk/simple-storage.js
addon-sdk/source/lib/sdk/stylesheet/style.js
addon-sdk/source/lib/sdk/system.js
addon-sdk/source/lib/sdk/system/globals.js
addon-sdk/source/lib/sdk/tabs/helpers.js
addon-sdk/source/lib/sdk/tabs/tab.js
addon-sdk/source/lib/sdk/tabs/tabs-firefox.js
addon-sdk/source/lib/sdk/tabs/utils.js
addon-sdk/source/lib/sdk/test/harness.js
addon-sdk/source/lib/sdk/test/utils.js
addon-sdk/source/lib/sdk/util/deprecate.js
addon-sdk/source/lib/sdk/util/match-pattern.js
addon-sdk/source/lib/sdk/util/object.js
addon-sdk/source/lib/sdk/util/rules.js
addon-sdk/source/lib/sdk/util/sequence.js
addon-sdk/source/lib/sdk/window/utils.js
addon-sdk/source/test/leak/leak-utils.js
--- a/addon-sdk/source/lib/sdk/addon/bootstrap.js
+++ b/addon-sdk/source/lib/sdk/addon/bootstrap.js
@@ -130,17 +130,18 @@ Bootstrap.prototype = {
       self.loader = loader;
 
       const module = Module("package.json", `${baseURI}package.json`);
       const require = Require(loader, module);
       const main = command === "test" ? "sdk/test/runner" : null;
       const prefsURI = `${baseURI}defaults/preferences/prefs.js`;
 
       // Init the 'sdk/webextension' module from the bootstrap addon parameter.
-      require("sdk/webextension").initFromBootstrapAddonParam(addon);
+      if (addon.webExtension)
+        require("sdk/webextension").initFromBootstrapAddonParam(addon);
 
       const { startup } = require("sdk/addon/runner");
       startup(reason, {loader, main, prefsURI});
     }.bind(this)).catch(error => {
       console.error(`Failed to start ${id} addon`, error);
       throw error;
     });
   },
--- a/addon-sdk/source/lib/sdk/addon/runner.js
+++ b/addon-sdk/source/lib/sdk/addon/runner.js
@@ -5,24 +5,20 @@
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Cc, Ci, Cu } = require('chrome');
 const { rootURI, metadata, isNative } = require('@loader/options');
 const { id, loadReason } = require('../self');
 const { descriptor, Sandbox, evaluate, main, resolveURI } = require('toolkit/loader');
-const { once } = require('../system/events');
 const { exit, env, staticArgs } = require('../system');
 const { when: unload } = require('../system/unload');
 const globals = require('../system/globals');
-const xulApp = require('../system/xul-app');
 const { get } = require('../preferences/service');
-const appShellService = Cc['@mozilla.org/appshell/appShellService;1'].
-                        getService(Ci.nsIAppShellService);
 const { preferences } = metadata;
 
 const Startup = Cu.import("resource://gre/modules/sdk/system/Startup.js", {}).exports;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyGetter(this, "BrowserToolboxProcess", function () {
   return Cu.import("resource://devtools/client/framework/ToolboxProcess.jsm", {}).
          BrowserToolboxProcess;
--- a/addon-sdk/source/lib/sdk/console/traceback.js
+++ b/addon-sdk/source/lib/sdk/console/traceback.js
@@ -4,17 +4,17 @@
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Ci, components } = require("chrome");
 const { parseStack, sourceURI } = require("toolkit/loader");
-const { readURISync } = require("../net/url");
+lazyRequire(this, "../net/url", "readURISync");
 
 function safeGetFileLine(path, line) {
   try {
     var scheme = require("../url").URL(path).scheme;
     // TODO: There should be an easier, more accurate way to figure out
     // what's the case here.
     if (!(scheme == "http" || scheme == "https"))
       return readURISync(path).split("\n")[line - 1];
--- a/addon-sdk/source/lib/sdk/content/events.js
+++ b/addon-sdk/source/lib/sdk/content/events.js
@@ -4,17 +4,17 @@
 
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Ci } = require("chrome");
-const { open } = require("../event/dom");
+lazyRequire(this, "../event/dom", "open");
 const { observe } = require("../event/chrome");
 const { filter, merge, map, expand } = require("../event/utils");
 const { windows } = require("../window/utils");
 const { events: windowEvents } = require("sdk/window/events");
 
 // Note: Please note that even though pagehide event is included
 // it's not observable reliably since it's not always triggered
 // when closing tabs. Implementation can be imrpoved once that
--- a/addon-sdk/source/lib/sdk/content/page-mod.js
+++ b/addon-sdk/source/lib/sdk/content/page-mod.js
@@ -2,36 +2,30 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "stable"
 };
 
-const { getAttachEventType } = require('../content/utils');
+lazyRequire(this, '../content/utils', 'getAttachEventType');
 const { Class } = require('../core/heritage');
 const { Disposable } = require('../core/disposable');
-const { WeakReference } = require('../core/reference');
-const { WorkerChild } = require('./worker-child');
+lazyRequire(this, './worker-child', 'WorkerChild');
 const { EventTarget } = require('../event/target');
 const { on, emit, once, setListeners } = require('../event/core');
-const { on: domOn, removeListener: domOff } = require('../dom/events');
-const { isRegExp, isUndefined } = require('../lang/type');
-const { merge } = require('../util/object');
-const { isBrowser, getFrames } = require('../window/utils');
-const { getTabs, getURI: getTabURI } = require('../tabs/utils');
-const { ignoreWindow } = require('../private-browsing/utils');
-const { Style } = require("../stylesheet/style");
-const { attach, detach } = require("../content/mod");
-const { has, hasAny } = require("../util/array");
-const { Rules } = require("../util/rules");
-const { List, addListItem, removeListItem } = require('../util/list');
-const { when } = require("../system/unload");
-const { uuid } = require('../util/uuid');
+lazyRequire(this, '../dom/events',{'on': 'domOn', 'removeListener': 'domOff'});
+lazyRequire(this, '../util/object', "merge");
+lazyRequire(this, '../window/utils', "getFrames");
+lazyRequire(this, '../private-browsing/utils', "ignoreWindow");
+lazyRequire(this, '../stylesheet/style', 'Style');
+lazyRequire(this, '../content/mod', 'attach', 'detach');
+lazyRequire(this, '../util/rules', 'Rules');
+lazyRequire(this, '../util/uuid', 'uuid');
 const { frames, process } = require('../remote/child');
 
 const pagemods = new Map();
 const styles = new WeakMap();
 var styleFor = (mod) => styles.get(mod);
 
 // Helper functions
 var modMatchesURI = (mod, uri) => mod.include.matchesAny(uri) && !mod.exclude.matchesAny(uri);
@@ -74,17 +68,17 @@ const ChildPageMod = Class({
       }));
     }
 
     pagemods.set(this.id, this);
     this.seenDocuments = new WeakMap();
 
     // `applyOnExistingDocuments` has to be called after `pagemods.add()`
     // otherwise its calls to `onContent` method won't do anything.
-    if (has(this.attachTo, 'existing'))
+    if (this.attachTo.includes('existing'))
       applyOnExistingDocuments(this);
   },
 
   dispose: function() {
     let style = styleFor(this);
     if (style)
       detach(style);
 
@@ -127,19 +121,19 @@ function applyOnExistingDocuments (mod) 
   for (let frame of frames) {
     // Fake a newly created document
     let window = frame.content;
     // on startup with e10s, contentWindow might not exist yet,
     // in which case we will get notified by "document-element-inserted".
     if (!window || !window.frames)
       return;
     let uri = window.location.href;
-    if (has(mod.attachTo, "top") && modMatchesURI(mod, uri))
+    if (mod.attachTo.includes("top") && modMatchesURI(mod, uri))
       onContent(mod, window);
-    if (has(mod.attachTo, "frame"))
+    if (mod.attachTo.includes("frame"))
       getFrames(window).
         filter(iframe => modMatchesURI(mod, iframe.location.href)).
         forEach(frame => onContent(mod, frame));
   }
 }
 
 function createWorker(mod, window) {
   let workerId = String(uuid());
@@ -162,20 +156,20 @@ function createWorker(mod, window) {
   });
 
   once(worker, 'detach', () => worker.destroy());
 }
 
 function onContent (mod, window) {
   let isTopDocument = window.top === window;
   // Is a top level document and `top` is not set, ignore
-  if (isTopDocument && !has(mod.attachTo, "top"))
+  if (isTopDocument && !mod.attachTo.includes("top"))
     return;
   // Is a frame document and `frame` is not set, ignore
-  if (!isTopDocument && !has(mod.attachTo, "frame"))
+  if (!isTopDocument && !mod.attachTo.includes("frame"))
     return;
 
   // ensure we attach only once per document
   let seen = mod.seenDocuments;
   if (seen.has(window.document))
     return;
   seen.set(window.document, true);
 
--- a/addon-sdk/source/lib/sdk/content/page-worker.js
+++ b/addon-sdk/source/lib/sdk/content/page-worker.js
@@ -1,28 +1,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { frames } = require("../remote/child");
 const { Class } = require("../core/heritage");
 const { Disposable } = require('../core/disposable');
-const { data } = require("../self");
-const { once } = require("../dom/events");
-const { getAttachEventType } = require("./utils");
-const { Rules } = require('../util/rules');
-const { uuid } = require('../util/uuid');
-const { WorkerChild } = require("./worker-child");
+lazyRequire(this, "../self", "data");
+lazyRequire(this, "../dom/events", "once");
+lazyRequire(this, "./utils", "getAttachEventType");
+lazyRequire(this, '../util/rules', "Rules");
+lazyRequire(this, '../util/uuid', "uuid");
+lazyRequire(this, "./worker-child", "WorkerChild");
 const { Cc, Ci, Cu } = require("chrome");
 const { on: onSystemEvent } = require("../system/events");
 
-const appShell = Cc["@mozilla.org/appshell/appShellService;1"].getService(Ci.nsIAppShellService);
+const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 
-const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyServiceGetter(this, 'appShell',
+                                   "@mozilla.org/appshell/appShellService;1",
+                                   "nsIAppShellService");
 
 const pages = new Map();
 
 const DOC_INSERTED = "document-element-inserted";
 
 function isValidURL(page, url) {
   return !page.rules || page.rules.matchesAny(url);
 }
--- a/addon-sdk/source/lib/sdk/content/sandbox.js
+++ b/addon-sdk/source/lib/sdk/content/sandbox.js
@@ -4,29 +4,32 @@
 'use strict';
 
 module.metadata = {
   'stability': 'unstable'
 };
 
 const { Class } = require('../core/heritage');
 const { EventTarget } = require('../event/target');
-const { on, off, emit } = require('../event/core');
-const { events } = require('./sandbox/events');
-const { requiresAddonGlobal } = require('./utils');
-const { delay: async } = require('../lang/functional');
+lazyRequire(this, '../event/core', "on", "off", "emit");
+lazyRequire(this, './sandbox/events', "events");
+lazyRequire(this, './utils', "requiresAddonGlobal");
+lazyRequire(this, '../lang/functional', {"delay": "async"});
 const { Ci, Cu, Cc } = require('chrome');
-const timer = require('../timers');
-const { URL } = require('../url');
-const { sandbox, evaluate, load } = require('../loader/sandbox');
-const { merge } = require('../util/object');
-const { getTabForContentWindowNoShim } = require('../tabs/utils');
-const { getInnerId } = require('../window/utils');
-const { PlainTextConsole } = require('../console/plain-text');
-const { data } = require('../self');const { isChildLoader } = require('../remote/core');
+lazyRequireModule(this, "../timers", "timer");
+lazyRequire(this, '../url', "URL");
+lazyRequire(this, '../loader/sandbox', "sandbox", "evaluate", "load");
+lazyRequire(this, '../util/object', "merge");
+lazyRequire(this, '../tabs/utils', "getTabForContentWindowNoShim");
+lazyRequire(this, '../window/utils', "getInnerId");
+lazyRequire(this, '../console/plain-text', "PlainTextConsole");
+
+lazyRequire(this, '../self', "data");
+lazyRequire(this, '../remote/core', "isChildLoader");
+
 // WeakMap of sandboxes so we can access private values
 const sandboxes = new WeakMap();
 
 /* Trick the linker in order to ensure shipping these files in the XPI.
   require('./content-worker.js');
   Then, retrieve URL of these files in the XPI:
 */
 var prefix = module.uri.split('sandbox.js')[0];
--- a/addon-sdk/source/lib/sdk/content/utils.js
+++ b/addon-sdk/source/lib/sdk/content/utils.js
@@ -2,22 +2,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 module.metadata = {
   'stability': 'unstable'
 };
 
-var { merge } = require('../util/object');
-var { data } = require('../self');
+lazyRequire(this, '../util/object', "merge");
+lazyRequire(this, '../self', "data");
 var assetsURI = data.url();
 var isArray = Array.isArray;
 var method = require('../../method/core');
-var { uuid } = require('../util/uuid');
+lazyRequire(this, '../util/uuid', "uuid");
 
 const isAddonContent = ({ contentURL }) =>
   contentURL && data.url(contentURL).startsWith(assetsURI);
 
 exports.isAddonContent = isAddonContent;
 
 function hasContentScript({ contentScript, contentScriptFile }) {
   return (isArray(contentScript) ? contentScript.length > 0 :
--- a/addon-sdk/source/lib/sdk/content/worker-child.js
+++ b/addon-sdk/source/lib/sdk/content/worker-child.js
@@ -1,22 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
-const { merge } = require('../util/object');
+lazyRequire(this, '../util/object', 'merge');
 const { Class } = require('../core/heritage');
-const { emit } = require('../event/core');
+lazyRequire(this, '../event/core', 'emit');
 const { EventTarget } = require('../event/target');
-const { getInnerId, getByInnerId } = require('../window/utils');
-const { instanceOf, isObject } = require('../lang/type');
-const system = require('../system/events');
+lazyRequire(this, '../window/utils', 'getInnerId');
+lazyRequire(this, '../lang/type', 'instanceOf', 'isObject');
+lazyRequireModule(this, '../system/events', 'system');
 const { when } = require('../system/unload');
-const { WorkerSandbox } = require('./sandbox');
+lazyRequire(this, './sandbox', 'WorkerSandbox');
 const { Ci } = require('chrome');
 const { process, frames } = require('../remote/child');
 
 const EVENTS = {
   'chrome-page-shown': 'pageshow',
   'content-page-shown': 'pageshow',
   'chrome-page-hidden': 'pagehide',
   'content-page-hidden': 'pagehide',
--- a/addon-sdk/source/lib/sdk/content/worker.js
+++ b/addon-sdk/source/lib/sdk/content/worker.js
@@ -2,29 +2,29 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
-const { emit } = require('../event/core');
+lazyRequire(this, '../event/core', "emit");
 const { omit, merge } = require('../util/object');
 const { Class } = require('../core/heritage');
 const { method } = require('../lang/functional');
-const { getInnerId } = require('../window/utils');
+lazyRequire(this, '../window/utils', "getInnerId");
 const { EventTarget } = require('../event/target');
-const { isPrivate } = require('../private-browsing/utils');
-const { getTabForBrowser, getTabForContentWindowNoShim, getBrowserForTab } = require('../tabs/utils');
-const { attach, connect, detach, destroy, makeChildOptions } = require('./utils');
+lazyRequire(this, '../private-browsing/utils', "isPrivate");
+lazyRequire(this, '../tabs/utils', "getTabForBrowser", "getTabForContentWindowNoShim", "getBrowserForTab");
+lazyRequire(this, './utils', "attach", "connect", "detach", "destroy", "makeChildOptions");
 const { ensure } = require('../system/unload');
-const { on: observe } = require('../system/events');
+lazyRequire(this, '../system/events', {"on": "observe"});
 const { Ci, Cu } = require('chrome');
-const { modelFor: tabFor } = require('sdk/model/core');
+lazyRequire(this, 'sdk/model/core', {"modelFor": "tabFor"});
 const { remoteRequire, processes, frames } = require('../remote/parent');
 remoteRequire('sdk/content/worker-child');
 
 const workers = new WeakMap();
 var modelFor = (worker) => workers.get(worker);
 
 const ERR_DESTROYED = "Couldn't find the worker to receive this message. " +
   "The script may not be initialized yet, or may already have been unloaded.";
--- a/addon-sdk/source/lib/sdk/context-menu.js
+++ b/addon-sdk/source/lib/sdk/context-menu.js
@@ -8,29 +8,28 @@ module.metadata = {
   "engines": {
     // TODO Fennec support Bug 788334
     "Firefox": "*",
     "SeaMonkey": "*"
   }
 };
 
 const { Class, mix } = require("./core/heritage");
-const { addCollectionProperty } = require("./util/collection");
 const { ns } = require("./core/namespace");
-const { validateOptions, getTypeOf } = require("./deprecated/api-utils");
-const { URL, isValidURI } = require("./url");
-const { WindowTracker, browserWindowIterator } = require("./deprecated/window-utils");
-const { isBrowser, getInnerId } = require("./window/utils");
-const { MatchPattern } = require("./util/match-pattern");
+lazyRequire(this, "./deprecated/api-utils", "validateOptions", "getTypeOf");
+lazyRequire(this, "./url", "URL", "isValidURI");
+lazyRequire(this, "./deprecated/window-utils", "WindowTracker", "browserWindowIterator");
+lazyRequire(this, "./window/utils", "isBrowser", "getInnerId");
+lazyRequire(this, "./util/match-pattern", "MatchPattern");
 const { EventTarget } = require("./event/target");
-const { emit } = require('./event/core');
+lazyRequire(this, './event/core', "emit");
 const { when } = require('./system/unload');
 const { contract: loaderContract } = require('./content/loader');
 const { omit } = require('./util/object');
-const self = require('./self')
+lazyRequireModule(this, './self', "self");
 const { remoteRequire, processes } = require('./remote/parent');
 remoteRequire('sdk/content/context-menu');
 
 // All user items we add have this class.
 const ITEM_CLASS = "addon-context-menu-item";
 
 // Items in the top-level context menu also have this class.
 const TOPLEVEL_ITEM_CLASS = "addon-context-menu-item-toplevel";
--- a/addon-sdk/source/lib/sdk/context-menu/context.js
+++ b/addon-sdk/source/lib/sdk/context-menu/context.js
@@ -1,15 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const { Class } = require("../core/heritage");
-const { extend } = require("../util/object");
-const { MatchPattern } = require("../util/match-pattern");
+lazyRequire(this, "../util/match-pattern", "MatchPattern");
 const readers = require("./readers");
 
 // Context class is required to implement a single `isCurrent(target)` method
 // that must return boolean value indicating weather given target matches a
 // context or not. Most context implementations below will have an associated
 // reader that way context implementation can setup a reader to extract necessary
 // information to make decision if target is matching a context.
 const Context = Class({
--- a/addon-sdk/source/lib/sdk/core/disposable.js
+++ b/addon-sdk/source/lib/sdk/core/disposable.js
@@ -5,18 +5,19 @@
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Class } = require("./heritage");
 const { Observer, subscribe, unsubscribe, observe } = require("./observer");
-const { isWeak } = require("./reference");
-const SDKWeakSet = require("../lang/weak-set");
+
+lazyRequire(this, "./reference", "isWeak");
+lazyRequireModule(this, "../lang/weak-set", "SDKWeakSet");
 
 const method = require("../../method/core");
 
 const unloadSubject = require('@loader/unload');
 const addonUnloadTopic = "sdk:loader:destroy";
 
 const uninstall = method("disposable/uninstall");
 exports.uninstall = uninstall;
--- a/addon-sdk/source/lib/sdk/core/observer.js
+++ b/addon-sdk/source/lib/sdk/core/observer.js
@@ -6,17 +6,17 @@
 
 module.metadata = {
   "stability": "experimental"
 };
 
 
 const { Cc, Ci, Cr, Cu } = require("chrome");
 const { Class } = require("./heritage");
-const { isWeak } = require("./reference");
+lazyRequire(this, "./reference", "isWeak");
 const method = require("../../method/core");
 
 const observerService = Cc['@mozilla.org/observer-service;1'].
                           getService(Ci.nsIObserverService);
 
 const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
 const addObserver = ShimWaiver.getProperty(observerService, "addObserver");
 const removeObserver = ShimWaiver.getProperty(observerService, "removeObserver");
--- a/addon-sdk/source/lib/sdk/deprecated/unit-test.js
+++ b/addon-sdk/source/lib/sdk/deprecated/unit-test.js
@@ -2,22 +2,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "deprecated"
 };
 
-const timer = require("../timers");
+lazyRequireModule(this, "../timers", "timer");
 const cfxArgs = require("../test/options");
-const { getTabs, closeTab, getURI, getTabId, getSelectedTab } = require("../tabs/utils");
-const { windows, isBrowser, getMostRecentBrowserWindow } = require("../window/utils");
+lazyRequire(this, "../tabs/utils", "getTabs", "closeTab", "getURI", "getTabId", "getSelectedTab");
+lazyRequire(this, "../window/utils", "windows", "isBrowser", "getMostRecentBrowserWindow");
 const { defer, all, Debugging: PromiseDebugging, resolve } = require("../core/promise");
-const { getInnerId } = require("../window/utils");
+lazyRequire(this, "../window/utils", "getInnerId");
 const { cleanUI } = require("../test/utils");
 
 const findAndRunTests = function findAndRunTests(options) {
   var TestFinder = require("./unit-test-finder").TestFinder;
   var finder = new TestFinder({
     filter: options.filter,
     testInProcess: options.testInProcess,
     testOutOfProcess: options.testOutOfProcess
--- a/addon-sdk/source/lib/sdk/event/dom.js
+++ b/addon-sdk/source/lib/sdk/event/dom.js
@@ -5,17 +5,17 @@
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
 const { Ci } = require("chrome");
 
-var { emit } = require("./core");
+lazyRequire(this, "./core", "emit");
 var { when: unload } = require("../system/unload");
 var listeners = new WeakMap();
 
 const { Cu } = require("chrome");
 const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
 const { ThreadSafeChromeUtils } = Cu.import("resource://gre/modules/Services.jsm", {});
 
 var getWindowFrom = x =>
--- a/addon-sdk/source/lib/sdk/event/utils.js
+++ b/addon-sdk/source/lib/sdk/event/utils.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
-var { emit, on, once, off, EVENT_TYPE_PATTERN } = require("./core");
+lazyRequire(this, "./core", "emit", "on", "once", "off", "EVENT_TYPE_PATTERN");
 const { Cu } = require("chrome");
 
 // This module provides set of high order function for working with event
 // streams (streams in a NodeJS style that dispatch data, end and error
 // events).
 
 // Function takes a `target` object and returns set of implicit references
 // (non property references) it keeps. This basically allows defining
--- a/addon-sdk/source/lib/sdk/frame/hidden-frame.js
+++ b/addon-sdk/source/lib/sdk/frame/hidden-frame.js
@@ -7,23 +7,23 @@
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Cc, Ci } = require("chrome");
 const { Class } = require("../core/heritage");
 const { List, addListItem, removeListItem } = require("../util/list");
 const { EventTarget } = require("../event/target");
-const { emit } = require("../event/core");
-const { create: makeFrame } = require("./utils");
-const { defer } = require("../core/promise");
+lazyRequire(this, "../event/core", "emit");
+lazyRequire(this, "./utils", { "create": "makeFrame" });
+lazyRequire(this, "../core/promise", "defer");
 const { when: unload } = require("../system/unload");
-const { validateOptions, getTypeOf } = require("../deprecated/api-utils");
-const { window } = require("../addon/window");
-const { fromIterator } = require("../util/array");
+lazyRequire(this, "../deprecated/api-utils", "validateOptions", "getTypeOf");
+lazyRequire(this, "../addon/window", "window");
+lazyRequire(this, "../util/array", "fromIterator");
 
 // This cache is used to access friend properties between functions
 // without exposing them on the public API.
 var cache = new Set();
 var elements = new WeakMap();
 
 function contentLoaded(target) {
   var deferred = defer();
--- a/addon-sdk/source/lib/sdk/io/buffer.js
+++ b/addon-sdk/source/lib/sdk/io/buffer.js
@@ -13,18 +13,18 @@ module.metadata = {
  * utf-8, utf-16le, utf-16be
  * http://encoding.spec.whatwg.org/#interface-textencoder
  *
  * Node however supports the following encodings:
  * ascii, utf-8, utf-16le, usc2, base64, hex
  */
 
 const { Cu } = require('chrome');
-const { isNumber } = require('sdk/lang/type');
-const { TextEncoder, TextDecoder } = Cu.import('resource://gre/modules/commonjs/toolkit/loader.js', {});
+lazyRequire(this, 'sdk/lang/type', "isNumber");
+Cu.importGlobalProperties(["TextEncoder", "TextDecoder"]);
 
 exports.TextEncoder = TextEncoder;
 exports.TextDecoder = TextDecoder;
 
 /**
  * Use WeakMaps to work around Bug 929146, which prevents us from adding
  * getters or values to typed arrays
  * https://bugzilla.mozilla.org/show_bug.cgi?id=929146
--- a/addon-sdk/source/lib/sdk/io/file.js
+++ b/addon-sdk/source/lib/sdk/io/file.js
@@ -4,18 +4,19 @@
 
 "use strict";
 
 module.metadata = {
   "stability": "deprecated"
 };
 
 const {Cc,Ci,Cr} = require("chrome");
-const byteStreams = require("./byte-streams");
-const textStreams = require("./text-streams");
+
+lazyRequireModule(this, "./byte-streams", "byteStreams");
+lazyRequireModule(this, "./text-streams", "textStreams");
 
 // Flags passed when opening a file.  See nsprpub/pr/include/prio.h.
 const OPEN_FLAGS = {
   RDONLY: parseInt("0x01"),
   WRONLY: parseInt("0x02"),
   CREATE_FILE: parseInt("0x08"),
   APPEND: parseInt("0x10"),
   TRUNCATE: parseInt("0x20"),
--- a/addon-sdk/source/lib/sdk/io/fs.js
+++ b/addon-sdk/source/lib/sdk/io/fs.js
@@ -4,20 +4,21 @@
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Cc, Ci, CC } = require("chrome");
 
-const { setTimeout } = require("../timers");
-const { Stream, InputStream, OutputStream } = require("./stream");
-const { emit, on } = require("../event/core");
-const { Buffer } = require("./buffer");
+lazyRequire(this, "../timers", "setTimeout");
+lazyRequire(this, "./stream", "Stream", "InputStream", "OutputStream");
+lazyRequire(this, "../event/core", "emit", "on");
+lazyRequire(this, "./buffer", "Buffer");
+
 const { ns } = require("../core/namespace");
 const { Class } = require("../core/heritage");
 
 
 const nsILocalFile = CC("@mozilla.org/file/local;1", "nsILocalFile",
                         "initWithPath");
 const FileOutputStream = CC("@mozilla.org/network/file-output-stream;1",
                             "nsIFileOutputStream", "init");
--- a/addon-sdk/source/lib/sdk/io/stream.js
+++ b/addon-sdk/source/lib/sdk/io/stream.js
@@ -4,20 +4,21 @@
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const { CC, Cc, Ci, Cu, Cr, components } = require("chrome");
 const { EventTarget } = require("../event/target");
-const { emit } = require("../event/core");
-const { Buffer } = require("./buffer");
 const { Class } = require("../core/heritage");
-const { setTimeout } = require("../timers");
+
+lazyRequire(this, "../event/core", "emit");
+lazyRequire(this, "./buffer", "Buffer");
+lazyRequire(this, "../timers", "setTimeout");
 
 
 const MultiplexInputStream = CC("@mozilla.org/io/multiplex-input-stream;1",
                                 "nsIMultiplexInputStream");
 const AsyncStreamCopier = CC("@mozilla.org/network/async-stream-copier;1",
                              "nsIAsyncStreamCopier", "init");
 const StringInputStream = CC("@mozilla.org/io/string-input-stream;1",
                              "nsIStringInputStream");
--- a/addon-sdk/source/lib/sdk/lang/functional/concurrent.js
+++ b/addon-sdk/source/lib/sdk/lang/functional/concurrent.js
@@ -8,17 +8,18 @@
 
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
 const { arity, name, derive, invoke } = require("./helpers");
-const { setTimeout, clearTimeout, setImmediate } = require("../../timers");
+
+lazyRequire(this, "sdk/timers", "setTimeout", "clearTimeout", "setImmediate");
 
 /**
  * Takes a function and returns a wrapped one instead, calling which will call
  * original function in the next turn of event loop. This is basically utility
  * to do `setImmediate(function() { ... })`, with a difference that returned
  * function is reused, instead of creating a new one each time. This also allows
  * to use this functions as event listeners.
  */
--- a/addon-sdk/source/lib/sdk/net/url.js
+++ b/addon-sdk/source/lib/sdk/net/url.js
@@ -5,18 +5,17 @@
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Ci, Cu, components } = require("chrome");
 
-const { defer } = require("../core/promise");
-const { merge } = require("../util/object");
+lazyRequire(this, "../core/promise", "defer");
 
 const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
 /**
  * Reads a URI and returns a promise.
  *
  * @param uri {string} The URI to read
--- a/addon-sdk/source/lib/sdk/page-mod.js
+++ b/addon-sdk/source/lib/sdk/page-mod.js
@@ -7,23 +7,23 @@ module.metadata = {
   "stability": "stable"
 };
 
 const { contract: loaderContract } = require('./content/loader');
 const { contract } = require('./util/contract');
 const { WorkerHost, connect } = require('./content/utils');
 const { Class } = require('./core/heritage');
 const { Disposable } = require('./core/disposable');
-const { Worker } = require('./content/worker');
+lazyRequire(this, './content/worker', "Worker");
 const { EventTarget } = require('./event/target');
-const { on, emit, once, setListeners } = require('./event/core');
-const { isRegExp, isUndefined } = require('./lang/type');
+lazyRequire(this, './event/core', "on", "emit", "once", "setListeners");
+lazyRequire(this, './lang/type', "isRegExp", "isUndefined");
 const { merge, omit } = require('./util/object');
-const { remove, has, hasAny } = require("./util/array");
-const { Rules } = require("./util/rules");
+lazyRequire(this, "./util/array", "remove", "has", "hasAny");
+lazyRequire(this, "./util/rules", "Rules");
 const { processes, frames, remoteRequire } = require('./remote/parent');
 remoteRequire('sdk/content/page-mod');
 
 const pagemods = new Map();
 const workers = new Map();
 const models = new WeakMap();
 var modelFor = (mod) => models.get(mod);
 var workerFor = (mod) => workers.get(mod)[0];
--- a/addon-sdk/source/lib/sdk/page-worker.js
+++ b/addon-sdk/source/lib/sdk/page-worker.js
@@ -4,29 +4,29 @@
 "use strict";
 
 module.metadata = {
   "stability": "stable"
 };
 
 const { Class } = require('./core/heritage');
 const { ns } = require('./core/namespace');
-const { pipe, stripListeners } = require('./event/utils');
+lazyRequire(this, './event/utils', "pipe", "stripListeners");
 const { connect, destroy, WorkerHost } = require('./content/utils');
-const { Worker } = require('./content/worker');
+lazyRequire(this, './content/worker', "Worker");
 const { Disposable } = require('./core/disposable');
 const { EventTarget } = require('./event/target');
-const { setListeners } = require('./event/core');
-const { window } = require('./addon/window');
-const { create: makeFrame, getDocShell } = require('./frame/utils');
+lazyRequire(this, './event/core', "setListeners");
+lazyRequire(this, './addon/window', "window");
+lazyRequire(this, './frame/utils', { "create": "makeFrame" }, "getDocShell");
 const { contract } = require('./util/contract');
 const { contract: loaderContract } = require('./content/loader');
-const { Rules } = require('./util/rules');
+lazyRequire(this, './util/rules', "Rules");
 const { merge } = require('./util/object');
-const { uuid } = require('./util/uuid');
+lazyRequire(this, './util/uuid', "uuid");
 const { useRemoteProcesses, remoteRequire, frames } = require("./remote/parent");
 remoteRequire("sdk/content/page-worker");
 
 const workers = new WeakMap();
 const pages = new Map();
 
 const internal = ns();
 
--- a/addon-sdk/source/lib/sdk/panel.js
+++ b/addon-sdk/source/lib/sdk/panel.js
@@ -9,38 +9,37 @@ module.metadata = {
   "stability": "stable",
   "engines": {
     "Firefox": "*",
     "SeaMonkey": "*"
   }
 };
 
 const { Cu, Ci } = require("chrome");
-const { setTimeout } = require('./timers');
+lazyRequire(this, './timers', "setTimeout");
 const { Class } = require("./core/heritage");
 const { DefaultWeakMap, merge } = require("./util/object");
 const { WorkerHost } = require("./content/utils");
-const { Worker } = require("./deprecated/sync-worker");
+lazyRequire(this, "./deprecated/sync-worker", "Worker");
 const { Disposable } = require("./core/disposable");
 const { WeakReference } = require('./core/reference');
 const { contract: loaderContract } = require("./content/loader");
 const { contract } = require("./util/contract");
-const { on, off, emit, setListeners } = require("./event/core");
+lazyRequire(this, "./event/core", "on", "off", "emit", "setListeners");
 const { EventTarget } = require("./event/target");
-const domPanel = require("./panel/utils");
-const { getDocShell } = require('./frame/utils');
+lazyRequireModule(this, "./panel/utils", "domPanel");
+lazyRequire(this, './frame/utils', "getDocShell");
 const { events } = require("./panel/events");
-const systemEvents = require("./system/events");
 const { filter, pipe, stripListeners } = require("./event/utils");
-const { getNodeView, getActiveView } = require("./view/core");
-const { isNil, isObject, isNumber } = require("./lang/type");
-const { getAttachEventType } = require("./content/utils");
+lazyRequire(this, "./view/core", "getNodeView", "getActiveView");
+lazyRequire(this, "./lang/type", "isNil", "isObject", "isNumber");
+lazyRequire(this, "./content/utils", "getAttachEventType");
 const { number, boolean, object } = require('./deprecated/api-utils');
-const { Style } = require("./stylesheet/style");
-const { attach, detach } = require("./content/mod");
+lazyRequire(this, "./stylesheet/style", "Style");
+lazyRequire(this, "./content/mod", "attach", "detach");
 
 var isRect = ({top, right, bottom, left}) => [top, right, bottom, left].
   some(value => isNumber(value) && !isNaN(value));
 
 var isSDKObj = obj => obj instanceof Class;
 
 var rectContract = contract({
   top: number,
--- a/addon-sdk/source/lib/sdk/panel/events.js
+++ b/addon-sdk/source/lib/sdk/panel/events.js
@@ -7,17 +7,17 @@
 // This module basically translates system/events to a SDK standard events
 // so that `map`, `filter` and other utilities could be used with them.
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const events = require("../system/events");
-const { emit } = require("../event/core");
+lazyRequire(this, "../event/core", "emit");
 
 var channel = {};
 
 function forward({ subject, type, data }) {
   return emit(channel, "data", { target: subject, type: type, data: data });
 }
 
 ["popupshowing", "popuphiding", "popupshown", "popuphidden",
--- a/addon-sdk/source/lib/sdk/panel/utils.js
+++ b/addon-sdk/source/lib/sdk/panel/utils.js
@@ -5,27 +5,27 @@
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
 const { Cc, Ci } = require("chrome");
 const { Services } = require("resource://gre/modules/Services.jsm");
-const { setTimeout } = require("../timers");
-const { platform } = require("../system");
-const { getMostRecentBrowserWindow, getOwnerBrowserWindow,
-        getHiddenWindow, getScreenPixelsPerCSSPixel } = require("../window/utils");
+lazyRequire(this, "../timers", "setTimeout");
+lazyRequire(this, "../system", "platform");
+lazyRequire(this, "../window/utils", "getMostRecentBrowserWindow", "getOwnerBrowserWindow",
+            "getHiddenWindow", "getScreenPixelsPerCSSPixel");
 
-const { create: createFrame, swapFrameLoaders, getDocShell } = require("../frame/utils");
-const { window: addonWindow } = require("../addon/window");
-const { isNil } = require("../lang/type");
-const { data } = require('../self');
+lazyRequire(this, "../frame/utils", { "create": "createFrame" }, "swapFrameLoaders", "getDocShell");
+lazyRequire(this, "../addon/window", { "window": "addonWindow" });
+lazyRequire(this, "../lang/type", "isNil");
+lazyRequire(this, '../self', "data");
 
-const events = require("../system/events");
+lazyRequireModule(this, "../system/events", "events",);
 
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 function calculateRegion({ position, width, height, defaultWidth, defaultHeight }, rect) {
   position = position || {};
 
   let x, y;
--- a/addon-sdk/source/lib/sdk/platform/xpcom.js
+++ b/addon-sdk/source/lib/sdk/platform/xpcom.js
@@ -7,19 +7,19 @@
 module.metadata = {
   "stability": "unstable"
 };
 
 const { Cc, Ci, Cr, Cm, components: { classesByID } } = require('chrome');
 const { registerFactory, unregisterFactory, isCIDRegistered } =
       Cm.QueryInterface(Ci.nsIComponentRegistrar);
 
-const { merge } = require('../util/object');
 const { Class, extend, mix } = require('../core/heritage');
-const { uuid } = require('../util/uuid');
+lazyRequire(this, '../util/object', 'merge');
+lazyRequire(this, '../util/uuid', 'uuid');
 
 // This is a base prototype, that provides bare bones of XPCOM. JS based
 // components can be easily implement by extending it.
 const Unknown = new function() {
   function hasInterface(component, iid) {
     return component && component.interfaces &&
       ( component.interfaces.some(id => iid.equals(Ci[id])) ||
         component.implements.some($ => hasInterface($, iid)) ||
--- a/addon-sdk/source/lib/sdk/preferences/native-options.js
+++ b/addon-sdk/source/lib/sdk/preferences/native-options.js
@@ -3,74 +3,73 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 module.metadata = {
   "stability": "unstable"
 };
 
 const { Cc, Ci, Cu } = require('chrome');
-const { on } = require('../system/events');
-const { id, preferencesBranch } = require('../self');
-const { localizeInlineOptions } = require('../l10n/prefs');
-const { Services } = require("resource://gre/modules/Services.jsm");
-const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm");
-const { defer } = require("sdk/core/promise");
+lazyRequire(this, '../system/events', "on");
+lazyRequire(this, '../self', "preferencesBranch");
+lazyRequire(this, '../l10n/prefs', "localizeInlineOptions");
+
+lazyRequire(this, "resource://gre/modules/Services.jsm", "Services");
+lazyRequire(this, "resource://gre/modules/AddonManager.jsm", "AddonManager");
+lazyRequire(this, "resource://gre/modules/Preferences.jsm", "Preferences");
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";;
 const DEFAULT_OPTIONS_URL = 'data:text/xml,<placeholder/>';
 
 const VALID_PREF_TYPES = ['bool', 'boolint', 'integer', 'string', 'color',
                           'file', 'directory', 'control', 'menulist', 'radio'];
 
 const isFennec = require("sdk/system/xul-app").is("Fennec");
 
 function enable({ preferences, id }) {
-  let enabled = defer();
+  return new Promise(resolve => {
+    validate(preferences);
 
-  validate(preferences);
-
-  setDefaults(preferences, preferencesBranch);
+    setDefaults(preferences, preferencesBranch);
 
-  // allow the use of custom options.xul
-  AddonManager.getAddonByID(id, (addon) => {
-    on('addon-options-displayed', onAddonOptionsDisplayed, true);
-    enabled.resolve({ id: id });
-  });
+    // allow the use of custom options.xul
+    AddonManager.getAddonByID(id, (addon) => {
+      on('addon-options-displayed', onAddonOptionsDisplayed, true);
+      resolve({ id });
+    });
 
-  function onAddonOptionsDisplayed({ subject: doc, data }) {
-    if (data === id) {
-      let parent;
+    function onAddonOptionsDisplayed({ subject: doc, data }) {
+      if (data === id) {
+        let parent;
 
-      if (isFennec) {
-        parent = doc.querySelector('.options-box');
+        if (isFennec) {
+          parent = doc.querySelector('.options-box');
 
-        // NOTE: This disable the CSS rule that makes the options invisible
-        let item = doc.querySelector('#addons-details .addon-item');
-        item.removeAttribute("optionsURL");
-      } else {
-        parent = doc.getElementById('detail-downloads').parentNode;
-      }
+          // NOTE: This disable the CSS rule that makes the options invisible
+          let item = doc.querySelector('#addons-details .addon-item');
+          item.removeAttribute("optionsURL");
+        } else {
+          parent = doc.getElementById('detail-downloads').parentNode;
+        }
 
-      if (parent) {
-        injectOptions({
-          preferences: preferences,
-          preferencesBranch: preferencesBranch,
-          document: doc,
-          parent: parent,
-          id: id
-        });
-        localizeInlineOptions(doc);
-      } else {
-        throw Error("Preferences parent node not found in Addon Details. The configured custom preferences will not be visible.");
+        if (parent) {
+          injectOptions({
+            preferences: preferences,
+            preferencesBranch: preferencesBranch,
+            document: doc,
+            parent: parent,
+            id: id
+          });
+          localizeInlineOptions(doc);
+        } else {
+          throw Error("Preferences parent node not found in Addon Details. The configured custom preferences will not be visible.");
+        }
       }
     }
-  }
-
-  return enabled.promise;
+  });
 }
 exports.enable = enable;
 
 // centralized sanity checks
 function validate(preferences) {
   for (let { name, title, type, label, options } of preferences) {
     // make sure the title is set and non-empty
     if (!title)
@@ -98,38 +97,24 @@ function validate(preferences) {
 
     // TODO: check that pref type matches default value type
   }
 }
 exports.validate = validate;
 
 // initializes default preferences, emulates defaults/prefs.js
 function setDefaults(preferences, preferencesBranch) {
-  const branch = Cc['@mozilla.org/preferences-service;1'].
-                 getService(Ci.nsIPrefService).
-                 getDefaultBranch('extensions.' + preferencesBranch + '.');
-  for (let { name, value } of preferences) {
-    switch (typeof value) {
-      case 'boolean':
-        branch.setBoolPref(name, value);
-        break;
-      case 'number':
-        // must be integer, ignore otherwise
-        if (value % 1 === 0) {
-          branch.setIntPref(name, value);
-        }
-        break;
-      case 'string':
-        let str = Cc["@mozilla.org/supports-string;1"].
-                  createInstance(Ci.nsISupportsString);
-        str.data = value;
-        branch.setComplexValue(name, Ci.nsISupportsString, str);
-        break;
-    }
-  }
+  let prefs = new Preferences({
+    branch: `extensions.${preferencesBranch}.`,
+    defaultBranch: true,
+  });
+
+  for (let { name, value } of preferences)
+    if (value !== undefined)
+      prefs.set(name, value);
 }
 exports.setDefaults = setDefaults;
 
 // dynamically injects inline options into about:addons page at runtime
 // NOTE: on Firefox Desktop the about:addons page is a xul page document,
 // on Firefox for Android the about:addons page is an xhtml page, to support both
 // the XUL xml namespace have to be enforced.
 function injectOptions({ preferences, preferencesBranch, document, parent, id }) {
--- a/addon-sdk/source/lib/sdk/private-browsing/utils.js
+++ b/addon-sdk/source/lib/sdk/private-browsing/utils.js
@@ -4,20 +4,21 @@
 'use strict';
 
 module.metadata = {
   "stability": "unstable"
 };
 
 const { Cc, Ci, Cu } = require('chrome');
 const { is } = require('../system/xul-app');
-const { isWindowPrivate } = require('../window/utils');
-const { isPrivateBrowsingSupported } = require('../self');
 const { dispatcher } = require("../util/dispatcher");
 
+lazyRequire(this, '../window/utils', "isWindowPrivate");
+lazyRequire(this, '../self', "isPrivateBrowsingSupported");
+
 var PrivateBrowsingUtils;
 
 // Private browsing is only supported in Fx
 try {
   PrivateBrowsingUtils = Cu.import('resource://gre/modules/PrivateBrowsingUtils.jsm', {}).PrivateBrowsingUtils;
 }
 catch (e) {}
 
--- a/addon-sdk/source/lib/sdk/remote/parent.js
+++ b/addon-sdk/source/lib/sdk/remote/parent.js
@@ -22,17 +22,18 @@ const { Disposable } = require('../core/
 const { omit } = require('../util/object');
 const { when } = require('../system/unload');
 const { EventTarget } = require('../event/target');
 const { emit } = require('../event/core');
 const system = require('../system/events');
 const { EventParent } = require('./utils');
 const options = require('@loader/options');
 const loaderModule = require('toolkit/loader');
-const { getTabForBrowser } = require('../tabs/utils');
+
+lazyRequire(this, '../tabs/utils', "getTabForBrowser");
 
 const appInfo = Cc["@mozilla.org/xre/app-info;1"].
                 getService(Ci.nsIXULRuntime);
 
 exports.useRemoteProcesses = appInfo.browserTabsRemoteAutostart;
 
 // Chose the right function for resolving relative a module id
 var moduleResolve;
@@ -321,18 +322,17 @@ var remoteModules = new Set();
 
 // Ensures a module is loaded in every child process. It is safe to send 
 // messages to this module immediately after calling this.
 // Pass a module to resolve the id relatively.
 function remoteRequire(id, module = null) {
   // Resolve relative to calling module if passed
   if (module)
     id = moduleResolve(id, module.id);
-  let uri = loaderModule.resolveURI(id, pathMapping);
 
   // Don't reload the same module
-  if (remoteModules.has(uri))
+  if (remoteModules.has(id))
     return;
 
-  remoteModules.add(uri);
-  processes.port.emit('sdk/remote/require', uri);
+  remoteModules.add(id);
+  processes.port.emit('sdk/remote/require', id);
 }
 exports.remoteRequire = remoteRequire;
--- a/addon-sdk/source/lib/sdk/remote/utils.js
+++ b/addon-sdk/source/lib/sdk/remote/utils.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { Class } = require('../core/heritage');
 const { List, addListItem, removeListItem } = require('../util/list');
-const { emit } = require('../event/core');
-const { pipe } = require('../event/utils');
+lazyRequire(this, '../event/core', 'emit');
+lazyRequire(this, '../event/utils', 'pipe');
 
 // A helper class that maintains a list of EventTargets. Any events emitted
 // to an EventTarget are also emitted by the EventParent. Likewise for an
 // EventTarget's port property.
 const EventParent = Class({
   implements: [ List ],
 
   attachItem: function(item) {
--- a/addon-sdk/source/lib/sdk/selection.js
+++ b/addon-sdk/source/lib/sdk/selection.js
@@ -7,28 +7,27 @@
 module.metadata = {
   "stability": "stable",
   "engines": {
     "Firefox": "*",
     "SeaMonkey": "*"
   }
 };
 
-const { Ci, Cc } = require("chrome"),
-    { setTimeout } = require("./timers"),
-    { emit, off } = require("./event/core"),
-    { Class, obscure } = require("./core/heritage"),
-    { EventTarget } = require("./event/target"),
-    { ns } = require("./core/namespace"),
-    { when: unload } = require("./system/unload"),
-    { ignoreWindow } = require('./private-browsing/utils'),
-    { getTabs, getTabForContentWindow,
-      getAllTabContentWindows } = require('./tabs/utils'),
-    winUtils = require("./window/utils"),
-    events = require("./system/events");
+const { Ci, Cc } = require("chrome");
+lazyRequire(this, "./timers", "setTimeout");
+lazyRequire(this, "./event/core", "emit", "off");
+const { Class, obscure } = require("./core/heritage");
+const { EventTarget } = require("./event/target");
+const { ns } = require("./core/namespace");
+const { when: unload } = require("./system/unload");
+lazyRequire(this, './private-browsing/utils', "ignoreWindow");
+lazyRequire(this, './tabs/utils', "getTabs", "getTabForContentWindow", "getAllTabContentWindows");
+lazyRequireModule(this, "./window/utils", "winUtils");
+const events = require("./system/events");
 
 // The selection types
 const HTML = 0x01,
       TEXT = 0x02,
       DOM  = 0x03; // internal use only
 
 // A more developer-friendly message than the caught exception when is not
 // possible change a selection.
--- a/addon-sdk/source/lib/sdk/self.js
+++ b/addon-sdk/source/lib/sdk/self.js
@@ -6,17 +6,17 @@
 module.metadata = {
   "stability": "stable"
 };
 
 const { CC } = require('chrome');
 const options = require('@loader/options');
 
 const { get } = require("./preferences/service");
-const { readURISync } = require('./net/url');
+lazyRequire(this, './net/url', "readURISync");
 
 const id = options.id;
 
 const readPref = key => get("extensions." + id + ".sdk." + key);
 
 const name = readPref("name") || options.name;
 const version = readPref("version") || options.version;
 const loadReason = readPref("load.reason") || options.loadReason;
--- a/addon-sdk/source/lib/sdk/simple-storage.js
+++ b/addon-sdk/source/lib/sdk/simple-storage.js
@@ -4,22 +4,22 @@
 
 "use strict";
 
 module.metadata = {
   "stability": "stable"
 };
 
 const { Cc, Ci } = require("chrome");
-const file = require("./io/file");
-const prefs = require("./preferences/service");
 const jpSelf = require("./self");
-const timer = require("./timers");
+lazyRequireModule(this, "./timers", "timer");
+lazyRequireModule(this, "./io/file", "file");
+lazyRequireModule(this, "./preferences/service", "prefs");
 const unload = require("./system/unload");
-const { emit, on, off } = require("./event/core");
+lazyRequire(this, "./event/core", "emit", "on", "off");
 
 const WRITE_PERIOD_PREF = "extensions.addon-sdk.simple-storage.writePeriod";
 const WRITE_PERIOD_DEFAULT = 300000; // 5 minutes
 
 const QUOTA_PREF = "extensions.addon-sdk.simple-storage.quota";
 const QUOTA_DEFAULT = 5242880; // 5 MiB
 
 const JETPACK_DIR_BASENAME = "jetpack";
--- a/addon-sdk/source/lib/sdk/stylesheet/style.js
+++ b/addon-sdk/source/lib/sdk/stylesheet/style.js
@@ -2,24 +2,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
-const { Cc, Ci } = require("chrome");
 const { Class } = require("../core/heritage");
-const { URL, isLocalURL } = require('../url');
-const events = require("../system/events");
-const { loadSheet, removeSheet, isTypeValid } = require("./utils");
-const { isString } = require("../lang/type");
+lazyRequire(this, "../url", "isLocalURL");
+lazyRequire(this, "./utils", "loadSheet", "removeSheet", "isTypeValid");
+lazyRequire(this, "../lang/type", "isString");
 const { attachTo, detachFrom } = require("../content/mod");
-const { data } = require('../self');
+lazyRequire(this, '../self', "data");
 
 const { freeze, create } = Object;
 
 function Style({ source, uri, type }) {
   source = source == null ? null : freeze([].concat(source));
   uri = uri == null ? null : freeze([].concat(uri));
   type = type == null ? "author" : type;
 
--- a/addon-sdk/source/lib/sdk/system.js
+++ b/addon-sdk/source/lib/sdk/system.js
@@ -7,40 +7,43 @@ module.metadata = {
   "stability": "unstable"
 };
 
 const { Cc, Ci, CC } = require('chrome');
 const options = require('@loader/options');
 const runtime = require("./system/runtime");
 const { when: unload } = require("./system/unload");
 
-const appStartup = Cc['@mozilla.org/toolkit/app-startup;1'].
-                   getService(Ci.nsIAppStartup);
+const { XPCOMUtils } = require('resource://gre/modules/XPCOMUtils.jsm');
+
+XPCOMUtils.defineLazyServiceGetter(this, 'appStartup',
+                                   '@mozilla.org/toolkit/app-startup;1',
+                                   'nsIAppStartup');
+XPCOMUtils.defineLazyServiceGetter(this, 'directoryService',
+                                   '@mozilla.org/file/directory_service;1',
+                                   'nsIProperties');
+
 const appInfo = Cc["@mozilla.org/xre/app-info;1"].
                 getService(Ci.nsIXULAppInfo);
-const directoryService = Cc['@mozilla.org/file/directory_service;1'].
-                         getService(Ci.nsIProperties);
 
 const PR_WRONLY = parseInt("0x02");
 const PR_CREATE_FILE = parseInt("0x08");
 const PR_APPEND = parseInt("0x10");
 const PR_TRUNCATE = parseInt("0x20");
 
 function openFile(path, mode) {
   let file = Cc["@mozilla.org/file/local;1"].
              createInstance(Ci.nsILocalFile);
   file.initWithPath(path);
   let stream = Cc["@mozilla.org/network/file-output-stream;1"].
                createInstance(Ci.nsIFileOutputStream);
   stream.init(file, mode, -1, 0);
   return stream
 }
 
-const { eAttemptQuit: E_ATTEMPT, eForceQuit: E_FORCE } = appStartup;
-
 /**
  * Parsed JSON object that was passed via `cfx --static-args "{ foo: 'bar' }"`
  */
 exports.staticArgs = options.staticArgs;
 
 /**
  * Environment variables. Environment variables are non-enumerable properties
  * of this object (key is name and value is value).
@@ -81,17 +84,17 @@ exports.exit = function exit(code) {
   }
 
   // Bug 856999: Prevent automatic kill of Firefox when running tests
   if (options.noQuit) {
     return unload(unloader);
   }
 
   unloader();
-  appStartup.quit(code ? E_ATTEMPT : E_FORCE);
+  appStartup.quit(code ? appStartup.eAttemptQuit : appStartup.eForceQuit);
 };
 
 // Adapter for nodejs's stdout & stderr:
 // http://nodejs.org/api/process.html#process_process_stdout
 var stdout = Object.freeze({ write: dump, end: dump });
 exports.stdout = stdout;
 exports.stderr = stdout;
 
--- a/addon-sdk/source/lib/sdk/system/globals.js
+++ b/addon-sdk/source/lib/sdk/system/globals.js
@@ -3,44 +3,45 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
-var { Cc, Ci, CC } = require('chrome');
-var { PlainTextConsole } = require('../console/plain-text');
-var { stdout } = require('../system');
-var ScriptError = CC('@mozilla.org/scripterror;1', 'nsIScriptError');
-var consoleService = Cc['@mozilla.org/consoleservice;1'].getService(Ci.nsIConsoleService);
+defineLazyGetter(this, "console", () => new (require('../console/plain-text').PlainTextConsole)());
 
-// On windows dump does not writes into stdout so cfx can't read thous dumps.
-// To workaround this issue we write to a special file from which cfx will
-// read and print to the console.
-// For more details see: bug-673383
-exports.dump = stdout.write;
-
-exports.console = new PlainTextConsole();
+defineLazyGetter(exports, "console", () => console);
 
 // Provide CommonJS `define` to allow authoring modules in a format that can be
 // loaded both into jetpack and into browser via AMD loaders.
-Object.defineProperty(exports, 'define', {
-  // `define` is provided as a lazy getter that binds below defined `define`
-  // function to the module scope, so that require, exports and module
-  // variables remain accessible.
-  configurable: true,
-  get: function() {
-    let sandbox = this;
-    return function define(factory) {
-      factory = Array.slice(arguments).pop();
-      factory.call(sandbox, sandbox.require, sandbox.exports, sandbox.module);
-    }
-  },
-  set: function(value) {
-    Object.defineProperty(this, 'define', {
+
+// `define` is provided as a lazy getter that binds below defined `define`
+// function to the module scope, so that require, exports and module
+// variables remain accessible.
+defineLazyGetter(exports, 'define', function() {
+  return (...args) => {
+    let factory = args.pop();
+    factory.call(this, this.require, this.exports, this.module);
+  };
+});
+
+function defineLazyGetter(object, prop, getter) {
+  let redefine = (obj, value) => {
+    Object.defineProperty(obj, prop, {
       configurable: true,
-      enumerable: true,
+      writable: true,
       value,
     });
-  },
-});
+    return value;
+  };
+
+  Object.defineProperty(object, prop, {
+    configurable: true,
+    get() {
+      return redefine(this, getter.call(this));
+    },
+    set(value) {
+      redefine(this, value);
+    }
+  });
+}
--- a/addon-sdk/source/lib/sdk/tabs/helpers.js
+++ b/addon-sdk/source/lib/sdk/tabs/helpers.js
@@ -6,17 +6,17 @@
 module.metadata = {
   'stability': 'unstable'
 };
 
 
 // NOTE: This file should only export Tab instances
 
 
-const { getTabForBrowser: getRawTabForBrowser } = require('./utils');
+lazyRequire(this, './utils', { "getTabForBrowser": "getRawTabForBrowser" });
 const { modelFor } = require('../model/core');
 
 exports.getTabForRawTab = modelFor;
 
 function getTabForBrowser(browser) {
   return modelFor(getRawTabForBrowser(browser)) || null;
 }
 exports.getTabForBrowser = getTabForBrowser;
--- a/addon-sdk/source/lib/sdk/tabs/tab.js
+++ b/addon-sdk/source/lib/sdk/tabs/tab.js
@@ -3,18 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 module.metadata = {
   'stability': 'unstable'
 };
 
 const { getTargetWindow } = require("../content/mod");
-const { getTabContentWindow, isTab } = require("./utils");
-const { viewFor } = require("../view/core");
+lazyRequire(this, "./utils", "getTabContentWindow", "isTab");
+lazyRequire(this, "../view/core", "viewFor");
 
 if (require('../system/xul-app').name == 'Fennec') {
   module.exports = require('./tab-fennec');
 }
 else {
   module.exports = require('./tab-firefox');
 }
 
--- a/addon-sdk/source/lib/sdk/tabs/tabs-firefox.js
+++ b/addon-sdk/source/lib/sdk/tabs/tabs-firefox.js
@@ -1,27 +1,27 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { Class } = require('../core/heritage');
 const { Tab, tabEvents } = require('./tab');
 const { EventTarget } = require('../event/target');
-const { emit, setListeners } = require('../event/core');
+lazyRequire(this, '../event/core', "emit", "setListeners");
 const { pipe } = require('../event/utils');
 const { observer: windowObserver } = require('../windows/observer');
 const { List, addListItem, removeListItem } = require('../util/list');
-const { modelFor } = require('../model/core');
-const { viewFor } = require('../view/core');
-const { getTabs, getSelectedTab } = require('./utils');
-const { getMostRecentBrowserWindow, isBrowser } = require('../window/utils');
-const { Options } = require('./common');
-const { isPrivate } = require('../private-browsing');
-const { ignoreWindow, isWindowPBSupported } = require('../private-browsing/utils')
+lazyRequire(this, '../model/core', "modelFor");
+lazyRequire(this, '../view/core', "viewFor");
+lazyRequire(this, './utils', "getTabs", "getSelectedTab");
+lazyRequire(this, '../window/utils', "getMostRecentBrowserWindow", "isBrowser");
+lazyRequire(this, './common', "Options");
+lazyRequire(this, '../private-browsing', "isPrivate");
+lazyRequire(this, '../private-browsing/utils', "ignoreWindow", "isWindowPBSupported")
 const { isPrivateBrowsingSupported } = require('sdk/self');
 
 const supportPrivateTabs = isPrivateBrowsingSupported && isWindowPBSupported;
 
 const Tabs = Class({
   implements: [EventTarget],
   extends: List,
   initialize: function() {
--- a/addon-sdk/source/lib/sdk/tabs/utils.js
+++ b/addon-sdk/source/lib/sdk/tabs/utils.js
@@ -7,19 +7,19 @@ module.metadata = {
   'stability': 'unstable'
 };
 
 
 // NOTE: This file should only deal with xul/native tabs
 
 
 const { Ci, Cu } = require('chrome');
-const { defer } = require("../lang/functional");
-const { windows, isBrowser } = require('../window/utils');
-const { isPrivateBrowsingSupported } = require('../self');
+lazyRequire(this, "../lang/functional", "defer");
+lazyRequire(this, '../window/utils', "windows", "isBrowser");
+lazyRequire(this, '../self', "isPrivateBrowsingSupported");
 const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
 
 // Bug 834961: ignore private windows when they are not supported
 function getWindows() {
   return windows(null, { includePrivate: isPrivateBrowsingSupported });
 }
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
--- a/addon-sdk/source/lib/sdk/test/harness.js
+++ b/addon-sdk/source/lib/sdk/test/harness.js
@@ -8,17 +8,17 @@ module.metadata = {
 };
 
 const { Cc, Ci, Cu } = require("chrome");
 const { Loader } = require('./loader');
 const { serializeStack, parseStack  } = require("toolkit/loader");
 const { setTimeout } = require('../timers');
 const { PlainTextConsole } = require("../console/plain-text");
 const { when: unload } = require("../system/unload");
-const { format, fromException }  = require("../console/traceback");
+lazyRequire(this, "../console/traceback", "format", "fromException");
 const system = require("../system");
 const { gc: gcPromise } = require('./memory');
 const { defer } = require('../core/promise');
 const { extend } = require('../core/heritage');
 
 // Trick manifest builder to make it think we need these modules ?
 const unit = require("../deprecated/unit-test");
 const test = require("../../test");
--- a/addon-sdk/source/lib/sdk/test/utils.js
+++ b/addon-sdk/source/lib/sdk/test/utils.js
@@ -2,23 +2,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 module.metadata = {
   'stability': 'unstable'
 };
 
-const { defer } = require('../core/promise');
-const { setInterval, clearInterval } = require('../timers');
-const { getTabs, closeTab } = require("../tabs/utils");
-const { windows: getWindows } = require("../window/utils");
-const { close: closeWindow } = require("../window/helpers");
-const { isGenerator } = require("../lang/type");
-const { env } = require("../system/environment");
+lazyRequire(this, '../core/promise', "defer");
+lazyRequire(this, '../timers', "setInterval", "clearInterval");
+lazyRequire(this, "../tabs/utils", "getTabs", "closeTab");
+lazyRequire(this, "../window/utils", {"windows": "getWindows"});
+lazyRequire(this, "../window/helpers", {"close": "closeWindow"});
+lazyRequire(this, "../lang/type", "isGenerator");
+lazyRequire(this, "../system/environment", "env");
 const { Task } = require("resource://gre/modules/Task.jsm");
 
 const getTestNames = (exports) =>
   Object.keys(exports).filter(name => /^test/.test(name));
 
 const isTestAsync = ({length}) => length > 1;
 const isHelperAsync = ({length}) => length > 2;
 
--- a/addon-sdk/source/lib/sdk/util/deprecate.js
+++ b/addon-sdk/source/lib/sdk/util/deprecate.js
@@ -3,18 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
-const { get, format } = require("../console/traceback");
-const { get: getPref } = require("../preferences/service");
+lazyRequire(this, "../console/traceback", "get", "format");
+lazyRequire(this, "../preferences/service", {"get": "getPref"});
 const PREFERENCE = "devtools.errorconsole.deprecation_warnings";
 
 function deprecateUsage(msg) {
   // Print caller stacktrace in order to help figuring out which code
   // does use deprecated thing
   let stack = get().slice(2);
 
   if (getPref(PREFERENCE)) 
--- a/addon-sdk/source/lib/sdk/util/match-pattern.js
+++ b/addon-sdk/source/lib/sdk/util/match-pattern.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
-const { URL } = require('../url');
+lazyRequire(this, '../url', "URL");
 const cache = {};
 
 function MatchPattern(pattern) {
   if (cache[pattern]) return cache[pattern];
 
   if (typeof pattern.test == "function") {
     // For compatibility with -moz-document rules, we require the RegExp's
     // global, ignoreCase, and multiline flags to be set to false.
--- a/addon-sdk/source/lib/sdk/util/object.js
+++ b/addon-sdk/source/lib/sdk/util/object.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
-const { flatten } = require('./array');
+lazyRequire(this, './array', "flatten");
 
 // Create a shortcut for Array.prototype.slice.call().
 const unbind = Function.call.bind(Function.bind, Function.call);
 const slice = unbind(Array.prototype.slice);
 
 class DefaultWeakMap extends WeakMap {
   constructor(createItem, items = undefined) {
     super(items);
--- a/addon-sdk/source/lib/sdk/util/rules.js
+++ b/addon-sdk/source/lib/sdk/util/rules.js
@@ -3,18 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
 const { Class } = require('../core/heritage');
-const { MatchPattern } = require('./match-pattern');
-const { emit } = require('../event/core');
+lazyRequire(this, './match-pattern', "MatchPattern");
+lazyRequire(this, '../event/core', "emit");
 const { EventTarget } = require('../event/target');
 const { List, addListItem, removeListItem } = require('./list');
 
 // Should deprecate usage of EventEmitter/compose
 const Rules = Class({
   implements: [
     EventTarget,
     List
--- a/addon-sdk/source/lib/sdk/util/sequence.js
+++ b/addon-sdk/source/lib/sdk/util/sequence.js
@@ -16,18 +16,18 @@ module.metadata = {
 // - `p` stands for "predicate" that is function which returns logical
 //   true or false and is intended to be side effect free.
 // - `x` / `y` single item of the sequence.
 // - `xs` / `ys` sequence of `x` / `y` items where `x` / `y` signifies
 //    type of the items in sequence, so sequence is not of the same item.
 // - `_` used for argument(s) or variable(s) who's values are ignored.
 
 const { complement, flip, identity } = require("../lang/functional");
-const { isArray, isArguments, isMap, isSet, isGenerator,
-        isString, isBoolean, isNumber } = require("../lang/type");
+lazyRequire(this, "../lang/type", "isArray", "isArguments", "isMap", "isSet",
+            "isGenerator", "isString", "isBoolean", "isNumber");
 
 const Sequence = function Sequence(iterator) {
   if (!isGenerator(iterator)) {
     throw TypeError("Expected generator argument");
   }
 
   this[Symbol.iterator] = iterator;
 };
--- a/addon-sdk/source/lib/sdk/window/utils.js
+++ b/addon-sdk/source/lib/sdk/window/utils.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 module.metadata = {
   'stability': 'unstable'
 };
 
 const { Cc, Ci } = require('chrome');
-const array = require('../util/array');
+lazyRequireModule(this, "../util/array", "array");
 const { defer } = require('sdk/core/promise');
 const { dispatcher } = require("../util/dispatcher");
 
 const windowWatcher = Cc['@mozilla.org/embedcomp/window-watcher;1'].
                        getService(Ci.nsIWindowWatcher);
 const appShellService = Cc['@mozilla.org/appshell/appShellService;1'].
                         getService(Ci.nsIAppShellService);
 const WM = Cc['@mozilla.org/appshell/window-mediator;1'].
--- a/addon-sdk/source/test/leak/leak-utils.js
+++ b/addon-sdk/source/test/leak/leak-utils.js
@@ -35,17 +35,18 @@ function gc() {
 // Execute the given test function and verify that we did not leak windows
 // in the process.  The test function must return a promise or be a generator.
 // If the promise is resolved, or generator completes, with an sdk loader
 // object then it will be unloaded after the memory measurements.
 exports.asyncWindowLeakTest = function*(assert, asyncTestFunc) {
 
   // SelfSupportBackend periodically tries to open windows.  This can
   // mess up our window leak detection below, so turn it off.
-  SelfSupportBackend.uninit();
+  if (SelfSupportBackend._log)
+    SelfSupportBackend.uninit();
 
   // Wait for the browser to finish loading.
   yield Startup.onceInitialized;
 
   // Track windows that are opened in an array of weak references.
   let weakWindows = [];
   function windowObserver(subject, topic) {
     let supportsWeak = subject.QueryInterface(Ci.nsISupportsWeakReference);