Merge fx-team to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Sun, 08 Jun 2014 13:15:44 -0400
changeset 207719 8bd92dc9ef59a915bb805ecf9fe244b8f3fc6e66
parent 207712 367d4c592c50ea2dd8b9acc4f0538e0d4d7553a5 (current diff)
parent 207718 17402b39be2440bd46c75fbb65c56a7ff4a2a5f3 (diff)
child 207725 84277c73fd7861829bfdb6d0ee5e5e25bae9bc35
push id494
push userraliiev@mozilla.com
push dateMon, 25 Aug 2014 18:42:16 +0000
treeherdermozilla-release@a3cc3e46b571 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge fx-team to m-c. a=merge
--- a/browser/components/preferences/in-content/advanced.xul
+++ b/browser/components/preferences/in-content/advanced.xul
@@ -193,22 +193,20 @@
                   label="&checkSpelling.label;"
                   accesskey="&checkSpelling.accesskey;"
                   onsyncfrompreference="return gAdvancedPane.readCheckSpelling();"
                   onsynctopreference="return gAdvancedPane.writeCheckSpelling();"
                   preference="layout.spellcheckDefault"/>
       </groupbox>
 #ifdef HAVE_SHELL_SERVICE
       <!-- System Defaults -->
-      <groupbox id="systemDefaultsGroup" orient="vertical">
+      <groupbox id="systemDefaultsGroup" orient="vertical" align="start">
         <caption><label>&systemDefaults.label;</label></caption>
-
         <checkbox id="alwaysCheckDefault" preference="browser.shell.checkDefaultBrowser"
-                  label="&alwaysCheckDefault.label;" accesskey="&alwaysCheckDefault.accesskey;"
-                  flex="1"/>
+                  label="&alwaysCheckDefault.label;" accesskey="&alwaysCheckDefault.accesskey;"/>
         <hbox class="indent">
           <deck id="setDefaultPane">
             <button id="setDefaultButton"
                     label="&setDefault.label;" accesskey="&setDefault.accesskey;"
                     oncommand="gAdvancedPane.setDefaultBrowser();"
                     preference="pref.general.disable_button.default_browser"/>
             <label id="isDefaultLabel">&isDefault.label;</label>
           </deck>
@@ -288,21 +286,23 @@
         <caption><label>&httpCache.label;</label></caption>
 
         <hbox align="center">
           <label id="actualDiskCacheSize" flex="1"/>
           <button id="clearCacheButton" icon="clear"
                   label="&clearCacheNow.label;" accesskey="&clearCacheNow.accesskey;"
                   oncommand="gAdvancedPane.clearCache();"/>
         </hbox>
-        <checkbox preference="browser.cache.disk.smart_size.enabled"
-                  id="allowSmartSize" flex="1"
-                  onsyncfrompreference="return gAdvancedPane.readSmartSizeEnabled();"
-                  label="&overrideSmartCacheSize.label;"
-                  accesskey="&overrideSmartCacheSize.accesskey;"/>
+        <hbox>
+          <checkbox preference="browser.cache.disk.smart_size.enabled"
+                    id="allowSmartSize"
+                    onsyncfrompreference="return gAdvancedPane.readSmartSizeEnabled();"
+                    label="&overrideSmartCacheSize.label;"
+                    accesskey="&overrideSmartCacheSize.accesskey;"/>
+        </hbox>
         <hbox align="center" class="indent">
           <label id="useCacheBefore" control="cacheSize"
                  accesskey="&limitCacheSizeBefore.accesskey;">
             &limitCacheSizeBefore.label;
           </label>
           <textbox id="cacheSize" type="number" size="4" max="1024"
                   preference="browser.cache.disk.capacity"
                   onsyncfrompreference="return gAdvancedPane.readCacheSize();"
@@ -318,20 +318,21 @@
 
         <hbox align="center">
           <label id="actualAppCacheSize" flex="1"/>
           <button id="clearOfflineAppCacheButton" icon="clear"
                   label="&clearOfflineAppCacheNow.label;" accesskey="&clearOfflineAppCacheNow.accesskey;"
                   oncommand="gAdvancedPane.clearOfflineAppCache();"/>
         </hbox>
         <hbox align="center">
-          <checkbox id="offlineNotify" flex="1"
+          <checkbox id="offlineNotify"
                     label="&offlineNotify.label;" accesskey="&offlineNotify.accesskey;"
                     preference="browser.offline-apps.notify"
                     onsyncfrompreference="return gAdvancedPane.readOfflineNotify();"/>
+          <spacer flex="1"/>
           <button id="offlineNotifyExceptions"
                   label="&offlineNotifyExceptions.label;"
                   accesskey="&offlineNotifyExceptions.accesskey;"
                   oncommand="gAdvancedPane.showOfflineExceptions();"/>
         </hbox>
         <hbox>
           <vbox flex="1">
             <label id="offlineAppsListLabel">&offlineAppsList2.label;</label>
@@ -351,17 +352,17 @@
           </vbox>
         </hbox>
       </groupbox>
     </tabpanel>
 
     <!-- Update -->
     <tabpanel id="updatePanel" orient="vertical">
 #ifdef MOZ_UPDATER
-      <groupbox id="updateApp">
+      <groupbox id="updateApp" align="start">
         <caption><label>&updateApp.label;</label></caption>
         <radiogroup id="updateRadioGroup"
                     oncommand="gAdvancedPane.updateWritePrefs();">
 #ifdef XP_WIN
 #ifdef MOZ_METRO
               <radio id="autoMetro"
                      value="autoMetro"
                      label="&updateAutoMetro.label;"
@@ -403,17 +404,17 @@
 #ifdef MOZ_MAINTENANCE_SERVICE
         <checkbox id="useService"
                   label="&useService.label;"
                   accesskey="&useService.accesskey;"
                   preference="app.update.service.enabled"/>
 #endif
       </groupbox>
 #endif
-      <groupbox id="updateOthers">
+      <groupbox id="updateOthers" align="start">
         <caption><label>&updateOthers.label;</label></caption>
         <checkbox id="enableSearchUpdate"
                   label="&enableSearchUpdate.label;"
                   accesskey="&enableSearchUpdate.accesskey;"
                   preference="browser.search.update"/>
       </groupbox>
     </tabpanel>
 
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -183,26 +183,28 @@
               label="&chooseFolderMac.label;"
 #else
               accesskey="&chooseFolderWin.accesskey;"
               label="&chooseFolderWin.label;"
 #endif
               preference="browser.download.folderList"
               onsynctopreference="return gMainPane.getFolderListPref();"/>
     </hbox>
-    <radio id="alwaysAsk"
-           value="false"
-           label="&alwaysAsk.label;"
-           accesskey="&alwaysAsk.accesskey;"/>
+    <hbox>
+      <radio id="alwaysAsk"
+             value="false"
+             label="&alwaysAsk.label;"
+             accesskey="&alwaysAsk.accesskey;"/>
+    </hbox>
   </radiogroup>
 </groupbox>
 
 <!-- Tab preferences -->
 <groupbox data-category="paneGeneral"
-          hidden="true">
+          hidden="true" align="start">
     <caption><label>&tabsGroup.label;</label></caption>
     <checkbox id="linkTargeting" label="&newWindowsAsTabs.label;"
               accesskey="&newWindowsAsTabs.accesskey;"
               preference="browser.link.open_newwindow"
               onsyncfrompreference="return gMainPane.readLinkTarget();"
               onsynctopreference="return gMainPane.writeLinkTarget();"/>
 
     <checkbox id="warnCloseMultiple" label="&warnCloseMultipleTabs.label;"
--- a/browser/components/preferences/in-content/security.xul
+++ b/browser/components/preferences/in-content/security.xul
@@ -40,58 +40,63 @@
   <label class="header-name">&paneSecurity.title;</label>
 </hbox>
 
 <!-- addons, forgery (phishing) UI -->
 <groupbox id="addonsPhishingGroup" data-category="paneSecurity" hidden="true">
   <caption><label>&general.label;</label></caption>
 
   <hbox id="addonInstallBox">
-    <checkbox id="warnAddonInstall" flex="1"
+    <checkbox id="warnAddonInstall"
               label="&warnAddonInstall.label;"
               accesskey="&warnAddonInstall.accesskey;"
               preference="xpinstall.whitelist.required"
               onsyncfrompreference="return gSecurityPane.readWarnAddonInstall();"/>
+    <spacer flex="1"/>
     <button id="addonExceptions"
             label="&addonExceptions.label;"
             accesskey="&addonExceptions.accesskey;"
             oncommand="gSecurityPane.showAddonExceptions();"/>
   </hbox>
 
   <separator class="thin"/>
-  <checkbox id="blockAttackSites"
-            label="&blockAttackSites.label;"
-            accesskey="&blockAttackSites.accesskey;"
-            preference="browser.safebrowsing.malware.enabled" />
-  <checkbox id="blockWebForgeries"
-            label="&blockWebForgeries.label;"
-            accesskey="&blockWebForgeries.accesskey;"
-            preference="browser.safebrowsing.enabled" />
+  <vbox align="start">
+    <checkbox id="blockAttackSites"
+              label="&blockAttackSites.label;"
+              accesskey="&blockAttackSites.accesskey;"
+              preference="browser.safebrowsing.malware.enabled" />
+    <checkbox id="blockWebForgeries"
+              label="&blockWebForgeries.label;"
+              accesskey="&blockWebForgeries.accesskey;"
+              preference="browser.safebrowsing.enabled" />
+  </vbox>
 </groupbox>
 
 <!-- Passwords -->
 <groupbox id="passwordsGroup" orient="vertical" data-category="paneSecurity" hidden="true">
   <caption><label>&passwords.label;</label></caption>
 
   <hbox id="savePasswordsBox">
-    <checkbox id="savePasswords" flex="1"
+    <checkbox id="savePasswords"
               label="&rememberPasswords.label;" accesskey="&rememberPasswords.accesskey;"
               preference="signon.rememberSignons"
               onsyncfrompreference="return gSecurityPane.readSavePasswords();"/>
+    <spacer flex="1"/>
     <button id="passwordExceptions"
             label="&passwordExceptions.label;"
             accesskey="&passwordExceptions.accesskey;"
             oncommand="gSecurityPane.showPasswordExceptions();"
             preference="pref.privacy.disable_button.view_passwords_exceptions"/>
   </hbox>
   <hbox id="masterPasswordBox">
-    <checkbox id="useMasterPassword" flex="1"
+    <checkbox id="useMasterPassword"
               oncommand="gSecurityPane.updateMasterPasswordButton();"
               label="&useMasterPassword.label;"
               accesskey="&useMasterPassword.accesskey;"/>
+    <spacer flex="1"/>
     <button id="changeMasterPassword"
             label="&changeMasterPassword.label;"
             accesskey="&changeMasterPassword.accesskey;"
             oncommand="gSecurityPane.changeMasterPassword();"/>
   </hbox>
 
   <hbox id="showPasswordsBox">
     <spacer flex="1"/>
--- a/browser/components/preferences/in-content/sync.xul
+++ b/browser/components/preferences/in-content/sync.xul
@@ -190,17 +190,17 @@
 
   <!-- These panels are for the Firefox Accounts identity provider -->
   <vbox id="fxaDeterminingStatus" align="center">
     <spacer flex="1"/>
     <label>&determiningAcctStatus.label;</label>
     <spacer flex="1"/>
   </vbox>
 
-  <vbox id="noFxaAccount">
+  <vbox id="noFxaAccount" align="start">
     <label>&welcome.description;</label>
     <label class="text-link"
            onclick="gSyncPane.signUp(); return false;">
       &welcome.createAccount.label;
     </label>
     <label class="text-link"
            onclick="gSyncPane.signIn(); return false;">
       &welcome.signIn.label;
--- a/browser/devtools/framework/test/browser.ini
+++ b/browser/devtools/framework/test/browser.ini
@@ -22,16 +22,17 @@ support-files =
 [browser_toolbox_options_disable_js.js]
 # [browser_toolbox_raise.js] # Bug 962258
 # skip-if = os == "win"
 [browser_toolbox_ready.js]
 [browser_toolbox_select_event.js]
 [browser_toolbox_sidebar.js]
 [browser_toolbox_tabsswitch_shortcuts.js]
 [browser_toolbox_tool_ready.js]
+[browser_toolbox_window_reload_target.js]
 [browser_toolbox_window_shortcuts.js]
 [browser_toolbox_window_title_changes.js]
 [browser_toolbox_zoom.js]
 [browser_toolbox_custom_host.js]
 
 # We want this test to run for mochitest-dt as well, so we include it here:
 [../../../base/content/test/general/browser_parsable_css.js]
 
new file mode 100644
--- /dev/null
+++ b/browser/devtools/framework/test/browser_toolbox_window_reload_target.js
@@ -0,0 +1,101 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let Toolbox = devtools.Toolbox;
+
+let target, toolbox, description, reloadsSent, toolIDs;
+
+function test() {
+  waitForExplicitFinish();
+
+  addTab("data:text/html;charset=utf-8,"+
+         "<html><head><title>Test reload</title></head>"+
+         "<body><h1>Testing reload from devtools</h1></body></html>",
+         () => {
+    target = TargetFactory.forTab(gBrowser.selectedTab);
+
+    target.makeRemote().then(() => {
+      toolIDs = gDevTools.getToolDefinitionArray()
+                  .filter(def => def.isTargetSupported(target))
+                  .map(def => def.id);
+      gDevTools.showToolbox(target, toolIDs[0], Toolbox.HostType.BOTTOM)
+               .then(startReloadTest);
+    });
+  });
+}
+
+function startReloadTest(aToolbox) {
+  toolbox = aToolbox;
+
+  reloadsSent = 0;
+  let reloads = 0;
+  let reloadCounter = (event) => {
+    reloads++;
+    info("Detected reload #"+reloads);
+    is(reloads, reloadsSent, "Reloaded from devtools window once and only for "+description+"");
+  };
+  gBrowser.selectedBrowser.addEventListener('load', reloadCounter, true);
+
+  testAllTheTools("docked", () => {
+    let origHostType = toolbox.hostType;
+    toolbox.switchHost(Toolbox.HostType.WINDOW).then(() => {
+      toolbox.doc.defaultView.focus();
+      testAllTheTools("undocked", () => {
+        toolbox.switchHost(origHostType).then(() => {
+          gBrowser.selectedBrowser.removeEventListener('load', reloadCounter, true);
+          // If we finish too early, the inspector breaks promises:
+          toolbox.getPanel("inspector").once("new-root", finishUp);
+        });
+      });
+    });
+  }, toolIDs.length-1 /* only test 1 tool in docked mode, to cut down test time */);
+}
+
+function testAllTheTools(docked, callback, toolNum=0) {
+  if (toolNum >= toolIDs.length) {
+    return callback();
+  }
+  toolbox.selectTool(toolIDs[toolNum]).then(() => {
+    testReload("toolbox-reload-key", docked, toolIDs[toolNum], () => {
+      testReload("toolbox-reload-key2", docked, toolIDs[toolNum], () => {
+        testReload("toolbox-force-reload-key", docked, toolIDs[toolNum], () => {
+          testReload("toolbox-force-reload-key2", docked, toolIDs[toolNum], () => {
+            testAllTheTools(docked, callback, toolNum+1);
+          });
+        });
+      });
+    });
+  });
+}
+
+function synthesizeKeyForToolbox(keyId) {
+  let el = toolbox.doc.getElementById(keyId);
+  let key = el.getAttribute("key") || el.getAttribute("keycode");
+  let mod = {};
+  el.getAttribute("modifiers").split(" ").forEach((m) => mod[m+"Key"] = true);
+  info("Synthesizing: key="+key+", mod="+JSON.stringify(mod));
+  EventUtils.synthesizeKey(key, mod, toolbox.doc.defaultView);
+}
+
+function testReload(key, docked, toolID, callback) {
+  let complete = () => {
+    gBrowser.selectedBrowser.removeEventListener('load', complete, true);
+    return callback();
+  };
+  gBrowser.selectedBrowser.addEventListener('load', complete, true);
+
+  description = docked+" devtools with tool "+toolID+", key #" + key;
+  info("Testing reload in "+description);
+  synthesizeKeyForToolbox(key);
+  reloadsSent++;
+}
+
+function finishUp() {
+  toolbox.destroy().then(() => {
+    gBrowser.removeCurrentTab();
+
+    target = toolbox = description = reloadsSent = toolIDs = null;
+
+    finish();
+  });
+}
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -242,16 +242,17 @@ Toolbox.prototype = {
         let closeButton = this.doc.getElementById("toolbox-close");
         closeButton.addEventListener("command", this.destroy, true);
 
         this._buildDockButtons();
         this._buildOptions();
         this._buildTabs();
         this._buildButtons();
         this._addKeysToWindow();
+        this._addReloadKeys();
         this._addToolSwitchingKeys();
         this._addZoomKeys();
         this._loadInitialZoom();
 
         this._telemetry.toolOpened("toolbox");
 
         this.selectTool(this._defaultToolId).then(panel => {
           this.emit("ready");
@@ -290,16 +291,29 @@ Toolbox.prototype = {
 
   _splitConsoleOnKeypress: function(e) {
     let responsiveModeActive = this._isResponsiveModeActive();
     if (e.keyCode === e.DOM_VK_ESCAPE && !responsiveModeActive) {
       this.toggleSplitConsole();
     }
   },
 
+  _addReloadKeys: function() {
+    [
+      ["toolbox-reload-key", false],
+      ["toolbox-reload-key2", false],
+      ["toolbox-force-reload-key", true],
+      ["toolbox-force-reload-key2", true]
+    ].forEach(([id, force]) => {
+      this.doc.getElementById(id).addEventListener("command", (event) => {
+        this.reloadTarget(force);
+      }, true);
+    });
+  },
+
   _addToolSwitchingKeys: function() {
     let nextKey = this.doc.getElementById("toolbox-next-tool-key");
     nextKey.addEventListener("command", this.selectNextTool.bind(this), true);
     let prevKey = this.doc.getElementById("toolbox-previous-tool-key");
     prevKey.addEventListener("command", this.selectPreviousTool.bind(this), true);
 
     // Split console uses keypress instead of command so the event can be
     // cancelled with stopPropagation on the keypress, and not preventDefault.
@@ -910,16 +924,23 @@ Toolbox.prototype = {
         this.loadTool("webconsole").then(() => {
           this.focusConsoleInput();
         });
       }
     }
   },
 
   /**
+   * Tells the target tab to reload.
+   */
+  reloadTarget: function(force) {
+    this.target.activeTab.reload({ force: force });
+  },
+
+  /**
    * Loads the tool next to the currently selected tool.
    */
   selectNextTool: function() {
     let tools = this.doc.querySelectorAll(".devtools-tab");
     let selected = this.doc.querySelector(".devtools-tab[selected]");
     let nextIndex = [...tools].indexOf(selected) + 1;
     let next = tools[nextIndex] || tools[0];
     let tool = next.getAttribute("toolid");
--- a/browser/devtools/framework/toolbox.xul
+++ b/browser/devtools/framework/toolbox.xul
@@ -42,16 +42,32 @@
     <key id="toolbox-zoom-out-key"
          key="&toolboxZoomOut.key;"
          oncommand="void(0);"
          modifiers="accel"/>
     <key id="toolbox-zoom-reset-key"
          key="&toolboxZoomReset.key;"
          oncommand="void(0);"
          modifiers="accel"/>
+    <key id="toolbox-reload-key"
+         key="&toolboxReload.key;"
+         oncommand="void(0);"
+         modifiers="accel"/>
+    <key id="toolbox-force-reload-key"
+         key="&toolboxReload.key;"
+         oncommand="void(0);"
+         modifiers="accel shift"/>
+    <key id="toolbox-reload-key2"
+         keycode="VK_F5"
+         oncommand="void(0);"
+         modifiers=""/>
+    <key id="toolbox-force-reload-key2"
+         keycode="VK_F5"
+         oncommand="void(0);"
+         modifiers="accel"/>
   </keyset>
 
   <notificationbox id="toolbox-notificationbox" flex="1">
     <toolbar class="devtools-tabbar">
       <hbox id="toolbox-picker-container" />
       <hbox id="toolbox-tabs" flex="1" />
       <hbox id="toolbox-buttons" pack="end"/>
       <vbox id="toolbox-controls-separator"/>
--- a/browser/locales/en-US/chrome/browser/devtools/toolbox.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/toolbox.dtd
@@ -12,16 +12,18 @@
 <!ENTITY toolboxNextTool.key           "]">
 <!ENTITY toolboxPreviousTool.key       "[">
 
 <!ENTITY toolboxZoomIn.key             "+">
 <!ENTITY toolboxZoomIn.key2            "="> <!-- + is above this key on many keyboards -->
 <!ENTITY toolboxZoomOut.key            "-">
 <!ENTITY toolboxZoomReset.key          "0">
 
+<!ENTITY toolboxReload.key             "r">
+
 <!-- LOCALIZATION NOTE (options.context.advancedSettings): This is the label for
   -  the heading of the advanced settings group in the options panel. -->
 <!ENTITY options.context.advancedSettings "Advanced settings">
 
 <!-- LOCALIZATION NOTE (options.context.inspector): This is the label for
   -  the heading of the Inspector group in the options panel. -->
 <!ENTITY options.context.inspector "Inspector">
 
--- a/mobile/android/base/widget/ButtonToast.java
+++ b/mobile/android/base/widget/ButtonToast.java
@@ -144,16 +144,17 @@ public class ButtonToast {
             // Using Android's animation frameworks will not correctly turn off clicking.
             // See bug 885717.
             PropertyAnimator animator = new PropertyAnimator(duration);
             animator.attach(mView, PropertyAnimator.Property.ALPHA, 0.0f);
             animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener () {
                 // If we are showing a toast and go in the background
                 // onAnimationEnd will be called when the app is restored
                 public void onPropertyAnimationEnd() {
+                    mView.clearAnimation();
                     mView.setVisibility(View.GONE);
                 }
                 public void onPropertyAnimationStart() { }
             });
             animator.start();
         }
     }
 
--- a/toolkit/devtools/client/dbg-client.jsm
+++ b/toolkit/devtools/client/dbg-client.jsm
@@ -1256,19 +1256,27 @@ TabClient.prototype = {
       this.client._tabClients.delete(this.actor);
       return aResponse;
     },
     telemetry: "TABDETACH"
   }),
 
   /**
    * Reload the page in this tab.
+   *
+   * @param [optional] object options
+   *        An object with a `force` property indicating whether or not
+   *        this reload should skip the cache
    */
-  reload: DebuggerClient.requester({
-    type: "reload"
+  reload: function(options = { force: false }) {
+    return this._reload(options);
+  },
+  _reload: DebuggerClient.requester({
+    type: "reload",
+    options: args(0)
   }, {
     telemetry: "RELOAD"
   }),
 
   /**
    * Navigate to another URL.
    *
    * @param string url
--- a/toolkit/devtools/server/actors/webbrowser.js
+++ b/toolkit/devtools/server/actors/webbrowser.js
@@ -823,20 +823,22 @@ TabActor.prototype = {
 
     return { type: "detached" };
   },
 
   /**
    * Reload the page in this tab.
    */
   onReload: function(aRequest) {
+    let force = aRequest && aRequest.options && aRequest.options.force;
     // Wait a tick so that the response packet can be dispatched before the
     // subsequent navigation event packet.
     Services.tm.currentThread.dispatch(DevToolsUtils.makeInfallible(() => {
-      this.window.location.reload();
+      this.webNavigation.reload(force ? Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE
+                                      : Ci.nsIWebNavigation.LOAD_FLAGS_NONE);
     }, "TabActor.prototype.onReload's delayed body"), 0);
     return {};
   },
 
   /**
    * Navigate this tab to a new location
    */
   onNavigateTo: function(aRequest) {
--- a/toolkit/modules/AsyncShutdown.jsm
+++ b/toolkit/modules/AsyncShutdown.jsm
@@ -688,17 +688,17 @@ Barrier.prototype = Object.freeze({
           // that any information on the topic and state appears
           // within the first 200 characters of the message. This
           // helps automatically sort oranges.
           let msg = "AsyncShutdown timeout in " + topic +
             " Conditions: " + JSON.stringify(state) +
             " At least one completion condition failed to complete" +
 	    " within a reasonable amount of time. Causing a crash to" +
 	    " ensure that we do not leave the user with an unresponsive" +
-	    " process draining resources." +
+	    " process draining resources.";
 	  err(msg);
 	  if (gCrashReporter && gCrashReporter.enabled) {
             let data = {
               phase: topic,
               conditions: state
 	    };
             gCrashReporter.annotateCrashReport("AsyncShutdownTimeout",
               JSON.stringify(data));