Merge fx-team to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 23 Jul 2014 18:47:48 -0700
changeset 217390 a91ec42d6a9cd718673372a2c70bfca7903c3ca6
parent 217386 757608ac7f199f0ce26b7982d6ae1e922288a3c6 (current diff)
parent 217389 43aa05ddc80a3a5fbc71bda65e9506be8a194b21 (diff)
child 217391 11d24cee09f697ad1bf097a54f58200779b6109a
child 217437 f654e6bf1089820f000b61fcb5fc3099d17bffe9
child 217474 997591f5afb2df97901cae9c6d3fb07a417860ab
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone34.0a1
first release with
nightly linux32
a91ec42d6a9c / 34.0a1 / 20140724030201 / files
nightly linux64
a91ec42d6a9c / 34.0a1 / 20140724030201 / files
nightly mac
a91ec42d6a9c / 34.0a1 / 20140724030201 / files
nightly win32
a91ec42d6a9c / 34.0a1 / 20140724030201 / files
nightly win64
a91ec42d6a9c / 34.0a1 / 20140724030201 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge fx-team to m-c a=merge
--- a/browser/components/loop/MozLoopAPI.jsm
+++ b/browser/components/loop/MozLoopAPI.jsm
@@ -28,49 +28,46 @@ function injectLoopAPI(targetWindow) {
   let ringerStopper;
 
   let api = {
     /**
      * Sets and gets the "do not disturb" mode activation flag.
      */
     doNotDisturb: {
       enumerable: true,
-      configurable: true,
       get: function() {
         return MozLoopService.doNotDisturb;
       },
       set: function(aFlag) {
         MozLoopService.doNotDisturb = aFlag;
       }
     },
 
     /**
      * Returns the current locale of the browser.
      *
      * @returns {String} The locale string
      */
     locale: {
       enumerable: true,
-      configurable: true,
       get: function() {
         return MozLoopService.locale;
       }
     },
 
     /**
      * Returns translated strings associated with an element. Designed
      * for use with l10n.js
      *
      * @param {String} key The element id
      * @returns {Object} A JSON string containing the localized
      *                   attribute/value pairs for the element.
      */
     getStrings: {
       enumerable: true,
-      configurable: true,
       writable: true,
       value: function(key) {
         return MozLoopService.getStrings(key);
       }
     },
 
     /**
      * Call to ensure that any necessary registrations for the Loop Service
@@ -80,17 +77,16 @@ function injectLoopAPI(targetWindow) {
      * - err null on successful registration, non-null otherwise.
      *
      * @param {Function} callback Will be called once registration is complete,
      *                            or straight away if registration has already
      *                            happened.
      */
     ensureRegistered: {
       enumerable: true,
-      configurable: true,
       writable: true,
       value: function(callback) {
         // We translate from a promise to a callback, as we can't pass promises from
         // Promise.jsm across the priv versus unpriv boundary.
         return MozLoopService.register().then(() => {
           callback(null);
         }, err => {
           callback(err);
@@ -107,17 +103,16 @@ function injectLoopAPI(targetWindow) {
      * This is used to determine whether or not we should be registering with the
      * push server on start.
      *
      * @param {Integer} expiryTimeSeconds The seconds since epoch of the expiry time
      *                                    of the url.
      */
     noteCallUrlExpiry: {
       enumerable: true,
-      configurable: true,
       writable: true,
       value: function(expiryTimeSeconds) {
         MozLoopService.noteCallUrlExpiry(expiryTimeSeconds);
       }
     },
 
     /**
      * Set any character preference under "loop."
@@ -125,17 +120,16 @@ function injectLoopAPI(targetWindow) {
      * @param {String} prefName The name of the pref without the preceding "loop."
      * @param {String} stringValue The value to set.
      *
      * Any errors thrown by the Mozilla pref API are logged to the console
      * and cause false to be returned.
      */
     setLoopCharPref: {
       enumerable: true,
-      configurable: true,
       writable: true,
       value: function(prefName, value) {
         MozLoopService.setLoopCharPref(prefName, value);
       }
     },
 
     /**
      * Return any preference under "loop." that's coercible to a character
@@ -147,29 +141,27 @@ function injectLoopAPI(targetWindow) {
      * Any errors thrown by the Mozilla pref API are logged to the console
      * and cause null to be returned. This includes the case of the preference
      * not being found.
      *
      * @return {String} on success, null on error
      */
     getLoopCharPref: {
       enumerable: true,
-      configurable: true,
       writable: true,
       value: function(prefName) {
         return MozLoopService.getLoopCharPref(prefName);
       }
     },
 
     /**
      * Starts alerting the user about an incoming call
      */
     startAlerting: {
       enumerable: true,
-      configurable: true,
       writable: true,
       value: function() {
         let chromeWindow = getChromeWindow(targetWindow);
         chromeWindow.getAttention();
         ringer = new chromeWindow.Audio();
         ringer.src = Services.prefs.getCharPref("loop.ringtone");
         ringer.loop = true;
         ringer.load();
@@ -183,17 +175,16 @@ function injectLoopAPI(targetWindow) {
       }
     },
 
     /**
      * Stops alerting the user about an incoming call
      */
     stopAlerting: {
       enumerable: true,
-      configurable: true,
       writable: true,
       value: function() {
         if (ringerStopper) {
           targetWindow.document.removeEventListener("visibilitychange",
                                                     ringerStopper);
           ringerStopper = null;
         }
         if (ringer) {
@@ -219,31 +210,31 @@ function injectLoopAPI(targetWindow) {
      * @param {String} path The path to make the request to.
      * @param {String} method The request method, e.g. 'POST', 'GET'.
      * @param {Object} payloadObj An object which is converted to JSON and
      *                            transmitted with the request.
      * @param {Function} callback Called when the request completes.
      */
     hawkRequest: {
       enumerable: true,
-      configurable: true,
       writable: true,
       value: function(path, method, payloadObj, callback) {
         // XXX Should really return a DOM promise here.
         return MozLoopService.hawkRequest(path, method, payloadObj).then((response) => {
           callback(null, response.body);
         }, (error) => {
           callback(Cu.cloneInto(error, targetWindow));
         });
       }
     },
   };
 
   let contentObj = Cu.createObjectIn(targetWindow);
   Object.defineProperties(contentObj, api);
+  Object.seal(contentObj);
   Cu.makeObjectPropsNormal(contentObj);
 
   targetWindow.navigator.wrappedJSObject.__defineGetter__("mozLoop", function() {
     // We do this in a getter, so that we create these objects
     // only on demand (this is a potential concern, since
     // otherwise we might add one per iframe, and keep them
     // alive for as long as the window is alive).
     delete targetWindow.navigator.wrappedJSObject.mozLoop;
--- a/browser/components/loop/MozLoopService.jsm
+++ b/browser/components/loop/MozLoopService.jsm
@@ -42,31 +42,36 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
                                   "resource:///modules/loop/MozLoopPushHandler.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
+// The current deferred for the registration process. This is set if in progress
+// or the registration was successful. This is null if a registration attempt was
+// unsuccessful.
+let gRegisteredDeferred = null;
+let gPushHandler = null;
+let gHawkClient = null;
+let gRegisteredLoopServer = false;
+let gLocalizedStrings =  null;
+let gInitializeTimer = null;
+
 /**
  * Internal helper methods and state
  *
  * The registration is a two-part process. First we need to connect to
  * and register with the push server. Then we need to take the result of that
  * and register with the Loop server.
  */
 let MozLoopServiceInternal = {
   // The uri of the Loop server.
-  loopServerUri: Services.prefs.getCharPref("loop.server"),
-
-  // The current deferred for the registration process. This is set if in progress
-  // or the registration was successful. This is null if a registration attempt was
-  // unsuccessful.
-  _registeredDeferred: null,
+  get loopServerUri() Services.prefs.getCharPref("loop.server"),
 
   /**
    * The initial delay for push registration. This ensures we don't start
    * kicking off straight after browser startup, just a few seconds later.
    */
   get initialRegistrationDelayMilliseconds() {
     try {
       // Let a pref override this for developer & testing use.
@@ -132,28 +137,28 @@ let MozLoopServiceInternal = {
    * with the Loop server. It will return early if already registered.
    *
    * @param {Object} mockPushHandler Optional, test-only mock push handler. Used
    *                                 to allow mocking of the MozLoopPushHandler.
    * @returns {Promise} a promise that is resolved with no params on completion, or
    *          rejected with an error code or string.
    */
   promiseRegisteredWithServers: function(mockPushHandler) {
-    if (this._registeredDeferred) {
-      return this._registeredDeferred.promise;
+    if (gRegisteredDeferred) {
+      return gRegisteredDeferred.promise;
     }
 
-    this._registeredDeferred = Promise.defer();
+    gRegisteredDeferred = Promise.defer();
     // We grab the promise early in case .initialize or its results sets
     // it back to null on error.
-    let result = this._registeredDeferred.promise;
+    let result = gRegisteredDeferred.promise;
 
-    this._pushHandler = mockPushHandler || MozLoopPushHandler;
+    gPushHandler = mockPushHandler || MozLoopPushHandler;
 
-    this._pushHandler.initialize(this.onPushRegistered.bind(this),
+    gPushHandler.initialize(this.onPushRegistered.bind(this),
       this.onHandleNotification.bind(this));
 
     return result;
   },
 
   /**
    * Performs a hawk based request to the loop server.
    *
@@ -163,35 +168,35 @@ let MozLoopServiceInternal = {
    *                            transmitted with the request.
    * @returns {Promise}
    *        Returns a promise that resolves to the response of the API call,
    *        or is rejected with an error.  If the server response can be parsed
    *        as JSON and contains an 'error' property, the promise will be
    *        rejected with this JSON-parsed response.
    */
   hawkRequest: function(path, method, payloadObj) {
-    if (!this._hawkClient) {
-      this._hawkClient = new HawkClient(this.loopServerUri);
+    if (!gHawkClient) {
+      gHawkClient = new HawkClient(this.loopServerUri);
     }
 
     let sessionToken;
     try {
       sessionToken = Services.prefs.getCharPref("loop.hawk-session-token");
     } catch (x) {
       // It is ok for this not to exist, we'll default to sending no-creds
     }
 
     let credentials;
     if (sessionToken) {
       // true = use a hex key, as required by the server (see bug 1032738).
       credentials = deriveHawkCredentials(sessionToken, "sessionToken",
                                           2 * 32, true);
     }
 
-    return this._hawkClient.request(path, method, credentials, payloadObj);
+    return gHawkClient.request(path, method, credentials, payloadObj);
   },
 
   /**
    * Used to store a session token from a request if it exists in the headers.
    *
    * @param {Object} headers The request headers, which may include a
    *                         "hawk-session-token" to be saved.
    * @return true on success or no token, false on failure.
@@ -200,57 +205,56 @@ let MozLoopServiceInternal = {
     let sessionToken = headers["hawk-session-token"];
     if (sessionToken) {
       // XXX should do more validation here
       if (sessionToken.length === 64) {
         Services.prefs.setCharPref("loop.hawk-session-token", sessionToken);
       } else {
         // XXX Bubble the precise details up to the UI somehow (bug 1013248).
         console.warn("Loop server sent an invalid session token");
-        this._registeredDeferred.reject("session-token-wrong-size");
-        this._registeredDeferred = null;
+        gRegisteredDeferred.reject("session-token-wrong-size");
+        gRegisteredDeferred = null;
         return false;
       }
     }
     return true;
   },
 
   /**
    * Callback from MozLoopPushHandler - The push server has been registered
    * and has given us a push url.
    *
    * @param {String} pushUrl The push url given by the push server.
    */
   onPushRegistered: function(err, pushUrl) {
     if (err) {
-      this._registeredDeferred.reject(err);
-      this._registeredDeferred = null;
+      gRegisteredDeferred.reject(err);
+      gRegisteredDeferred = null;
       return;
     }
 
     this.registerWithLoopServer(pushUrl);
   },
 
   /**
    * Registers with the Loop server.
    *
    * @param {String} pushUrl The push url given by the push server.
    * @param {Boolean} noRetry Optional, don't retry if authentication fails.
    */
   registerWithLoopServer: function(pushUrl, noRetry) {
     this.hawkRequest("/registration", "POST", { simple_push_url: pushUrl})
       .then((response) => {
         // If this failed we got an invalid token. storeSessionToken rejects
-        // the _registeredDeferred promise for us, so here we just need to
+        // the gRegisteredDeferred promise for us, so here we just need to
         // early return.
         if (!this.storeSessionToken(response.headers))
           return;
 
-        this.registeredLoopServer = true;
-        this._registeredDeferred.resolve();
+        gRegisteredDeferred.resolve();
         // No need to clear the promise here, everything was good, so we don't need
         // to re-register.
       }, (error) => {
         // There's other errors than invalid auth token, but we should only do the reset
         // as a last resort.
         if (error.code === 401 && error.errno === INVALID_AUTH_TOKEN) {
           if (this.urlExpiryTimeIsInFuture()) {
             // XXX Should this be reported to the user is a visible manner?
@@ -261,18 +265,18 @@ let MozLoopServiceInternal = {
           // Authorization failed, invalid token, we need to try again with a new token.
           Services.prefs.clearUserPref("loop.hawk-session-token");
           this.registerWithLoopServer(pushUrl, true);
           return;
         }
 
         // XXX Bubble the precise details up to the UI somehow (bug 1013248).
         Cu.reportError("Failed to register with the loop server. error: " + error);
-        this._registeredDeferred.reject(error.errno);
-        this._registeredDeferred = null;
+        gRegisteredDeferred.reject(error.errno);
+        gRegisteredDeferred = null;
       }
     );
   },
 
   /**
    * Callback from MozLoopPushHandler - A push notification has been received from
    * the server.
    *
@@ -288,18 +292,18 @@ let MozLoopServiceInternal = {
 
   /**
    * A getter to obtain and store the strings for loop. This is structured
    * for use by l10n.js.
    *
    * @returns {Object} a map of element ids with attributes to set.
    */
   get localizedStrings() {
-    if (this._localizedStrings)
-      return this._localizedStrings;
+    if (gLocalizedStrings)
+      return gLocalizedStrings;
 
     var stringBundle =
       Services.strings.createBundle('chrome://browser/locale/loop/loop.properties');
 
     var map = {};
     var enumerator = stringBundle.getSimpleEnumeration();
     while (enumerator.hasMoreElements()) {
       var string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
@@ -311,17 +315,17 @@ let MozLoopServiceInternal = {
         property = key.substring(i + 1);
         key = key.substring(0, i);
       }
       if (!(key in map))
         map[key] = {};
       map[key][property] = string.value;
     }
 
-    return this._localizedStrings = map;
+    return gLocalizedStrings = map;
   },
 
   /**
    * Saves loop logs to the saved-telemetry-pings folder.
    *
    * @param {Object} pc The peerConnection in question.
    */
   stageForTelemetryUpload: function(window, pc) {
@@ -440,54 +444,55 @@ let MozLoopServiceInternal = {
         let pc_static = new window.mozRTCPeerConnectionStatic();
         pc_static.registerPeerConnectionLifecycleCallback(onPCLifecycleChange);
       }.bind(this), true);
     };
 
     Chat.open(contentWindow, origin, title, url, undefined, undefined, callback);
   }
 };
+Object.freeze(MozLoopServiceInternal);
+
+let gInitializeTimerFunc = () => {
+  // Kick off the push notification service into registering after a timeout
+  // this ensures we're not doing too much straight after the browser's finished
+  // starting up.
+  gInitializeTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+  gInitializeTimer.initWithCallback(() => {
+    MozLoopService.register();
+    gInitializeTimer = null;
+  },
+  MozLoopServiceInternal.initialRegistrationDelayMilliseconds, Ci.nsITimer.TYPE_ONE_SHOT);
+};
 
 /**
  * Public API
  */
 this.MozLoopService = {
+  set initializeTimerFunc(value) {
+    gInitializeTimerFunc = value;
+  },
+
   /**
    * Initialized the loop service, and starts registration with the
    * push and loop servers.
    */
   initialize: function() {
     // Don't do anything if loop is not enabled.
     if (!Services.prefs.getBoolPref("loop.enabled")) {
       return;
     }
 
     // If expiresTime is in the future then kick-off registration.
     if (MozLoopServiceInternal.urlExpiryTimeIsInFuture()) {
-      this._startInitializeTimer();
+      gInitializeTimerFunc();
     }
   },
 
   /**
-   * Internal function, exposed for testing purposes only. Used to start the
-   * initialize timer.
-   */
-  _startInitializeTimer: function() {
-    // Kick off the push notification service into registering after a timeout
-    // this ensures we're not doing too much straight after the browser's finished
-    // starting up.
-    this._initializeTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-    this._initializeTimer.initWithCallback(function() {
-      this.register();
-      this._initializeTimer = null;
-    }.bind(this),
-    MozLoopServiceInternal.initialRegistrationDelayMilliseconds, Ci.nsITimer.TYPE_ONE_SHOT);
-  },
-
-  /**
    * Starts registration of Loop with the push server, and then will register
    * with the Loop server. It will return early if already registered.
    *
    * @param {Object} mockPushHandler Optional, test-only mock push handler. Used
    *                                 to allow mocking of the MozLoopPushHandler.
    * @returns {Promise} a promise that is resolved with no params on completion, or
    *          rejected with an error code or string.
    */
@@ -618,8 +623,9 @@ this.MozLoopService = {
    *        or is rejected with an error.  If the server response can be parsed
    *        as JSON and contains an 'error' property, the promise will be
    *        rejected with this JSON-parsed response.
    */
   hawkRequest: function(path, method, payloadObj) {
     return MozLoopServiceInternal.hawkRequest(path, method, payloadObj);
   },
 };
+Object.freeze(this.MozLoopService);
--- a/browser/components/loop/test/xpcshell/test_loopservice_initialize.js
+++ b/browser/components/loop/test/xpcshell/test_loopservice_initialize.js
@@ -47,14 +47,14 @@ add_task(function test_initialize_starts
   Assert.equal(startTimerCalled, true,
     "should start the timer when expiry time is in the future");
 });
 
 function run_test()
 {
   // Override MozLoopService's initializeTimer, so that we can verify the timeout is called
   // correctly.
-  MozLoopService._startInitializeTimer = function() {
+  MozLoopService.initializeTimerFunc = function() {
     startTimerCalled = true;
   };
 
   run_next_test();
 }
--- a/browser/components/preferences/in-content/subdialogs.js
+++ b/browser/components/preferences/in-content/subdialogs.js
@@ -3,23 +3,25 @@
    - You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 let gSubDialog = {
   _closingCallback: null,
   _frame: null,
   _overlay: null,
+  _box: null,
   _injectedStyleSheets: ["chrome://mozapps/content/preferences/preferences.css",
                          "chrome://browser/skin/preferences/preferences.css",
                          "chrome://browser/skin/preferences/in-content/preferences.css"],
 
   init: function() {
     this._frame = document.getElementById("dialogFrame");
     this._overlay = document.getElementById("dialogOverlay");
+    this._box = document.getElementById("dialogBox");
 
     // Make the close button work.
     let dialogClose = document.getElementById("dialogClose");
     dialogClose.addEventListener("command", this.close.bind(this));
 
     // DOMTitleChanged isn't fired on the frame, only on the chromeEventHandler
     let chromeBrowser = window.QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIWebNavigation)
@@ -59,16 +61,20 @@ let gSubDialog = {
   },
 
   open: function(aURL, aFeatures = null, aParams = null, aClosingCallback = null) {
     let features = aFeatures || "modal,centerscreen,resizable=no";
     let dialog = window.openDialog(aURL, "dialogFrame", features, aParams);
     if (aClosingCallback) {
       this._closingCallback = aClosingCallback.bind(dialog);
     }
+    let featureParams = new URLSearchParams(features.toLowerCase());
+    this._box.setAttribute("resizable", featureParams.has("resizable") &&
+                                        featureParams.get("resizable") != "no" &&
+                                        featureParams.get("resizable") != 0);
     return dialog;
   },
 
   close: function(aEvent = null) {
     if (this._closingCallback) {
       try {
         this._closingCallback.call(null, aEvent);
       } catch (ex) {
--- a/browser/themes/shared/incontentprefs/preferences.css
+++ b/browser/themes/shared/incontentprefs/preferences.css
@@ -864,16 +864,23 @@ description > html|a {
 #dialogBox {
   border: 1px solid #666;
   display: -moz-box;
   margin: 0;
   padding-right: 6px;
   padding-left: 6px;
 }
 
+#dialogBox[resizable="true"] {
+  resize: both;
+  overflow: hidden;
+  min-height: 30em;
+  min-width: 66ch;
+}
+
 #dialogTitle {
   -moz-margin-start: 5px !important;
 }
 
 .close-icon {
   background-color: transparent !important;
   border: none;
   box-shadow: none;
@@ -883,18 +890,19 @@ description > html|a {
 }
 
 #dialogBox > .groupbox-body {
   -moz-appearance: none;
   padding: 0;
 }
 
 #dialogFrame {
+  -moz-box-flex: 1;
   /* Default dialog dimensions */
-  height: 20em;
+  height: 30em;
   width: 66ch;
 }
 
 /* needs to be removed with bug 1035625 */
 :-moz-any(dialog, window, prefwindow) resizer {
   display: none;
 }
 
--- a/toolkit/devtools/client/dbg-client.jsm
+++ b/toolkit/devtools/client/dbg-client.jsm
@@ -61,16 +61,18 @@ this.makeInfallible = DevToolsUtils.make
 
 let LOG_PREF = "devtools.debugger.log";
 let VERBOSE_PREF = "devtools.debugger.log.verbose";
 let wantLogging = Services.prefs.getBoolPref(LOG_PREF);
 let wantVerbose =
   Services.prefs.getPrefType(VERBOSE_PREF) !== Services.prefs.PREF_INVALID &&
   Services.prefs.getBoolPref(VERBOSE_PREF);
 
+let noop = () => {};
+
 function dumpn(str) {
   if (wantLogging) {
     dump("DBG-CLIENT: " + str + "\n");
   }
 }
 
 function dumpv(msg) {
   if (wantVerbose) {
@@ -438,17 +440,17 @@ DebuggerClient.prototype = {
    * Attach to a tab actor.
    *
    * @param string aTabActor
    *        The actor ID for the tab to attach.
    * @param function aOnResponse
    *        Called with the response packet and a TabClient
    *        (which will be undefined on error).
    */
-  attachTab: function (aTabActor, aOnResponse) {
+  attachTab: function (aTabActor, aOnResponse = noop) {
     if (this._clients.has(aTabActor)) {
       let cachedTab = this._clients.get(aTabActor);
       let cachedResponse = {
         cacheDisabled: cachedTab.cacheDisabled,
         javascriptEnabled: cachedTab.javascriptEnabled,
         traits: cachedTab.traits,
       };
       setTimeout(() => aOnResponse(cachedResponse, cachedTab), 0);
@@ -473,17 +475,17 @@ DebuggerClient.prototype = {
    * Attach to an addon actor.
    *
    * @param string aAddonActor
    *        The actor ID for the addon to attach.
    * @param function aOnResponse
    *        Called with the response packet and a AddonClient
    *        (which will be undefined on error).
    */
-  attachAddon: function DC_attachAddon(aAddonActor, aOnResponse) {
+  attachAddon: function DC_attachAddon(aAddonActor, aOnResponse = noop) {
     let packet = {
       to: aAddonActor,
       type: "attach"
     };
     this.request(packet, aResponse => {
       let addonClient;
       if (!aResponse.error) {
         addonClient = new AddonClient(this, aAddonActor);
@@ -501,17 +503,17 @@ DebuggerClient.prototype = {
    *        The ID for the console actor to attach to.
    * @param array aListeners
    *        The console listeners you want to start.
    * @param function aOnResponse
    *        Called with the response packet and a WebConsoleClient
    *        instance (which will be undefined on error).
    */
   attachConsole:
-  function (aConsoleActor, aListeners, aOnResponse) {
+  function (aConsoleActor, aListeners, aOnResponse = noop) {
     let packet = {
       to: aConsoleActor,
       type: "startListeners",
       listeners: aListeners,
     };
 
     this.request(packet, (aResponse) => {
       let consoleClient;
@@ -534,17 +536,17 @@ DebuggerClient.prototype = {
    *        The actor ID for the thread to attach.
    * @param function aOnResponse
    *        Called with the response packet and a ThreadClient
    *        (which will be undefined on error).
    * @param object aOptions
    *        Configuration options.
    *        - useSourceMaps: whether to use source maps or not.
    */
-  attachThread: function (aThreadActor, aOnResponse, aOptions={}) {
+  attachThread: function (aThreadActor, aOnResponse = noop, aOptions={}) {
     if (this._clients.has(aThreadActor)) {
       setTimeout(() => aOnResponse({}, this._clients.get(aThreadActor)), 0);
       return;
     }
 
    let packet = {
       to: aThreadActor,
       type: "attach",
@@ -563,17 +565,17 @@ DebuggerClient.prototype = {
    * Attach to a trace actor.
    *
    * @param string aTraceActor
    *        The actor ID for the tracer to attach.
    * @param function aOnResponse
    *        Called with the response packet and a TraceClient
    *        (which will be undefined on error).
    */
-  attachTracer: function (aTraceActor, aOnResponse) {
+  attachTracer: function (aTraceActor, aOnResponse = noop) {
     if (this._clients.has(aTraceActor)) {
       setTimeout(() => aOnResponse({}, this._clients.get(aTraceActor)), 0);
       return;
     }
 
     let packet = {
       to: aTraceActor,
       type: "attach"
@@ -1272,17 +1274,17 @@ TabClient.prototype = {
    *
    * @param object aOptions
    *        Configuration options.
    *        - useSourceMaps: whether to use source maps or not.
    * @param function aOnResponse
    *        Called with the response packet and a ThreadClient
    *        (which will be undefined on error).
    */
-  attachThread: function(aOptions={}, aOnResponse) {
+  attachThread: function(aOptions={}, aOnResponse = noop) {
     if (this.thread) {
       setTimeout(() => aOnResponse({}, this.thread), 0);
       return;
     }
 
     let packet = {
       to: this._threadActor,
       type: "attach",
@@ -1620,17 +1622,17 @@ ThreadClient.prototype = {
    *
    * @param boolean aFlag
    *        Enables pausing if true, disables otherwise.
    * @param function aOnResponse
    *        Called with the response packet.
    */
   pauseOnExceptions: function (aPauseOnExceptions,
                                aIgnoreCaughtExceptions,
-                               aOnResponse) {
+                               aOnResponse = noop) {
     this._pauseOnExceptions = aPauseOnExceptions;
     this._ignoreCaughtExceptions = aIgnoreCaughtExceptions;
 
     // If the debuggee is paused, we have to send the flag via a reconfigure
     // request.
     if (this.paused) {
       this.reconfigure({
         pauseOnExceptions: aPauseOnExceptions,
@@ -1656,17 +1658,17 @@ ThreadClient.prototype = {
    *
    * @param array|string events
    *        An array of strings, representing the DOM event types to pause on,
    *        or "*" to pause on all DOM events. Pass an empty array to
    *        completely disable pausing on DOM events.
    * @param function onResponse
    *        Called with the response packet in a future turn of the event loop.
    */
-  pauseOnDOMEvents: function (events, onResponse) {
+  pauseOnDOMEvents: function (events, onResponse = noop) {
     this._pauseOnDOMEvents = events;
     // If the debuggee is paused, the value of the array will be communicated in
     // the next resumption. Otherwise we have to force a pause in order to send
     // the array.
     if (this.paused) {
       setTimeout(() => onResponse({}), 0);
       return;
     }
@@ -1734,17 +1736,18 @@ ThreadClient.prototype = {
   /**
    * Request to set a breakpoint in the specified location.
    *
    * @param object aLocation
    *        The source location object where the breakpoint will be set.
    * @param function aOnResponse
    *        Called with the thread's response.
    */
-  setBreakpoint: function ({ url, line, column, condition }, aOnResponse) {
+  setBreakpoint: function ({ url, line, column, condition },
+                           aOnResponse = noop) {
     // A helper function that sets the breakpoint.
     let doSetBreakpoint = (aCallback) => {
       const location = {
         url: url,
         line: line,
         column: column
       };
 
@@ -1762,19 +1765,17 @@ ThreadClient.prototype = {
           let root = this.client.mainRoot;
           bpClient = new BreakpointClient(
             this.client,
             aResponse.actor,
             location,
             root.traits.conditionalBreakpoints ? condition : undefined
           );
         }
-        if (aOnResponse) {
-          aOnResponse(aResponse, bpClient);
-        }
+        aOnResponse(aResponse, bpClient);
         if (aCallback) {
           aCallback();
         }
       });
     };
 
     // If the debuggee is paused, just set the breakpoint.
     if (this.paused) {