merge m-c to fx-team
authorTim Taubert <tim.taubert@gmx.de>
Tue, 22 May 2012 15:20:39 +0200
changeset 96821 5586efaf687dd0bca43499d315876d1d6eb352f9
parent 96813 ce618ce8d84a03a9f8db4f98ea28cceb69183bfc (current diff)
parent 96820 49cbdb1ea4e2a4d7f44bd381d15d1125e2ac49a2 (diff)
child 96822 edb5a8927daef77d674fd98d18c5382322666e92
child 96942 f789661fed75116697adc8aeb2ad83692ad13072
push id1439
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 20:19:22 +0000
treeherdermozilla-aurora@ea74834dccd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone15.0a1
merge m-c to fx-team
browser/app/profile/firefox.js
browser/devtools/commandline/gcliblank.xhtml
toolkit/components/telemetry/TelemetryPing.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1021,17 +1021,17 @@ pref("devtools.inspector.activeSidebar",
 pref("devtools.inspector.highlighterShowVeil", true);
 pref("devtools.inspector.highlighterShowInfobar", true);
 
 // Enable the Layout View
 pref("devtools.layoutview.enabled", false);
 pref("devtools.layoutview.open", false);
 
 // Enable the Debugger
-pref("devtools.debugger.enabled", false);
+pref("devtools.debugger.enabled", true);
 pref("devtools.debugger.remote-enabled", false);
 pref("devtools.debugger.remote-host", "localhost");
 pref("devtools.debugger.remote-port", 6000);
 pref("devtools.debugger.remote-autoconnect", false);
 pref("devtools.debugger.remote-connection-retries", 3);
 pref("devtools.debugger.remote-timeout", 3000);
 
 // The default Debugger UI height
--- a/browser/components/sessionstore/src/DocumentUtils.jsm
+++ b/browser/components/sessionstore/src/DocumentUtils.jsm
@@ -68,26 +68,26 @@ let DocumentUtils = {
             value = node.value;
             hasDefaultValue = value == node.defaultValue;
             break;
         }
       } else if (!node.multiple) {
         // <select>s without the multiple attribute are hard to determine the
         // default value, so assume we don't have the default.
         hasDefaultValue = false;
-        value = node.selectedIndex;
+        value = { selectedIndex: node.selectedIndex, value: node.value };
       } else {
         // <select>s with the multiple attribute are easier to determine the
         // default value since each <option> has a defaultSelected
         let options = Array.map(node.options, function(aOpt, aIx) {
           let oSelected = aOpt.selected;
           hasDefaultValue = hasDefaultValue && (oSelected == aOpt.defaultSelected);
-          return oSelected ? aIx : -1;
+          return oSelected ? aOpt.value : -1;
         });
-        value = options.filter(function(aIx) aIx >= 0);
+        value = options.filter(function(aIx) aIx !== -1);
       }
 
       // In order to reduce XPath generation (which is slow), we only save data
       // for form fields that have been changed. (cf. bug 537289)
       if (!hasDefaultValue) {
         if (nId) {
           ret.id[nId] = value;
         } else {
@@ -172,33 +172,51 @@ let DocumentUtils = {
       // Don't dispatch a change event for no change.
       if (aNode.checked == aValue) {
         return;
       }
       
       aNode.checked = aValue;
       eventType = "change";
     } else if (typeof aValue == "number") {
+      // handle select backwards compatibility, example { "#id" : index }
       // We saved the value blindly since selects take more work to determine
       // default values. So now we should check to avoid unnecessary events.
       if (aNode.selectedIndex == aValue) {
         return;
       }
       
-      try {
+      if (aValue < aNode.options.length) {
         aNode.selectedIndex = aValue;
         eventType = "change";
-      } catch (ex) { /* throws for invalid indices */ }
+      } 
+    } else if (aValue && aValue.selectedIndex >= 0 && aValue.value) {
+      // handle select new format
+
+      // Don't dispatch a change event for no change
+      if (aNode.options[aNode.selectedIndex].value == aValue.value) {
+        return;
+      }
+
+      // find first option with matching aValue if possible
+      for (let i = 0; i < aNode.options.length; i++) {
+        if (aNode.options[i].value == aValue.value) {
+          aNode.selectedIndex = i;
+          break;
+        }
+      }
+      eventType = "change";
     } else if (aValue && aValue.fileList && aValue.type == "file" &&
       aNode.type == "file") {
       aNode.mozSetFileNameArray(aValue.fileList, aValue.fileList.length);
       eventType = "input";
     } else if (aValue && typeof aValue.indexOf == "function" && aNode.options) {
       Array.forEach(aNode.options, function(opt, index) {
-        opt.selected = aValue.indexOf(index) > -1;
+        // don't worry about malformed options with same values
+        opt.selected = aValue.indexOf(opt.value) > -1;
         
         // Only fire the event here if this wasn't selected by default
         if (!opt.defaultSelected) {
           eventType = "change";
         }
       });
     }
 
--- a/browser/components/sessionstore/test/Makefile.in
+++ b/browser/components/sessionstore/test/Makefile.in
@@ -116,16 +116,18 @@ include $(topsrcdir)/config/rules.mk
 	browser_624727.js \
 	browser_625257.js \
 	browser_628270.js \
 	browser_635418.js \
 	browser_636279.js \
 	browser_644409-scratchpads.js \
 	browser_645428.js \
 	browser_659591.js \
+	browser_662743.js \
+	browser_662743_sample.html \
 	browser_662812.js \
 	browser_665702-state_session.js \
 	browser_682507.js \
 	browser_687710.js \
 	browser_687710_2.js \
 	browser_694378.js \
 	browser_701377.js \
 	browser_705597.js \
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_662743.js
@@ -0,0 +1,128 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This tests that session restore component does restore the right <select> option.
+// Session store should not rely only on previous user's selectedIndex, it should
+// check its value as well.
+
+function test() {
+  /** Tests selected options **/
+  waitForExplicitFinish();
+
+  let testTabCount = 0;
+  let formData = [
+  // default case
+    { },
+  // old format
+    { "#select_id" : 0 },
+    { "#select_id" : 2 },
+    // invalid index
+    { "#select_id" : 8 },
+    { "/xhtml:html/xhtml:body/xhtml:select" : 5},
+    { "/xhtml:html/xhtml:body/xhtml:select[@name='select_name']" : 6},
+
+  // new format
+    // index doesn't match value (testing an option in between (two))
+    { id:{ "select_id": {"selectedIndex":0,"value":"val2"} } },
+    // index doesn't match value (testing an invalid value)
+    { id:{ "select_id": {"selectedIndex":4,"value":"val8"} } },
+    // index doesn't match value (testing an invalid index)
+    { id:{ "select_id": {"selectedIndex":8,"value":"val5"} } },
+    // index and value match position zero
+    { id:{ "select_id": {"selectedIndex":0,"value":"val0"} }, xpath: {} },
+    // index doesn't match value (testing the last option (seven))
+    { id:{},"xpath":{ "/xhtml:html/xhtml:body/xhtml:select[@name='select_name']": {"selectedIndex":1,"value":"val7"} } },
+    // index and value match the default option "selectedIndex":3,"value":"val3"
+    { xpath: { "/xhtml:html/xhtml:body/xhtml:select[@name='select_name']" : {"selectedIndex":3,"value":"val3"} } },
+    // index matches default option however it doesn't match value
+    { id:{ "select_id": {"selectedIndex":3,"value":"val4"} } },
+
+  // combinations
+    { "#select_id" : 3, id:{ "select_id": {"selectedIndex":1,"value":"val1"} } },
+    { "#select_id" : 5, xpath: { "/xhtml:html/xhtml:body/xhtml:select[@name='select_name']" : {"selectedIndex":4,"value":"val4"} } },
+    { "/xhtml:html/xhtml:body/xhtml:select" : 5, id:{ "select_id": {"selectedIndex":1,"value":"val1"} }},
+    { "/xhtml:html/xhtml:body/xhtml:select[@name='select_name']" : 2, xpath: { "/xhtml:html/xhtml:body/xhtml:select[@name='select_name']" : {"selectedIndex":7,"value":"val7"} } }
+  ];
+
+  let expectedValues = [
+    [ "val3"], // default value
+    [ "val0"],
+    [ "val2"],
+    [ "val3"], // default value (invalid index)
+    [ "val5"],
+    [ "val6"],
+    [ "val2"],
+    [ "val3"], // default value (invalid value)
+    [ "val5"], // value is still valid (even it has an invalid index)
+    [ "val0"],
+    [ "val7"],
+    [ "val3"],
+    [ "val4"],
+    [ "val1"],
+    [ "val4"],
+    [ "val1"],
+    [ "val7"]
+  ];
+  let callback = function() {
+    testTabCount--;
+    if (testTabCount == 0) {
+      finish();
+    }
+  };
+
+  for (let i = 0; i < formData.length; i++) {
+    testTabCount++;
+    testTabRestoreData(formData[i], expectedValues[i], callback);
+  }
+}
+
+function testTabRestoreData(aFormData, aExpectedValues, aCallback) {
+  let testURL =
+    getRootDirectory(gTestPath) + "browser_662743_sample.html";
+  let tab = gBrowser.addTab(testURL);
+  let tabState = { entries: [{ url: testURL, formdata: aFormData}] };
+
+  tab.linkedBrowser.addEventListener("load", function(aEvent) {
+    tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
+    ss.setTabState(tab, JSON.stringify(tabState));
+
+    tab.addEventListener("SSTabRestored", function(aEvent) {
+      tab.removeEventListener("SSTabRestored", arguments.callee);
+      let doc = tab.linkedBrowser.contentDocument;
+      let select = doc.getElementById("select_id");
+      let value = select.options[select.selectedIndex].value;
+
+      // test select options values
+      is(value, aExpectedValues[0],
+        "Select Option by selectedIndex &/or value has been restored correctly");
+
+      // clean up
+      gBrowser.removeTab(tab);
+      aCallback();
+    });
+
+    tab.addEventListener("TabClose", function(aEvent) {
+      tab.removeEventListener("TabClose", arguments.callee);
+      let restoredTabState = JSON.parse(ss.getTabState(tab));
+      let restoredFormData = restoredTabState.entries[0].formdata;
+      let selectIdFormData = restoredFormData.id.select_id;
+      let value = restoredFormData.id.select_id.value;
+
+      // test format
+      ok("id" in restoredFormData && "xpath" in restoredFormData,
+        "FormData format is valid");
+      // validate that there are no old keys
+      is(Object.keys(restoredFormData).length, 2,
+        "FormData key length is valid");
+      // test format
+      ok("selectedIndex" in selectIdFormData && "value" in selectIdFormData,
+        "select format is valid");
+      // validate that there are no old keys
+      is(Object.keys(selectIdFormData).length, 2,
+        "select_id length is valid");
+       // test set collection values
+      is(value, aExpectedValues[0],
+        "Collection has been saved correctly");
+    });
+  }, true);
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_662743_sample.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<title>Test 662743</title>
+
+<!-- Select events -->
+<h3>Select options</h3>
+<select id="select_id" name="select_name">
+  <option value="val0">Zero</option>
+  <option value="val1">One</option>
+  <option value="val2">Two</option>
+  <option value="val3" selected="selected">Three</option>
+  <option value="val4">Four</option>
+  <option value="val5">Five</option>
+  <option value="val6">Six</option>
+  <option value="val7">Seven</option>
+</select>
\ No newline at end of file
--- a/browser/devtools/commandline/gcli.jsm
+++ b/browser/devtools/commandline/gcli.jsm
@@ -6196,17 +6196,17 @@ var eagerHelperSettingSpec = {
   type: {
     name: 'selection',
     lookup: [
       { name: 'never', value: Eagerness.NEVER },
       { name: 'sometimes', value: Eagerness.SOMETIMES },
       { name: 'always', value: Eagerness.ALWAYS },
     ]
   },
-  defaultValue: 1,
+  defaultValue: Eagerness.SOMETIMES,
   description: l10n.lookup('eagerHelperDesc'),
   ignoreTypeDifference: true
 };
 var eagerHelper;
 
 /**
  * Register (and unregister) the hide-intro setting
  */
@@ -6341,17 +6341,18 @@ FocusManager.prototype.removeMonitoredEl
 };
 
 /**
  * Monitor for new command executions
  */
 FocusManager.prototype.updatePosition = function(dimensions) {
   var ev = {
     tooltipVisible: this.isTooltipVisible,
-    outputVisible: this.isOutputVisible
+    outputVisible: this.isOutputVisible,
+    dimensions: dimensions
   };
   this.onVisibilityChange(ev);
 };
 
 /**
  * Monitor for new command executions
  */
 FocusManager.prototype._outputted = function(ev) {
new file mode 100644
--- /dev/null
+++ b/browser/devtools/commandline/gclioutput.xhtml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
+  [
+    <!ENTITY % webConsoleDTD SYSTEM "chrome://browser/locale/devtools/webConsole.dtd">
+    %webConsoleDTD;
+  ]
+>
+
+<!-- ***** BEGIN LICENSE BLOCK *****
+   - Version: MPL 1.1/GPL 2.0/LGPL 2.1
+   -
+   - The contents of this file are subject to the Mozilla Public License Version
+   - 1.1 (the "License"); you may not use this file except in compliance with
+   - the License. You may obtain a copy of the License at
+   - http://www.mozilla.org/MPL/
+   -
+   - Software distributed under the License is distributed on an "AS IS" basis,
+   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+   - for the specific language governing rights and limitations under the
+   - License.
+   -
+   - The Original Code is GCLI.
+   -
+   - The Initial Developer of the Original Code is
+   - Mozilla Foundation.
+   - Portions created by the Initial Developer are Copyright (C) 2012
+   - the Initial Developer. All Rights Reserved.
+   -
+   - Contributor(s):
+   -   Joe Walker <jwalker@mozilla.com>
+   -
+   - Alternatively, the contents of this file may be used under the terms of
+   - either the GNU General Public License Version 2 or later (the "GPL"), or
+   - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+   - in which case the provisions of the GPL or the LGPL are applicable instead
+   - of those above. If you wish to allow use of your version of this file only
+   - under the terms of either the GPL or the LGPL, and not to allow others to
+   - use your version of this file under the terms of the MPL, indicate your
+   - decision by deleting the provisions above and replace them with the notice
+   - and other provisions required by the LGPL or the GPL. If you do not delete
+   - the provisions above, a recipient may use your version of this file under
+   - the terms of any one of the MPL, the GPL or the LGPL.
+   -
+   - ***** END LICENSE BLOCK ***** -->
+
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+  <link rel="stylesheet" href="chrome://global/skin/global.css" type="text/css"/>
+  <link rel="stylesheet" href="chrome://browser/content/devtools/gcli.css" type="text/css"/>
+  <link rel="stylesheet" href="chrome://browser/skin/devtools/gcli.css" type="text/css"/>
+</head>
+<body class="gcli-body">
+<div id="gcli-output-root"></div>
+</body>
+</html>
rename from browser/devtools/commandline/gcliblank.xhtml
rename to browser/devtools/commandline/gclitooltip.xhtml
--- a/browser/devtools/commandline/gcliblank.xhtml
+++ b/browser/devtools/commandline/gclitooltip.xhtml
@@ -14,13 +14,13 @@
 
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
   <link rel="stylesheet" href="chrome://global/skin/global.css" type="text/css"/>
   <link rel="stylesheet" href="chrome://browser/content/devtools/gcli.css" type="text/css"/>
   <link rel="stylesheet" href="chrome://browser/skin/devtools/gcli.css" type="text/css"/>
 </head>
-<body id="gclichrome-body">
-<div>
-</div>
+<body class="gcli-body">
+<div id="gcli-tooltip-root"></div>
+<div id="gcli-tooltip-connector"></div>
 </body>
 </html>
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -528,53 +528,30 @@ StackFrames.prototype = {
       // Add nodes for every other variable in scope.
       variables = frame.environment.bindings.variables;
       for (let variable in variables) {
         let paramVar = localScope.addVar(variable);
         let paramVal = variables[variable].value;
         paramVar.setGrip(paramVal);
         this._addExpander(paramVar, paramVal);
       }
-
-      // If we already found 'arguments', we are done here.
-      if ("arguments" in frame.environment.bindings.variables) {
-        // Signal that variables have been fetched.
-        DebuggerController.dispatchEvent("Debugger:FetchedVariables");
-        return;
-      }
     }
 
-    // Sometimes in call frames with arguments we don't get 'arguments' in the
-    // environment (bug 746601) and we have to construct it manually. Note, that
-    // in this case arguments.callee will be absent, even in the cases where it
-    // shouldn't be.
-    if (frame.arguments && frame.arguments.length > 0) {
-      // Add "arguments".
-      let argsVar = localScope.addVar("arguments");
-      argsVar.setGrip({
-        type: "object",
-        class: "Arguments"
-      });
-      this._addExpander(argsVar, frame.arguments);
-
-      // Signal that variables have been fetched.
-      DebuggerController.dispatchEvent("Debugger:FetchedVariables");
-    }
-
+    // Signal that variables have been fetched.
+    DebuggerController.dispatchEvent("Debugger:FetchedVariables");
   },
 
   /**
    * Adds an 'onexpand' callback for a variable, lazily handling the addition of
    * new properties.
    */
   _addExpander: function SF__addExpander(aVar, aObject) {
-    // No need for expansion for null and undefined values, but we do need them
-    // for frame.arguments which is a regular array.
+    // No need for expansion for null and undefined values.
     if (!aVar || !aObject || typeof aObject !== "object" ||
-        (aObject.type !== "object" && !Array.isArray(aObject))) {
+        aObject.type !== "object") {
       return;
     }
 
     // Force the twisty to show up.
     aVar.forceShowArrow();
     aVar.onexpand = this._addVarProperties.bind(this, aVar, aObject);
   },
 
@@ -583,33 +560,16 @@ StackFrames.prototype = {
    * expanded.
    */
   _addVarProperties: function SF__addVarProperties(aVar, aObject) {
     // Retrieve the properties only once.
     if (aVar.fetched) {
       return;
     }
 
-    // For arrays we have to construct a grip-like object.
-    if (Array.isArray(aObject)) {
-      let properties = { length: { value: aObject.length } };
-      for (let i = 0, l = aObject.length; i < l; i++) {
-        properties[i] = { value: aObject[i] };
-      }
-      aVar.addProperties(properties);
-
-      // Expansion handlers must be set after the properties are added.
-      for (let i = 0, l = aObject.length; i < l; i++) {
-        this._addExpander(aVar[i], aObject[i]);
-      }
-
-      aVar.fetched = true;
-      return;
-    }
-
     let objClient = this.activeThread.pauseGrip(aObject);
     objClient.getPrototypeAndProperties(function SF_onProtoAndProps(aResponse) {
       // Add __proto__.
       if (aResponse.prototype.type !== "null") {
         let properties = { "__proto__ ": { value: aResponse.prototype } };
         aVar.addProperties(properties);
 
         // Expansion handlers must be set after the properties are added.
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-07.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-07.js
@@ -71,28 +71,27 @@ function testFrameParameters()
         "Should have the right property value for 'dArg'.");
 
       is(localNodes[5].querySelector(".info").textContent, "null",
         "Should have the right property value for 'eArg'.");
 
       is(localNodes[6].querySelector(".info").textContent, "undefined",
         "Should have the right property value for 'fArg'.");
 
-      // FIXME bug TODO: reenable
-      //is(localNodes[7].querySelector(".info").textContent, "1",
-      //  "Should have the right property value for 'a'.");
+      is(localNodes[7].querySelector(".info").textContent, "1",
+       "Should have the right property value for 'a'.");
+
+      is(localNodes[8].querySelector(".info").textContent, "[object Object]",
+       "Should have the right property value for 'b'.");
 
-      //is(localNodes[8].querySelector(".info").textContent, "[object Object]",
-      //  "Should have the right property value for 'b'.");
+      is(localNodes[9].querySelector(".info").textContent, "[object Object]",
+       "Should have the right property value for 'c'.");
 
-      //is(localNodes[9].querySelector(".info").textContent, "[object Object]",
-      //  "Should have the right property value for 'c'.");
-
-      //is(localNodes[10].querySelector(".info").textContent, "[object Arguments]",
-      //  "Should have the right property value for 'arguments'.");
+      is(localNodes[10].querySelector(".info").textContent, "[object Arguments]",
+        "Should have the right property value for 'arguments'.");
 
       resumeAndFinish();
     }}, 0);
   }, false);
 
   EventUtils.sendMouseEvent({ type: "click" },
     content.document.querySelector("button"),
     content.window);
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-08.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-08.js
@@ -51,27 +51,28 @@ function testFrameParameters()
         "Should have three frames.");
 
       is(localNodes.length, 11,
         "The localScope should contain all the created variable elements.");
 
       is(localNodes[0].querySelector(".info").textContent, "[object Proxy]",
         "Should have the right property value for 'this'.");
 
-      // Expand the '__proto__', 'arguments' and 'a' tree nodes. This causes
+      // Expand the 'this', 'arguments' and 'c' tree nodes. This causes
       // their properties to be retrieved and displayed.
       localNodes[0].expand();
       localNodes[9].expand();
       localNodes[10].expand();
 
       // Poll every few milliseconds until the properties are retrieved.
       // It's important to set the timer in the chrome window, because the
       // content window timers are disabled while the debuggee is paused.
       let count = 0;
       let intervalID = window.setInterval(function(){
+        dump("count: "+count+" ");
         if (++count > 50) {
           ok(false, "Timed out while polling for the properties.");
           resumeAndFinish();
         }
         if (!localNodes[0].fetched ||
             !localNodes[9].fetched ||
             !localNodes[10].fetched) {
           return;
@@ -91,27 +92,27 @@ function testFrameParameters()
         is(localNodes[9].querySelectorAll(".property > .title > .key")[1]
                         .textContent, "a",
           "Should have the right property name for 'a'.");
 
         is(localNodes[9].querySelectorAll(".property > .title > .value")[1]
                         .textContent, 1,
           "Should have the right value for 'c.a'.");
 
-        //is(localNodes[10].querySelector(".info").textContent,
-        //  "[object Arguments]",
-        //  "Should have the right property value for 'arguments'.");
+        is(localNodes[10].querySelector(".info").textContent,
+         "[object Arguments]",
+         "Should have the right property value for 'arguments'.");
 
-        //is(localNodes[10].querySelector(".property > .title > .key")
-        //                .textContent, "length",
-        //  "Should have the right property name for 'length'.");
+        is(localNodes[10].querySelectorAll(".property > .title > .key")[7]
+                       .textContent, "length",
+         "Should have the right property name for 'length'.");
 
-        //is(localNodes[10].querySelector(".property > .title > .value")
-        //                .textContent, 5,
-        //  "Should have the right argument length.");
+        is(localNodes[10].querySelectorAll(".property > .title > .value")[7]
+                       .textContent, 5,
+         "Should have the right argument length.");
 
         resumeAndFinish();
       }, 100);
     }}, 0);
   }, false);
 
   EventUtils.sendMouseEvent({ type: "click" },
     content.document.querySelector("button"),
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -18,9 +18,10 @@ browser.jar:
     content/browser/devtools/layoutview/view.css  (layoutview/view.css)
     content/browser/orion.js                      (sourceeditor/orion/orion.js)
 *   content/browser/source-editor-overlay.xul     (sourceeditor/source-editor-overlay.xul)
 *   content/browser/debugger.xul                  (debugger/debugger.xul)
     content/browser/debugger.css                  (debugger/debugger.css)
     content/browser/debugger-controller.js        (debugger/debugger-controller.js)
     content/browser/debugger-view.js              (debugger/debugger-view.js)
     content/browser/devtools/gcli.css             (commandline/gcli.css)
-    content/browser/devtools/gcliblank.xhtml      (commandline/gcliblank.xhtml)
+    content/browser/devtools/gclioutput.xhtml     (commandline/gclioutput.xhtml)
+    content/browser/devtools/gclitooltip.xhtml    (commandline/gclitooltip.xhtml)
--- a/browser/devtools/shared/DeveloperToolbar.jsm
+++ b/browser/devtools/shared/DeveloperToolbar.jsm
@@ -2,17 +2,16 @@
  * 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 EXPORTED_SYMBOLS = [ "DeveloperToolbar" ];
 
 const NS_XHTML = "http://www.w3.org/1999/xhtml";
-const URI_GCLIBLANK = "chrome://browser/content/devtools/gcliblank.xhtml";
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "gcli", function() {
   let obj = {};
   Components.utils.import("resource:///modules/devtools/gcli.jsm", obj);
   Components.utils.import("resource:///modules/devtools/GcliCommands.jsm", {});
@@ -74,17 +73,16 @@ Object.defineProperty(DeveloperToolbar.p
  * toggle the toolbar
  */
 DeveloperToolbar.prototype.toggle = function DT_toggle()
 {
   if (this.visible) {
     this.hide();
   } else {
     this.show();
-    this._input.focus();
   }
 };
 
 /**
  * Even if the user has not clicked on 'Got it' in the intro, we only show it
  * once per session.
  * Warning this is slightly messed up because this.DeveloperToolbar is not the
  * same as this.DeveloperToolbar when in browser.js context.
@@ -149,19 +147,21 @@ DeveloperToolbar.prototype._onload = fun
     scratchpad: null
   });
 
   this.display.onVisibilityChange.add(this.outputPanel._visibilityChanged, this.outputPanel);
   this.display.onVisibilityChange.add(this.tooltipPanel._visibilityChanged, this.tooltipPanel);
   this.display.onOutput.add(this.outputPanel._outputChanged, this.outputPanel);
 
   this._chromeWindow.getBrowser().tabContainer.addEventListener("TabSelect", this, false);
-  this._chromeWindow.getBrowser().addEventListener("load", this, true); 
+  this._chromeWindow.getBrowser().addEventListener("load", this, true);
+  this._chromeWindow.addEventListener("resize", this, false);
 
   this._element.hidden = false;
+  this._input.focus();
 
   this._notify(NOTIFICATIONS.SHOW);
   if (this._pendingShowCallback) {
     this._pendingShowCallback.call();
     this._pendingShowCallback = undefined;
   }
 
   // If a hide event happened while we were loading, then we need to hide.
@@ -258,111 +258,55 @@ DeveloperToolbar.prototype.handleEvent =
         chromeWindow: this._chromeWindow,
         environment: {
           chromeDocument: this._doc,
           contentDocument: contentDocument
         },
       });
     }
   }
+  else if (aEvent.type == "resize") {
+    this.outputPanel._resize();
+  }
 };
 
 /**
- * Add class="gcli-panel-inner-arrowcontent" to a panel's
- * |<xul:box class="panel-inner-arrowcontent">| so we can alter the styling
- * without complex CSS expressions.
- * @param aPanel The panel to affect
- */
-function getContentBox(aPanel)
-{
-  let container = aPanel.ownerDocument.getAnonymousElementByAttribute(
-          aPanel, "anonid", "container");
-  return container.querySelector(".panel-inner-arrowcontent");
-}
-
-/**
- * Helper function to calculate the sum of the vertical padding and margins
- * between a nested node |aNode| and an ancestor |aRoot|. Iff all of the
- * children of aRoot are 'only-childs' until you get to aNode then to avoid
- * scroll-bars, the 'correct' height of aRoot is verticalSpacing + aNode.height.
- * @param aNode The child node whose height is known.
- * @param aRoot The parent height whose height we can affect.
- * @return The sum of the vertical padding/margins in between aNode and aRoot.
- */
-function getVerticalSpacing(aNode, aRoot)
-{
-  let win = aNode.ownerDocument.defaultView;
-
-  function pxToNum(styles, property) {
-    return parseInt(styles.getPropertyValue(property).replace(/px$/, ''), 10);
-  }
-
-  let vertSpacing = 0;
-  do {
-    let styles = win.getComputedStyle(aNode);
-    vertSpacing += pxToNum(styles, "padding-top");
-    vertSpacing += pxToNum(styles, "padding-bottom");
-    vertSpacing += pxToNum(styles, "margin-top");
-    vertSpacing += pxToNum(styles, "margin-bottom");
-    vertSpacing += pxToNum(styles, "border-top-width");
-    vertSpacing += pxToNum(styles, "border-bottom-width");
-
-    let prev = aNode.previousSibling;
-    while (prev != null) {
-      vertSpacing += prev.clientHeight;
-      prev = prev.previousSibling;
-    }
-
-    let next = aNode.nextSibling;
-    while (next != null) {
-      vertSpacing += next.clientHeight;
-      next = next.nextSibling;
-    }
-
-    aNode = aNode.parentNode;
-  } while (aNode !== aRoot);
-
-  return vertSpacing + 9;
-}
-
-/**
  * Panel to handle command line output.
  * @param aChromeDoc document from which we can pull the parts we need.
  * @param aInput the input element that should get focus.
  * @param aLoadCallback called when the panel is loaded properly.
  */
 function OutputPanel(aChromeDoc, aInput, aLoadCallback)
 {
   this._input = aInput;
-  this._anchor = aChromeDoc.getElementById("developer-toolbar");
+  this._toolbar = aChromeDoc.getElementById("developer-toolbar");
 
   this._loadCallback = aLoadCallback;
 
   /*
   <panel id="gcli-output"
-         type="arrow"
          noautofocus="true"
          noautohide="true"
          class="gcli-panel">
-    <iframe id="gcli-output-frame"
-            src=URI_GCLIBLANK
-            flex="1"/>
+    <html:iframe xmlns:html="http://www.w3.org/1999/xhtml"
+                 id="gcli-output-frame"
+                 src="chrome://browser/content/devtools/gclioutput.xhtml"
+                 flex="1"/>
   </panel>
   */
   this._panel = aChromeDoc.createElement("panel");
   this._panel.id = "gcli-output";
   this._panel.classList.add("gcli-panel");
-  this._panel.setAttribute("type", "arrow");
   this._panel.setAttribute("noautofocus", "true");
   this._panel.setAttribute("noautohide", "true");
-  this._anchor.parentElement.insertBefore(this._panel, this._anchor);
+  this._toolbar.parentElement.insertBefore(this._panel, this._toolbar);
 
-  this._frame = aChromeDoc.createElement("iframe");
+  this._frame = aChromeDoc.createElementNS(NS_XHTML, "iframe");
   this._frame.id = "gcli-output-frame";
-  this._frame.setAttribute("src", URI_GCLIBLANK);
+  this._frame.setAttribute("src", "chrome://browser/content/devtools/gclioutput.xhtml");
   this._frame.setAttribute("flex", "1");
   this._panel.appendChild(this._frame);
 
   this.displayedOutput = undefined;
 
   this._onload = this._onload.bind(this);
   this._frame.addEventListener("load", this._onload, true);
 
@@ -372,57 +316,59 @@ function OutputPanel(aChromeDoc, aInput,
 /**
  * Wire up the element from the iframe, and inform the _loadCallback.
  */
 OutputPanel.prototype._onload = function OP_onload()
 {
   this._frame.removeEventListener("load", this._onload, true);
   delete this._onload;
 
-  this._content = getContentBox(this._panel);
-  this._content.classList.add("gcli-panel-inner-arrowcontent");
+  this.document = this._frame.contentDocument;
 
-  this.document = this._frame.contentDocument;
-  this.document.body.classList.add("gclichrome-output");
-
-  this._div = this.document.querySelector("div");
+  this._div = this.document.getElementById("gcli-output-root");
   this._div.classList.add('gcli-row-out');
   this._div.setAttribute('aria-live', 'assertive');
 
   this.loaded = true;
   if (this._loadCallback) {
     this._loadCallback();
     delete this._loadCallback;
   }
 };
 
 /**
  * Display the OutputPanel.
  */
 OutputPanel.prototype.show = function OP_show()
 {
+  // This is nasty, but displaying the panel causes it to re-flow, which can
+  // change the size it should be, so we need to resize the iframe after the
+  // panel has displayed
   this._panel.ownerDocument.defaultView.setTimeout(function() {
     this._resize();
   }.bind(this), 0);
 
+  this._panel.openPopup(this._input, "before_start", 0, 0, false, false, null);
   this._resize();
-  this._panel.openPopup(this._anchor, "before_end", -300, 0, false, false, null);
 
   this._input.focus();
 };
 
 /**
  * Internal helper to set the height of the output panel to fit the available
  * content;
  */
 OutputPanel.prototype._resize = function CLP_resize()
 {
-  let vertSpacing = getVerticalSpacing(this._content, this._panel);
-  let idealHeight = this.document.body.scrollHeight + vertSpacing;
-  this._panel.sizeTo(400, Math.min(idealHeight, 500));
+  if (this._panel == null || this.document == null || !this._panel.state == "closed") {
+    return
+  }
+
+  this._frame.height = this.document.body.scrollHeight;
+  this._frame.width = this._input.clientWidth + 2;
 };
 
 /**
  * Called by GCLI when a command is executed.
  */
 OutputPanel.prototype._outputChanged = function OP_outputChanged(aEvent)
 {
   if (aEvent.output.hidden) {
@@ -471,20 +417,20 @@ OutputPanel.prototype.remove = function 
 /**
  * Detach listeners from the currently displayed Output.
  */
 OutputPanel.prototype.destroy = function OP_destroy()
 {
   this.remove();
 
   this._panel.removeChild(this._frame);
-  this._anchor.parentElement.removeChild(this._panel);
+  this._toolbar.parentElement.removeChild(this._panel);
 
   delete this._input;
-  delete this._anchor;
+  delete this._toolbar;
   delete this._panel;
   delete this._frame;
   delete this._content;
   delete this._div;
   delete this.document;
 };
 
 /**
@@ -505,83 +451,122 @@ OutputPanel.prototype._visibilityChanged
  * Panel to handle tooltips.
  * @param aChromeDoc document from which we can pull the parts we need.
  * @param aInput the input element that should get focus.
  * @param aLoadCallback called when the panel is loaded properly.
  */
 function TooltipPanel(aChromeDoc, aInput, aLoadCallback)
 {
   this._input = aInput;
-  this._anchor = aChromeDoc.getElementById("developer-toolbar");
+  this._toolbar = aChromeDoc.getElementById("developer-toolbar");
+  this._dimensions = { start: 0, end: 0 };
 
   this._onload = this._onload.bind(this);
   this._loadCallback = aLoadCallback;
   /*
   <panel id="gcli-tooltip"
          type="arrow"
          noautofocus="true"
          noautohide="true"
          class="gcli-panel">
-    <iframe id="gcli-tooltip-frame"
-            src=URI_GCLIBLANK
-            flex="1"/>
+    <html:iframe xmlns:html="http://www.w3.org/1999/xhtml"
+                 id="gcli-tooltip-frame"
+                 src="chrome://browser/content/devtools/gclitooltip.xhtml"
+                 flex="1"/>
   </panel>
   */
   this._panel = aChromeDoc.createElement("panel");
   this._panel.id = "gcli-tooltip";
   this._panel.classList.add("gcli-panel");
-  this._panel.setAttribute("type", "arrow");
   this._panel.setAttribute("noautofocus", "true");
   this._panel.setAttribute("noautohide", "true");
-  this._anchor.parentElement.insertBefore(this._panel, this._anchor);
+  this._toolbar.parentElement.insertBefore(this._panel, this._toolbar);
 
-  this._frame = aChromeDoc.createElement("iframe");
+  this._frame = aChromeDoc.createElementNS(NS_XHTML, "iframe");
   this._frame.id = "gcli-tooltip-frame";
-  this._frame.setAttribute("src", URI_GCLIBLANK);
+  this._frame.setAttribute("src", "chrome://browser/content/devtools/gclitooltip.xhtml");
   this._frame.setAttribute("flex", "1");
   this._panel.appendChild(this._frame);
 
   this._frame.addEventListener("load", this._onload, true);
   this.loaded = false;
 }
 
 /**
  * Wire up the element from the iframe, and inform the _loadCallback.
  */
 TooltipPanel.prototype._onload = function TP_onload()
 {
   this._frame.removeEventListener("load", this._onload, true);
 
-  this._content = getContentBox(this._panel);
-  this._content.classList.add("gcli-panel-inner-arrowcontent");
-
   this.document = this._frame.contentDocument;
-  this.document.body.classList.add("gclichrome-tooltip");
-
-  this.hintElement = this.document.querySelector("div");
+  this.hintElement = this.document.getElementById("gcli-tooltip-root");
+  this._connector = this.document.getElementById("gcli-tooltip-connector");
 
   this.loaded = true;
 
   if (this._loadCallback) {
     this._loadCallback();
     delete this._loadCallback;
   }
 };
 
 /**
  * Display the TooltipPanel.
  */
-TooltipPanel.prototype.show = function TP_show()
+TooltipPanel.prototype.show = function TP_show(aDimensions)
 {
-  let vertSpacing = getVerticalSpacing(this._content, this._panel);
-  let idealHeight = this.document.body.scrollHeight + vertSpacing;
-  this._panel.sizeTo(350, Math.min(idealHeight, 500));
-  this._panel.openPopup(this._anchor, "before_start", 0, 0, false, false, null);
+  if (!aDimensions) {
+    aDimensions = { start: 0, end: 0 };
+  }
+  this._dimensions = aDimensions;
+
+  // This is nasty, but displaying the panel causes it to re-flow, which can
+  // change the size it should be, so we need to resize the iframe after the
+  // panel has displayed
+  this._panel.ownerDocument.defaultView.setTimeout(function() {
+    this._resize();
+  }.bind(this), 0);
+
+  this._resize();
+  this._panel.openPopup(this._input, "before_start", aDimensions.start * 10, 0, false, false, null);
+  this._input.focus();
+};
 
-  this._input.focus();
+/**
+ * One option is to spend lots of time taking an average width of characters
+ * in the current font, dynamically, and weighting for the frequency of use of
+ * various characters, or even to render the given string off screen, and then
+ * measure the width.
+ * Or we could do this...
+ */
+const AVE_CHAR_WIDTH = 4.5;
+
+/**
+ * Display the TooltipPanel.
+ */
+TooltipPanel.prototype._resize = function TP_resize()
+{
+  if (this._panel == null || this.document == null || !this._panel.state == "closed") {
+    return
+  }
+
+  let offset = 10 + Math.floor(this._dimensions.start * AVE_CHAR_WIDTH);
+  this._frame.style.marginLeft = offset + "px";
+
+  /*
+  // Bug 744906: UX review - Not sure if we want this code to fatten connector
+  // with param width
+  let width = Math.floor(this._dimensions.end * AVE_CHAR_WIDTH);
+  width = Math.min(width, 100);
+  width = Math.max(width, 10);
+  this._connector.style.width = width + "px";
+  */
+
+  this._frame.height = this.document.body.scrollHeight;
 };
 
 /**
  * Hide the TooltipPanel.
  */
 TooltipPanel.prototype.remove = function TP_remove()
 {
   this._panel.hidePopup();
@@ -590,32 +575,34 @@ TooltipPanel.prototype.remove = function
 /**
  * Hide the TooltipPanel.
  */
 TooltipPanel.prototype.destroy = function TP_destroy()
 {
   this.remove();
 
   this._panel.removeChild(this._frame);
-  this._anchor.parentElement.removeChild(this._panel);
+  this._toolbar.parentElement.removeChild(this._panel);
 
+  delete this._connector;
+  delete this._dimensions;
   delete this._input;
   delete this._onload;
   delete this._panel;
   delete this._frame;
-  delete this._anchor;
+  delete this._toolbar;
   delete this._content;
   delete this.document;
   delete this.hintElement;
 };
 
 /**
  * Called by GCLI to indicate that we should show or hide one either the
  * tooltip panel or the output panel.
  */
 TooltipPanel.prototype._visibilityChanged = function TP_visibilityChanged(aEvent)
 {
   if (aEvent.tooltipVisible === true) {
-    this.show();
+    this.show(aEvent.dimensions);
   } else {
     this._panel.hidePopup();
   }
 };
--- a/browser/devtools/shared/test/Makefile.in
+++ b/browser/devtools/shared/test/Makefile.in
@@ -13,16 +13,17 @@ include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_TEST_FILES = \
   browser_browser_basic.js \
   browser_promise_basic.js \
   browser_require_basic.js \
   browser_templater_basic.js \
   browser_toolbar_basic.js \
+  browser_toolbar_tooltip.js \
   head.js \
   $(NULL)
 
 _BROWSER_TEST_PAGES = \
   browser_templater_basic.html \
   browser_toolbar_basic.html \
   $(NULL)
 
--- a/browser/devtools/shared/test/browser_toolbar_basic.js
+++ b/browser/devtools/shared/test/browser_toolbar_basic.js
@@ -4,20 +4,20 @@
 // Tests that the developer toolbar works properly
 
 let imported = {};
 Components.utils.import("resource:///modules/HUDService.jsm", imported);
 registerCleanupFunction(function() {
   imported = {};
 });
 
-const URL = "http://example.com/browser/browser/devtools/shared/test/browser_toolbar_basic.html";
+const TEST_URI = "http://example.com/browser/browser/devtools/shared/test/browser_toolbar_basic.html";
 
 function test() {
-  addTab(URL, function(browser, tab) {
+  addTab(TEST_URI, function(browser, tab) {
     info("Starting browser_toolbar_basic.js");
     runTest();
   });
 }
 
 function runTest() {
   ok(!DeveloperToolbar.visible, "DeveloperToolbar is not visible in runTest");
 
new file mode 100755
--- /dev/null
+++ b/browser/devtools/shared/test/browser_toolbar_tooltip.js
@@ -0,0 +1,45 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the developer toolbar works properly
+
+const TEST_URI = "data:text/html;charset=utf-8,<p>Tooltip Tests</p>";
+
+function test() {
+  DeveloperToolbarTest.test(TEST_URI, function(browser, tab) {
+    runTest();
+    finish();
+  });
+}
+
+function runTest() {
+  let tooltipPanel = DeveloperToolbar.tooltipPanel;
+
+  DeveloperToolbar.display.focusManager.helpRequest();
+  DeveloperToolbar.display.inputter.setInput('help help');
+
+  DeveloperToolbar.display.inputter.setCursor({ start: 'help help'.length });
+  is(tooltipPanel._dimensions.start, 'help '.length,
+          'search param start, when cursor at end');
+  ok(getLeftMargin() > 30, 'tooltip offset, when cursor at end')
+
+  DeveloperToolbar.display.inputter.setCursor({ start: 'help'.length });
+  is(tooltipPanel._dimensions.start, 0,
+          'search param start, when cursor at end of command');
+  ok(getLeftMargin() > 9, 'tooltip offset, when cursor at end of command')
+
+  DeveloperToolbar.display.inputter.setCursor({ start: 'help help'.length - 1 });
+  is(tooltipPanel._dimensions.start, 'help '.length,
+          'search param start, when cursor at penultimate position');
+  ok(getLeftMargin() > 30, 'tooltip offset, when cursor at penultimate position')
+
+  DeveloperToolbar.display.inputter.setCursor({ start: 0 });
+  is(tooltipPanel._dimensions.start, 0,
+          'search param start, when cursor at start');
+  ok(getLeftMargin() > 9, 'tooltip offset, when cursor at start')
+}
+
+function getLeftMargin() {
+  let style = DeveloperToolbar.tooltipPanel._frame.style.marginLeft;
+  return parseInt(style.slice(0, -2), 10);
+}
--- a/browser/devtools/shared/test/head.js
+++ b/browser/devtools/shared/test/head.js
@@ -62,17 +62,65 @@ let DeveloperToolbarTest = {
   hide: function DTT_hide() {
     if (!DeveloperToolbar.visible) {
       ok(false, "!DeveloperToolbar.visible at start of closeDeveloperToolbar");
     }
     else {
       DeveloperToolbar.display.inputter.setInput("");
       DeveloperToolbar.hide();
     }
-  }
+  },
+
+  /**
+   * Quick wrapper around the things you need to do to run DeveloperToolbar
+   * command tests:
+   * - Set the pref 'devtools.toolbar.enabled' to true
+   * - Add a tab pointing at |uri|
+   * - Open the DeveloperToolbar
+   * - Register a cleanup function to undo the above
+   * - Run the tests
+   *
+   * @param uri The uri of a page to load. Can be 'about:blank' or 'data:...'
+   * @param testFunc A function containing the tests to run. This should
+   * arrange for 'finish()' to be called on completion.
+   */
+  test: function DTT_test(uri, testFunc) {
+    let menuItem = document.getElementById("menu_devToolbar");
+    let command = document.getElementById("Tools:DevToolbar");
+    let appMenuItem = document.getElementById("appmenu_devToolbar");
+
+    registerCleanupFunction(function() {
+      DeveloperToolbarTest.hide();
+
+      // a.k.a Services.prefs.clearUserPref("devtools.toolbar.enabled");
+      if (menuItem) menuItem.hidden = true;
+      if (command) command.setAttribute("disabled", "true");
+      if (appMenuItem) appMenuItem.hidden = true;
+    });
+
+    // a.k.a: Services.prefs.setBoolPref("devtools.toolbar.enabled", true);
+    if (menuItem) menuItem.hidden = false;
+    if (command) command.removeAttribute("disabled");
+    if (appMenuItem) appMenuItem.hidden = false;
+
+    addTab(uri, function(browser, tab) {
+      DeveloperToolbarTest.show(function() {
+
+        try {
+          testFunc(browser, tab);
+        }
+        catch (ex) {
+          ok(false, "" + ex);
+          console.error(ex);
+          finish();
+          throw ex;
+        }
+      });
+    });
+  },
 };
 
 function catchFail(func) {
   return function() {
     try {
       return func.apply(null, arguments);
     }
     catch (ex) {
--- a/browser/devtools/webconsole/HUDService.jsm
+++ b/browser/devtools/webconsole/HUDService.jsm
@@ -1753,16 +1753,17 @@ HUD_SERVICE.prototype =
 
     // Make sure that the console panel does not try to call
     // deactivateHUDForContext() again.
     hud.consoleWindowUnregisterOnHide = false;
 
     // Remove the HUDBox and the consolePanel if the Web Console is inside a
     // floating panel.
     if (hud.consolePanel && hud.consolePanel.parentNode) {
+      hud.consolePanel.hidePopup();
       hud.consolePanel.parentNode.removeChild(hud.consolePanel);
       hud.consolePanel.removeAttribute("hudId");
       hud.consolePanel = null;
     }
 
     hud.HUDBox.parentNode.removeChild(hud.HUDBox);
 
     if (hud.splitter.parentNode) {
--- a/browser/installer/windows/nsis/installer.nsi
+++ b/browser/installer/windows/nsis/installer.nsi
@@ -1087,24 +1087,24 @@ Function .onInit
   ; Setup the components.ini file for the Components Page
   WriteINIStr "$PLUGINSDIR\components.ini" "Settings" NumFields "2"
 
   WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Type   "label"
   WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Text   "$(OPTIONAL_COMPONENTS_DESC)"
   WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Left   "0"
   WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Right  "-1"
   WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Top    "5"
-  WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Bottom "15"
+  WriteINIStr "$PLUGINSDIR\components.ini" "Field 1" Bottom "25"
 
   WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Type   "checkbox"
   WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Text   "$(MAINTENANCE_SERVICE_CHECKBOX_DESC)"
   WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Left   "0"
   WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Right  "-1"
-  WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Top    "20"
-  WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Bottom "30"
+  WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Top    "27"
+  WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Bottom "37"
   WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" State  "1"
   WriteINIStr "$PLUGINSDIR\components.ini" "Field 2" Flags  "GROUP"
 
   ; There must always be a core directory.
   ${GetSize} "$EXEDIR\core\" "/S=0K" $R5 $R7 $R8
   SectionSetSize ${APP_IDX} $R5
 
   ; Initialize $hHeaderBitmap to prevent redundant changing of the bitmap if
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -224,19 +224,19 @@ These should match what Safari and other
 <!ENTITY scratchpad.label             "Scratchpad">
 <!ENTITY scratchpad.accesskey         "s">
 <!ENTITY scratchpad.keycode           "VK_F4">
 <!ENTITY scratchpad.keytext           "F4">
 
 <!ENTITY inspectCloseButton.tooltiptext "Close Inspector">
 
 <!ENTITY devToolbarCloseButton.tooltiptext "Close Developer Toolbar">
-<!ENTITY devToolbarMenu.label "Developer Toolbar">
-<!ENTITY devToolbarMenu.accesskey "v">
-<!ENTITY devToolbar.commandkey "v">
+<!ENTITY devToolbarMenu.label              "Developer Toolbar">
+<!ENTITY devToolbarMenu.accesskey          "v">
+<!ENTITY devToolbar.commandkey             "v">
 
 <!ENTITY webConsoleButton.label "Web Console">
 <!ENTITY inspectorButton.label "Inspector">
 <!ENTITY scriptsButton.label "Scripts">
 
 <!ENTITY inspectorHTMLCopyInner.label       "Copy Inner HTML">
 <!ENTITY inspectorHTMLCopyInner.accesskey   "I">
 
--- a/toolkit/components/telemetry/TelemetryPing.js
+++ b/toolkit/components/telemetry/TelemetryPing.js
@@ -168,16 +168,17 @@ TelemetryPing.prototype = {
   _prevValues: {},
   // Generate a unique id once per session so the server can cope with
   // duplicate submissions.
   _uuid: generateUUID(),
   // Regex that matches histograms we carea bout during startup.
   _startupHistogramRegex: /SQLITE|HTTP|SPDY|CACHE|DNS/,
   _slowSQLStartup: {},
   _prevSession: null,
+  _hasWindowRestoredObserver : false,
   // Bug 756152
   _disablePersistentTelemetrySending: true,
 
   /**
    * When reflecting a histogram into JS, Telemetry hands us an object
    * with the following properties:
    * 
    * - min, max, histogram_type, sum: simple integers;
@@ -623,16 +624,17 @@ TelemetryPing.prototype = {
       // This may change once about:telemetry is added.
       Telemetry.canRecord = false;
       return;
     }
     Services.obs.addObserver(this, "private-browsing", false);
     Services.obs.addObserver(this, "profile-before-change", false);
     Services.obs.addObserver(this, "sessionstore-windows-restored", false);
     Services.obs.addObserver(this, "quit-application-granted", false);
+    this._hasWindowRestoredObserver = true;
 
     // Delay full telemetry initialization to give the browser time to
     // run various late initializers. Otherwise our gathered memory
     // footprint and other numbers would be too optimistic.
     let self = this;
     this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
     let timerCallback = function() {
       self._initialized = true;
@@ -656,20 +658,19 @@ TelemetryPing.prototype = {
     Telemetry.loadHistograms(file, loadCallback, sync);
   },
 
   /** 
    * Remove observers to avoid leaks
    */
   uninstall: function uninstall() {
     this.detachObservers()
-    try {
+    if (this._hasWindowRestoredObserver) {
       Services.obs.removeObserver(this, "sessionstore-windows-restored");
-    } catch (e) {
-      // Already observed this event.
+      this._hasWindowRestoredObserver = false;
     }
     Services.obs.removeObserver(this, "profile-before-change");
     Services.obs.removeObserver(this, "private-browsing");
     Services.obs.removeObserver(this, "quit-application-granted");
   },
 
   /**
    * This observer drives telemetry.
@@ -701,16 +702,17 @@ TelemetryPing.prototype = {
       if (aData == "enter") {
         this.detachObservers()
       } else {
         this.attachObservers()
       }
       break;
     case "sessionstore-windows-restored":
       Services.obs.removeObserver(this, "sessionstore-windows-restored");
+      this._hasWindowRestoredObserver = false;
       // fall through
     case "test-gather-startup":
       this.gatherStartupInformation();
       break;
     case "idle-daily":
       // Enqueue to main-thread, otherwise components may be inited by the
       // idle-daily category and miss the gather-telemetry notification.
       Services.tm.mainThread.dispatch((function() {