Merge inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Sat, 22 Sep 2012 08:28:28 -0400
changeset 107917 b461a7cd250e813aa920ebe6cbefb8112e554e10
parent 107875 9cfb80a82883725b5fbdba4adb271d9f2d8f2e41 (diff)
parent 107916 a06ea10ddb048c494401d42b40fbc159c71e826c (current diff)
child 107918 156f00b9ac9238a087a504371438e1c497af9e17
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
milestone18.0a1
Merge inbound to m-c.
browser/base/content/browser.js
image/test/mochitest/bug733553-iframe.html
--- a/browser/base/content/browser-tabview.js
+++ b/browser/base/content/browser-tabview.js
@@ -6,17 +6,16 @@ let TabView = {
   _deck: null,
   _iframe: null,
   _window: null,
   _initialized: false,
   _browserKeyHandlerInitialized: false,
   _closedLastVisibleTabBeforeFrameInitialized: false,
   _isFrameLoading: false,
   _initFrameCallbacks: [],
-  _lastSessionGroupName: null,
   PREF_BRANCH: "browser.panorama.",
   PREF_FIRST_RUN: "browser.panorama.experienced_first_run",
   PREF_STARTUP_PAGE: "browser.startup.page",
   PREF_RESTORE_ENABLED_ONCE: "browser.panorama.session_restore_enabled_once",
   GROUPS_IDENTIFIER: "tabview-groups",
   VISIBILITY_IDENTIFIER: "tabview-visibility",
 
   // ----------
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -150,17 +150,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyGetter(this, "SafeBrowsing", function() {
   let tmp = {};
   Cu.import("resource:///modules/SafeBrowsing.jsm", tmp);
   return tmp.SafeBrowsing;
 });
 #endif
 
 XPCOMUtils.defineLazyModuleGetter(this, "gBrowserNewTabPreloader",
-  "resource://gre/modules/BrowserNewTabPreloader.jsm", "BrowserNewTabPreloader");
+  "resource:///modules/BrowserNewTabPreloader.jsm", "BrowserNewTabPreloader");
 
 let gInitialPages = [
   "about:blank",
   "about:newtab",
   "about:home",
   "about:privatebrowsing",
   "about:sessionrestore"
 ];
--- a/browser/components/tabview/groupitems.js
+++ b/browser/components/tabview/groupitems.js
@@ -29,17 +29,16 @@
 //   immediately - true if we want all placement immediately, not with animation
 function GroupItem(listOfEls, options) {
   if (!options)
     options = {};
 
   this._inited = false;
   this._uninited = false;
   this._children = []; // an array of Items
-  this.defaultSize = new Point(TabItems.tabWidth * 1.5, TabItems.tabHeight * 1.5);
   this.isAGroupItem = true;
   this.id = options.id || GroupItems.getNextID();
   this._isStacked = false;
   this.expanded = null;
   this.hidden = false;
   this.fadeAwayUndoButtonDelay = 15000;
   this.fadeAwayUndoButtonDuration = 300;
 
@@ -2107,28 +2106,16 @@ let GroupItems = {
   getNextID: function GroupItems_getNextID() {
     var result = this.nextID;
     this.nextID++;
     this._save();
     return result;
   },
 
   // ----------
-  // Function: getStorageData
-  // Returns an object for saving GroupItems state to persistent storage.
-  getStorageData: function GroupItems_getStorageData() {
-    var data = {nextID: this.nextID, groupItems: []};
-    this.groupItems.forEach(function(groupItem) {
-      data.groupItems.push(groupItem.getStorageData());
-    });
-
-    return data;
-  },
-
-  // ----------
   // Function: saveAll
   // Saves GroupItems state, as well as the state of all of the groupItems.
   saveAll: function GroupItems_saveAll() {
     this._save();
     this.groupItems.forEach(function(groupItem) {
       groupItem.save();
     });
   },
--- a/browser/components/tabview/items.js
+++ b/browser/components/tabview/items.js
@@ -12,19 +12,16 @@
 // If you subclass, in addition to the things Item provides, you need to also provide these methods:
 //   setBounds - function(rect, immediately, options)
 //   setZ - function(value)
 //   close - function()
 //   save - function()
 //
 // Subclasses of Item must also provide the <Subscribable> interface.
 //
-// ... and this property:
-//   defaultSize - a Point
-//
 // Make sure to call _init() from your subclass's constructor.
 function Item() {
   // Variable: isAnItem
   // Always true for Items
   this.isAnItem = true;
 
   // Variable: bounds
   // The position and size of this Item, represented as a <Rect>.
@@ -97,17 +94,16 @@ Item.prototype = {
         typeof this.removeSubscriber == 'function' && 
         typeof this._sendToSubscribers == 'function',
         'Subclass must implement the Subscribable interface');
     Utils.assert(Utils.isDOMElement(container), 'container must be a DOM element');
     Utils.assert(typeof this.setBounds == 'function', 'Subclass must provide setBounds');
     Utils.assert(typeof this.setZ == 'function', 'Subclass must provide setZ');
     Utils.assert(typeof this.close == 'function', 'Subclass must provide close');
     Utils.assert(typeof this.save == 'function', 'Subclass must provide save');
-    Utils.assert(Utils.isPoint(this.defaultSize), 'Subclass must provide defaultSize');
     Utils.assert(Utils.isRect(this.bounds), 'Subclass must provide bounds');
 
     this.container = container;
     this.$container = iQ(container);
 
     iQ(this.container).data('item', this);
 
     // ___ drag
--- a/browser/components/tabview/storage.js
+++ b/browser/components/tabview/storage.js
@@ -32,39 +32,16 @@ let Storage = {
 
   // ----------
   // Function: uninit
   uninit: function Storage_uninit () {
     this._sessionStore = null;
   },
 
   // ----------
-  // Function: wipe
-  // Cleans out all the stored data, leaving empty objects.
-  wipe: function Storage_wipe() {
-    try {
-      var self = this;
-
-      // ___ Tabs
-      AllTabs.tabs.forEach(function(tab) {
-        self.saveTab(tab, null);
-      });
-
-      // ___ Other
-      this.saveGroupItemsData(gWindow, {});
-      this.saveUIData(gWindow, {});
-
-      this._sessionStore.setWindowValue(gWindow, this.GROUP_DATA_IDENTIFIER,
-        JSON.stringify({}));
-    } catch (e) {
-      Utils.log("Error in wipe: "+e);
-    }
-  },
-
-  // ----------
   // Function: saveTab
   // Saves the data for a single tab.
   saveTab: function Storage_saveTab(tab, data) {
     Utils.assert(tab, "tab");
 
     this._sessionStore.setTabValue(tab, this.TAB_DATA_IDENTIFIER,
       JSON.stringify(data));
   },
--- a/browser/components/tabview/tabitems.js
+++ b/browser/components/tabview/tabitems.js
@@ -36,17 +36,16 @@ function TabItem(tab, options) {
   this.$tabTitle = iQ('.tab-title', $div);
   this.$canvas = iQ('.thumb canvas', $div);
   this.$cachedThumb = iQ('img.cached-thumb', $div);
   this.$favImage = iQ('.favicon>img', $div);
   this.$close = iQ('.close', $div);
 
   this.tabCanvas = new TabCanvas(this.tab, this.$canvas[0]);
 
-  this.defaultSize = new Point(TabItems.tabWidth, TabItems.tabHeight);
   this._hidden = false;
   this.isATabItem = true;
   this.keepProportional = true;
   this._hasBeenDrawn = false;
   this._reconnected = false;
   this.isDragging = false;
   this.isStacked = false;
 
deleted file mode 100644
--- a/browser/components/tabview/test/browser_tabview_bug595020.js
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-let stateStartup = {windows:[
-  {tabs:[{entries:[{url:"about:home"}]}], extData:{"tabview-last-session-group-name":"title"}}
-]};
-
-function test() {
-  let assertWindowTitle = function (win, title) {
-    let browser = win.gBrowser.tabs[0].linkedBrowser;
-    let winTitle = win.gBrowser.getWindowTitleForBrowser(browser);
-
-    info('window title is: "' + winTitle + '"');
-    is(winTitle.indexOf(title), 0, "title starts with '" + title + "'");
-  };
-
-  let testGroupNameChange = function (win) {
-    showTabView(function () {
-      let cw = win.TabView.getContentWindow();
-      let groupItem = cw.GroupItems.groupItems[0];
-      groupItem.setTitle("new-title");
-
-      hideTabView(function () {
-        assertWindowTitle(win, "new-title");
-        finish();
-      }, win);
-    }, win);
-  };
-
-  waitForExplicitFinish();
-
-  newWindowWithState(stateStartup, function (win) {
-    registerCleanupFunction(function () win.close());
-    assertWindowTitle(win, "title");
-    testGroupNameChange(win);
-  });
-}
--- a/browser/devtools/debugger/test/browser_dbg_globalactor-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_globalactor-01.js
@@ -17,17 +17,49 @@ function test()
     is(aType, "browser", "Root actor should identify itself as a browser.");
     gClient.listTabs(function(aResponse) {
       let globalActor = aResponse.testGlobalActor1;
       ok(globalActor, "Found the test tab actor.")
       ok(globalActor.indexOf("testone") >= 0,
          "testTabActor's actorPrefix should be used.");
       gClient.request({ to: globalActor, type: "ping" }, function(aResponse) {
         is(aResponse.pong, "pong", "Actor should respond to requests.");
-        finish_test();
+        // Send another ping to see if the same actor is used.
+        gClient.request({ to: globalActor, type: "ping" }, function(aResponse) {
+          is(aResponse.pong, "pong", "Actor should respond to requests.");
+
+          // Make sure that lazily-created actors are created only once.
+          let connections = Object.keys(DebuggerServer._connections);
+          is(connections.length, 1, "Only one connection is established.");
+          let connPrefix = connections[0];
+          ok(DebuggerServer._connections[connPrefix],
+             connPrefix + " is the only connection.");
+          // First we look for the pool of global actors.
+          let extraPools = DebuggerServer._connections[connPrefix]._extraPools;
+          let globalPool;
+          for (let pool of extraPools) {
+            if (Object.keys(pool._actors).some(function(elem) {
+              // Tab actors are in the global pool.
+              let re = new RegExp(connPrefix + "tab", "g");
+              return elem.match(re) !== null;
+            })) {
+              globalPool = pool;
+              break;
+            }
+          }
+          // Then we look if the global pool contains only one test actor.
+          let actorPrefix = connPrefix + "testone";
+          let actors = Object.keys(globalPool._actors).join();
+          info("Global actors: " + actors);
+          isnot(actors.indexOf(actorPrefix), -1, "The test actor exists in the pool.");
+          is(actors.indexOf(actorPrefix), actors.lastIndexOf(actorPrefix),
+             "Only one actor exists in the pool.");
+
+          finish_test();
+        });
       });
     });
   });
 }
 
 function finish_test()
 {
   gClient.close(function() {
--- a/browser/devtools/webconsole/test/Makefile.in
+++ b/browser/devtools/webconsole/test/Makefile.in
@@ -110,16 +110,17 @@ MOCHITEST_BROWSER_FILES = \
 	browser_webconsole_window_zombie.js \
 	browser_cached_messages.js \
 	browser_bug664688_sandbox_update_after_navigation.js \
 	browser_webconsole_menustatus.js \
 	browser_result_format_as_string.js \
 	browser_webconsole_bug_737873_mixedcontent.js \
 	browser_output_breaks_after_console_dir_uninspectable.js \
 	browser_console_log_inspectable_object.js \
+	browser_bug_638949_copy_link_location.js \
 	head.js \
 	$(NULL)
 
 MOCHITEST_BROWSER_FILES += \
 	test-console.html \
 	test-network.html \
 	test-network-request.html \
 	test-mutation.html \
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_bug_638949_copy_link_location.js
@@ -0,0 +1,153 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/" +
+  "test/test-console.html?_date=" + Date.now();
+const COMMAND_NAME = "consoleCmd_copyURL";
+const CONTEXT_MENU_ID = "#menu_copyURL";
+
+let HUD = null;
+let output = null;
+let menu = null;
+
+function test() {
+  addTab(TEST_URI);
+
+  browser.addEventListener("load", function onLoad() {
+    browser.removeEventListener("load", onLoad, true);
+
+    openConsole(null, function (aHud) {
+      HUD = aHud;
+      output = aHud.outputNode;
+      menu = HUD.iframeWindow.document.getElementById("output-contextmenu");
+
+      executeSoon(testWithoutNetActivity);
+    });
+  }, true);
+}
+
+// Return whether "Copy Link Location" command is enabled or not.
+function isEnabled() {
+  let controller = top.document.commandDispatcher.
+    getControllerForCommand(COMMAND_NAME);
+
+  return controller && controller.isCommandEnabled(COMMAND_NAME);
+}
+
+function select(query) {
+  let target = output.querySelector(query);
+
+  output.focus();
+  output.selectedItem = target;
+
+  return target;
+}
+
+function testWithoutNetActivity() {
+  HUD.jsterm.clearOutput();
+  output = HUD.outputNode;
+  content.wrappedJSObject.console.log("bug 638949");
+
+  // Test that "Copy Link Location" command is disabled if there
+  // were no network-related messages.
+  waitForSuccess({
+    name: "no net activity in console",
+
+    validatorFn: function () {
+      return output.textContent.indexOf("bug 638949") > -1;
+    },
+
+    successFn: function () {
+      select(".webconsole-msg-log");
+      goUpdateCommand(COMMAND_NAME);
+      ok(!isEnabled(), COMMAND_NAME + "is disabled");
+      executeSoon(testMenuWithoutNetActivity);
+    }
+  });
+}
+
+function testMenuWithoutNetActivity() {
+  // Test that "Copy Link Location" menu item is hidden if there
+  // were no network-related messages.
+  let target = select(".webconsole-msg-log");
+
+  function next() {
+    menu.hidePopup();
+    executeSoon(testWithNetActivity);
+  }
+
+  waitForOpenContextMenu(menu, {
+    target: target,
+
+    successFn: function () {
+      let isHidden = menu.querySelector(CONTEXT_MENU_ID).hidden;
+      ok(isHidden, CONTEXT_MENU_ID + " is hidden");
+      next();
+    },
+
+    failureFn: next
+  });
+}
+
+function testWithNetActivity() {
+  HUD.jsterm.clearOutput();
+  content.location.reload(); // Reloading will produce network logging
+
+  // Test that "Copy Link Location" command is enabled and works
+  // as expected if there were any network-related messages.
+  //
+  // This command should copy only the URL without timestamp and other
+  // stuff.
+  waitForSuccess({
+    name: "net activity in console",
+
+    validatorFn: function () {
+      let item = select(".webconsole-msg-network");
+      return item && item.url;
+    },
+
+    successFn: function () {
+      output.focus();
+      goUpdateCommand(COMMAND_NAME);
+      ok(isEnabled(), COMMAND_NAME + " is enabled");
+
+      waitForClipboard(output.selectedItem.url, function clipboardSetup() {
+        goDoCommand(COMMAND_NAME);
+      }, testMenuWithNetActivity, testMenuWithNetActivity);
+    },
+
+    failureFn: testMenuWithNetActivity
+  });
+}
+
+function testMenuWithNetActivity() {
+  // Test that "Copy Link Location" menu item is visible if there
+  // were any network-related messages.
+  let target = select(".webconsole-msg-network");
+
+  function next() {
+    menu.hidePopup();
+    executeSoon(finalize);
+  }
+
+  waitForOpenContextMenu(menu, {
+    target: target,
+
+    successFn: function () {
+      let isVisible = !menu.querySelector(CONTEXT_MENU_ID).hidden;
+      ok(isVisible, CONTEXT_MENU_ID + " is visible");
+      next();
+    },
+
+    failureFn: next
+  });
+}
+
+function finalize() {
+  HUD = null;
+  output = null;
+  menu = null;
+  finishTest();
+}
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_764572_output_open_url.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_764572_output_open_url.js
@@ -165,62 +165,11 @@ function testOnNetActivity_contextmenu()
 
 function finalizeTest() {
   HUD = null;
   outputNode = null;
   contextMenu = null
   finishTest();
 }
 
-/**
- * Polls a given function waiting for opening context menu.
- *
- * @Param {nsIDOMElement} aContextMenu
- * @param object aOptions
- *        Options object with the following properties:
- *        - successFn
- *        A function called if opening the given context menu - success to return.
- *        - failureFn
- *        A function called if not opening the given context menu - fails to return.
- *        - target
- *        The target element for showing a context menu.
- *        - timeout
- *        Timeout for popup shown, in milliseconds. Default is 5000.
- */
-function waitForOpenContextMenu(aContextMenu, aOptions) {
-  let start = Date.now();
-  let timeout = aOptions.timeout || 5000;
-  let targetElement = aOptions.target;
-
-  if (!aContextMenu) {
-    ok(false, "Can't get a context menu.");
-    aOptions.failureFn();
-    return;
-  }
-  if (!targetElement) {
-    ok(false, "Can't get a target element.");
-    aOptions.failureFn();
-    return;
-  }
-
-  function onPopupShown() {
-    aContextMenu.removeEventListener("popupshown", onPopupShown);
-    clearTimeout(onTimeout);
-    aOptions.successFn();
-  }
-
-
-  aContextMenu.addEventListener("popupshown", onPopupShown);
-
-  let onTimeout = setTimeout(function(){
-    aContextMenu.removeEventListener("popupshown", onPopupShown);
-    aOptions.failureFn();
-  }, timeout);
-
-  // open a context menu.
-  let eventDetails = { type : "contextmenu", button : 2};
-  EventUtils.synthesizeMouse(targetElement, 2, 2,
-                             eventDetails, targetElement.ownerDocument.defaultView);
-}
-
 function closeContextMenu (aContextMenu) {
   aContextMenu.hidePopup();
 }
--- a/browser/devtools/webconsole/test/head.js
+++ b/browser/devtools/webconsole/test/head.js
@@ -174,16 +174,67 @@ function closeConsole(aTab, aCallback)
 
   if (aCallback) {
     Services.obs.addObserver(onWebConsoleClose, "web-console-destroyed", false);
   }
 
   HUDService.deactivateHUDForContext(aTab || tab);
 }
 
+/**
+ * Polls a given function waiting for opening context menu.
+ *
+ * @Param {nsIDOMElement} aContextMenu
+ * @param object aOptions
+ *        Options object with the following properties:
+ *        - successFn
+ *        A function called if opening the given context menu - success to return.
+ *        - failureFn
+ *        A function called if not opening the given context menu - fails to return.
+ *        - target
+ *        The target element for showing a context menu.
+ *        - timeout
+ *        Timeout for popup shown, in milliseconds. Default is 5000.
+ */
+function waitForOpenContextMenu(aContextMenu, aOptions) {
+  let start = Date.now();
+  let timeout = aOptions.timeout || 5000;
+  let targetElement = aOptions.target;
+
+  if (!aContextMenu) {
+    ok(false, "Can't get a context menu.");
+    aOptions.failureFn();
+    return;
+  }
+  if (!targetElement) {
+    ok(false, "Can't get a target element.");
+    aOptions.failureFn();
+    return;
+  }
+
+  function onPopupShown() {
+    aContextMenu.removeEventListener("popupshown", onPopupShown);
+    clearTimeout(onTimeout);
+    aOptions.successFn();
+  }
+
+
+  aContextMenu.addEventListener("popupshown", onPopupShown);
+
+  let onTimeout = setTimeout(function(){
+    aContextMenu.removeEventListener("popupshown", onPopupShown);
+    aOptions.failureFn();
+  }, timeout);
+
+  // open a context menu.
+  let eventDetails = { type : "contextmenu", button : 2};
+  EventUtils.synthesizeMouse(targetElement, 2, 2,
+                             eventDetails, targetElement.ownerDocument.defaultView);
+}
+
 function finishTest()
 {
   browser = hudId = hud = filterBox = outputNode = cs = null;
 
   let hud = HUDService.getHudByWindow(content);
   if (!hud) {
     finish();
     return;
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -2266,19 +2266,26 @@ WebConsoleFrame.prototype = {
       }
 
       aCallback(this, aEvent);
     }, false);
   },
 
   /**
    * Copies the selected items to the system clipboard.
+   *
+   * @param object aOptions
+   *        - linkOnly:
+   *        An optional flag to copy only URL without timestamp and
+   *        other meta-information. Default is false.
    */
-  copySelectedItems: function WCF_copySelectedItems()
+  copySelectedItems: function WCF_copySelectedItems(aOptions)
   {
+    aOptions = aOptions || { linkOnly: false };
+
     // Gather up the selected items and concatenate their clipboard text.
     let strings = [];
     let newGroup = false;
 
     let children = this.outputNode.children;
 
     for (let i = 0; i < children.length; i++) {
       let item = children[i];
@@ -2295,17 +2302,23 @@ WebConsoleFrame.prototype = {
       // Ensure the selected item hasn't been filtered by type or string.
       if (!item.classList.contains("hud-filtered-by-type") &&
           !item.classList.contains("hud-filtered-by-string")) {
         let timestampString = l10n.timestampString(item.timestamp);
         if (newGroup) {
           strings.push("--");
           newGroup = false;
         }
-        strings.push("[" + timestampString + "] " + item.clipboardText);
+
+        if (aOptions.linkOnly) {
+          strings.push(item.url);
+        }
+        else {
+          strings.push("[" + timestampString + "] " + item.clipboardText);
+        }
       }
     }
 
     clipboardHelper.copyString(strings.join("\n"), this.document);
   },
 
   /**
    * Open the selected item's URL in a new tab.
@@ -3459,29 +3472,35 @@ CommandController.prototype = {
   /**
    * Open the URL of the selected message in a new tab.
    */
   openURL: function CommandController_openURL()
   {
     this.owner.openSelectedItemInTab();
   },
 
+  copyURL: function CommandController_copyURL()
+  {
+    this.owner.copySelectedItems({ linkOnly: true });
+  },
+
   supportsCommand: function CommandController_supportsCommand(aCommand)
   {
     return this.isCommandEnabled(aCommand);
   },
 
   isCommandEnabled: function CommandController_isCommandEnabled(aCommand)
   {
     switch (aCommand) {
       case "cmd_copy":
         // Only enable "copy" if nodes are selected.
         return this.owner.outputNode.selectedCount > 0;
-      case "consoleCmd_openURL": {
-        // Only enable "open url" if node is Net Activity.
+      case "consoleCmd_openURL":
+      case "consoleCmd_copyURL": {
+        // Only enable URL-related actions if node is Net Activity.
         let selectedItem = this.owner.outputNode.selectedItem;
         return selectedItem && selectedItem.url;
       }
       case "cmd_fontSizeEnlarge":
       case "cmd_fontSizeReduce":
       case "cmd_fontSizeReset":
       case "cmd_selectAll":
         return true;
@@ -3492,16 +3511,19 @@ CommandController.prototype = {
   {
     switch (aCommand) {
       case "cmd_copy":
         this.copy();
         break;
       case "consoleCmd_openURL":
         this.openURL();
         break;
+      case "consoleCmd_copyURL":
+        this.copyURL();
+        break;
       case "cmd_selectAll":
         this.selectAll();
         break;
       case "cmd_fontSizeEnlarge":
         this.owner.changeFontSize("+");
         break;
       case "cmd_fontSizeReduce":
         this.owner.changeFontSize("-");
@@ -3517,16 +3539,17 @@ function gSequenceId()
 {
   return gSequenceId.n++;
 }
 gSequenceId.n = 0;
 
 
 function goUpdateConsoleCommands() {
   goUpdateCommand("consoleCmd_openURL");
+  goUpdateCommand("consoleCmd_copyURL");
 }
 
 
 
 ///////////////////////////////////////////////////////////////////////////////
 // Context Menu
 ///////////////////////////////////////////////////////////////////////////////
 
--- a/browser/devtools/webconsole/webconsole.xul
+++ b/browser/devtools/webconsole/webconsole.xul
@@ -22,16 +22,18 @@
   <commandset id="editMenuCommands"/>
 
   <commandset id="consoleCommands"
               commandupdater="true"
               events="richlistbox-select"
               oncommandupdate="goUpdateConsoleCommands();">
     <command id="consoleCmd_openURL"
              oncommand="goDoCommand('consoleCmd_openURL');"/>
+    <command id="consoleCmd_copyURL"
+             oncommand="goDoCommand('consoleCmd_copyURL');"/>
     <command id="cmd_fullZoomEnlarge" oncommand="goDoCommand('cmd_fontSizeEnlarge');"/>
     <command id="cmd_fullZoomReduce" oncommand="goDoCommand('cmd_fontSizeReduce');"/>
     <command id="cmd_fullZoomReset" oncommand="goDoCommand('cmd_fontSizeReset');"/>
   </commandset>
   <keyset id="fontSizeChangeSet">
     <key id="key_fullZoomReduce"  key="&fullZoomReduceCmd.commandkey;" command="cmd_fullZoomReduce"  modifiers="accel"/>
     <key key="&fullZoomReduceCmd.commandkey2;"  command="cmd_fullZoomReduce" modifiers="accel"/>
     <key id="key_fullZoomEnlarge" key="&fullZoomEnlargeCmd.commandkey;" command="cmd_fullZoomEnlarge" modifiers="accel"/>
@@ -45,16 +47,19 @@
   <popupset id="mainPopupSet">
     <menupopup id="output-contextmenu"
                onpopupshowing="ConsoleContextMenu.build(event);">
       <menuitem id="saveBodiesContextMenu" type="checkbox" label="&saveBodies.label;"
                 accesskey="&saveBodies.accesskey;"/>
       <menuitem id="menu_openURL" label="&openURL.label;"
                 accesskey="&openURL.accesskey;" command="consoleCmd_openURL"
                 selection="network" selectionType="single"/>
+      <menuitem id="menu_copyURL" label="&copyURLCmd.label;"
+                accesskey="&copyURLCmd.accesskey;" command="consoleCmd_copyURL"
+                selection="network" selectionType="single"/>
       <menuitem id="menu_copy"/>
       <menuitem id="menu_selectAll"/>
     </menupopup>
   </popupset>
 
   <vbox class="hud-outer-wrapper" flex="1">
     <vbox class="hud-console-wrapper" flex="1">
       <toolbar class="hud-console-filter-toolbar devtools-toolbar" mode="full">
--- a/browser/locales/en-US/chrome/browser/devtools/webConsole.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/webConsole.dtd
@@ -88,8 +88,11 @@
 <!ENTITY fullZoomEnlargeCmd.commandkey2 "="> <!-- + is above this key on many keyboards -->
 <!ENTITY fullZoomEnlargeCmd.commandkey3 "">
 
 <!ENTITY fullZoomReduceCmd.commandkey   "-">
 <!ENTITY fullZoomReduceCmd.commandkey2  "">
 
 <!ENTITY fullZoomResetCmd.commandkey    "0">
 <!ENTITY fullZoomResetCmd.commandkey2   "">
+
+<!ENTITY copyURLCmd.label     "Copy Link Location">
+<!ENTITY copyURLCmd.accesskey "a">
--- a/browser/modules/BrowserNewTabPreloader.jsm
+++ b/browser/modules/BrowserNewTabPreloader.jsm
@@ -10,39 +10,83 @@ const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const PREF_BRANCH = "browser.newtab.";
+const TOPIC_DELAYED_STARTUP = "browser-delayed-startup-finished";
+const PRELOADER_INIT_DELAY_MS = 5000;
 
-let BrowserNewTabPreloader =  {
+let BrowserNewTabPreloader = {
   init: function Preloader_init() {
-    Preferences.init();
-
-    if (Preferences.enabled) {
-      HiddenBrowser.create();
-    }
+    Initializer.start();
   },
 
   uninit: function Preloader_uninit() {
+    Initializer.stop();
     HostFrame.destroy();
     Preferences.uninit();
     HiddenBrowser.destroy();
   },
 
   newTab: function Preloader_newTab(aTab) {
     HiddenBrowser.swapWithNewTab(aTab);
   }
 };
 
 Object.freeze(BrowserNewTabPreloader);
 
+let Initializer = {
+  _timer: null,
+  _observing: false,
+
+  start: function Initializer_start() {
+    Services.obs.addObserver(this, TOPIC_DELAYED_STARTUP, false);
+    this._observing = true;
+  },
+
+  stop: function Initializer_stop() {
+    if (this._timer) {
+      this._timer.cancel();
+      this._timer = null;
+    }
+
+    if (this._observing) {
+      Services.obs.removeObserver(this, TOPIC_DELAYED_STARTUP);
+      this._observing = false;
+    }
+  },
+
+  observe: function Initializer_observe(aSubject, aTopic, aData) {
+    if (aTopic == TOPIC_DELAYED_STARTUP) {
+      Services.obs.removeObserver(this, TOPIC_DELAYED_STARTUP);
+      this._observing = false;
+      this._startTimer();
+    } else if (aTopic == "timer-callback") {
+      this._timer = null;
+      this._startPreloader();
+    }
+  },
+
+  _startTimer: function Initializer_startTimer() {
+    this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    this._timer.init(this, PRELOADER_INIT_DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT);
+  },
+
+  _startPreloader: function Initializer_startPreloader() {
+    Preferences.init();
+    if (Preferences.enabled) {
+      HiddenBrowser.create();
+    }
+  }
+};
+
 let Preferences = {
   _enabled: null,
   _branch: null,
   _url: null,
 
   get enabled() {
     if (this._enabled === null) {
       this._enabled = this._branch.getBoolPref("preload") &&
--- a/docshell/shistory/src/nsSHistory.cpp
+++ b/docshell/shistory/src/nsSHistory.cpp
@@ -101,17 +101,17 @@ static PRLogModuleInfo* gLogModule = PR_
   PR_BEGIN_MACRO                                           \
   {                                                        \
     nsAutoTObserverArray<nsWeakPtr, 2>::EndLimitedIterator \
       iter(mListeners);                                    \
     while (iter.HasMore()) {                               \
       nsCOMPtr<nsISHistoryListener> listener =             \
         do_QueryReferent(iter.GetNext());                  \
       if (listener) {                                      \
-        body;                                              \
+        body                                               \
       }                                                    \
     }                                                      \
   }                                                        \
   PR_END_MACRO
 
 // Calls a given method on all registered session history listeners.
 #define NOTIFY_LISTENERS(method, args)                     \
   ITERATE_LISTENERS(                                       \
old mode 100755
new mode 100644
--- a/toolkit/devtools/debugger/server/dbg-script-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-script-actors.js
@@ -35,33 +35,31 @@ function ThreadActor(aHooks)
  */
 ThreadActor._breakpointStore = {};
 
 ThreadActor.prototype = {
   actorPrefix: "context",
 
   get state() { return this._state; },
 
-  get dbg() { return this._dbg; },
-
   get _breakpointStore() { return ThreadActor._breakpointStore; },
 
   get threadLifetimePool() {
     if (!this._threadLifetimePool) {
       this._threadLifetimePool = new ActorPool(this.conn);
       this.conn.addActorPool(this._threadLifetimePool);
     }
     return this._threadLifetimePool;
   },
 
   clearDebuggees: function TA_clearDebuggees() {
-    if (this._dbg) {
-      let debuggees = this._dbg.getDebuggees();
+    if (this.dbg) {
+      let debuggees = this.dbg.getDebuggees();
       for (let debuggee of debuggees) {
-        this._dbg.removeDebuggee(debuggee);
+        this.dbg.removeDebuggee(debuggee);
       }
     }
     this.conn.removeActorPool(this._threadLifetimePool || undefined);
     this._threadLifetimePool = null;
     // Unless we carefully take apart the scripts table this way, we end up
     // leaking documents. It would be nice to track this down carefully, once
     // we have the appropriate tools.
     for (let url in this._scripts) {
@@ -74,21 +72,21 @@ ThreadActor.prototype = {
    * Add a debuggee global to the Debugger object.
    */
   addDebuggee: function TA_addDebuggee(aGlobal) {
     // Use the inspector xpcom component to turn on debugging
     // for aGlobal's compartment.  Ideally this won't be necessary
     // medium- to long-term, and will be managed by the engine
     // instead.
 
-    if (!this._dbg) {
-      this._dbg = new Debugger();
-      this._dbg.uncaughtExceptionHook = this.uncaughtExceptionHook.bind(this);
-      this._dbg.onDebuggerStatement = this.onDebuggerStatement.bind(this);
-      this._dbg.onNewScript = this.onNewScript.bind(this);
+    if (!this.dbg) {
+      this.dbg = new Debugger();
+      this.dbg.uncaughtExceptionHook = this.uncaughtExceptionHook.bind(this);
+      this.dbg.onDebuggerStatement = this.onDebuggerStatement.bind(this);
+      this.dbg.onNewScript = this.onNewScript.bind(this);
       // Keep the debugger disabled until a client attaches.
       this.dbg.enabled = this._state != "detached";
     }
 
     this.dbg.addDebuggee(aGlobal);
     for (let s of this.dbg.findScripts()) {
       this._addScript(s);
     }
@@ -110,21 +108,21 @@ ThreadActor.prototype = {
     if (this._state == "paused") {
       this.onResume();
     }
 
     this._state = "exited";
 
     this.clearDebuggees();
 
-    if (!this._dbg) {
+    if (!this.dbg) {
       return;
     }
-    this._dbg.enabled = false;
-    this._dbg = null;
+    this.dbg.enabled = false;
+    this.dbg = null;
   },
 
   /**
    * Disconnect the debugger and put the actor in the exited state.
    */
   exit: function TA_exit() {
     this.disconnect();
   },
@@ -1121,17 +1119,21 @@ PauseScopedActor.prototype = {
  *
  * @param aTarget Object
  *        The object being updated.
  * @param aNewAttrs Object
  *        The new attributes being set on the target.
  */
 function update(aTarget, aNewAttrs) {
   for (let key in aNewAttrs) {
-    aTarget[key] = aNewAttrs[key];
+    let desc = Object.getOwnPropertyDescriptor(aNewAttrs, key);
+
+    if (desc) {
+      Object.defineProperty(aTarget, key, desc);
+    }
   }
 }
 
 
 /**
  * Creates an actor for the specified object.
  *
  * @param aObj Debugger.Object
--- a/toolkit/devtools/debugger/server/dbg-server.js
+++ b/toolkit/devtools/debugger/server/dbg-server.js
@@ -524,18 +524,21 @@ DebuggerServerConnection.prototype = {
       } catch (e) {
         Cu.reportError(e);
         this.transport.send({
           error: "unknownError",
           message: ("error occurred while creating actor '" + actor.name +
                     "': " + safeErrorString(e))
         });
       }
+      // We want the newly-constructed actor to completely replace the factory
+      // actor. Reusing the existing actor ID will make sure ActorPool.addActor
+      // does the right thing.
+      instance.actorID = actor.actorID;
       actor.registeredPool.addActor(instance);
-      actor.registeredPool.removeActor(actor);
       actor = instance;
     }
 
     var ret = null;
 
     // Dispatch the request to the actor.
     if (actor.requestTypes && actor.requestTypes[aPacket.type]) {
       try {