Merge mozilla-central into services-central
authorGregory Szorc <gps@mozilla.com>
Thu, 26 Jan 2012 17:11:45 -0800
changeset 86728 c07595bee6cf341885c2b8b8497fc7a3e2c6a10e
parent 86727 93c071e2d4eaa34e93d8717380e7a45da38035f4 (current diff)
parent 86725 347bd64309233167c13bcb2c44b33db86a98a97a (diff)
child 86734 206305cbbeb15ffb1be04fc5ed4e1ed5b6e5e740
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone12.0a1
Merge mozilla-central into services-central
browser/devtools/styleeditor/test/browser_styleeditor_sv_filter.js
build/mobile/robocop/robotium-solo-3.0.jar
dom/interfaces/core/nsIDOMDOMError.idl
dom/interfaces/core/nsIDOMDOMErrorHandler.idl
dom/interfaces/traversal/nsIDOMDocumentTraversal.idl
gfx/angle/include/GLSLANG/ResourceLimits.h
gfx/angle/src/compiler/Link.cpp
gfx/angle/src/compiler/MozAngleLink.cpp
gfx/angle/src/compiler/generate_glslang_lexer.sh
gfx/angle/src/compiler/generate_glslang_parser.sh
gfx/angle/src/compiler/unistd.h
gfx/angle/src/libGLESv2/RefCountObject.cpp
gfx/angle/src/libGLESv2/RefCountObject.h
gfx/graphite2/src/CachedFace.h
gfx/graphite2/src/CharInfo.h
gfx/graphite2/src/CmapCache.h
gfx/graphite2/src/Code.h
gfx/graphite2/src/Endian.h
gfx/graphite2/src/Face.h
gfx/graphite2/src/FeatureMap.h
gfx/graphite2/src/FeatureVal.h
gfx/graphite2/src/Font.h
gfx/graphite2/src/GlyphFace.h
gfx/graphite2/src/GlyphFaceCache.h
gfx/graphite2/src/List.h
gfx/graphite2/src/Machine.h
gfx/graphite2/src/Main.h
gfx/graphite2/src/NameTable.h
gfx/graphite2/src/Pass.h
gfx/graphite2/src/Position.h
gfx/graphite2/src/Rule.h
gfx/graphite2/src/SegCache.h
gfx/graphite2/src/SegCacheEntry.h
gfx/graphite2/src/SegCacheStore.h
gfx/graphite2/src/Segment.h
gfx/graphite2/src/Silf.h
gfx/graphite2/src/Slot.h
gfx/graphite2/src/Sparse.h
gfx/graphite2/src/TtfTypes.h
gfx/graphite2/src/TtfUtil.h
gfx/graphite2/src/UtfCodec.h
gfx/graphite2/src/XmlTraceLog.h
gfx/graphite2/src/XmlTraceLogTags.h
gfx/graphite2/src/locale2lcid.h
gfx/graphite2/src/opcode_table.h
gfx/graphite2/src/opcodes.h
gfx/graphite2/src/processUTF.h
intl/chardet/public/nsDocumentCharsetInfoCID.h
intl/chardet/public/nsIDocCharset.idl
intl/chardet/public/nsIDocumentCharsetInfo.idl
intl/chardet/public/nsXMLEncodingCID.h
intl/chardet/src/nsDocumentCharsetInfo.cpp
intl/chardet/src/nsDocumentCharsetInfo.h
js/src/jit-test/tests/basic/bug684796.js
js/src/jit-test/tests/basic/testBug650618.js
js/src/jit-test/tests/basic/testInitSharp.js
js/src/jit-test/tests/jaeger/bug580883.js
js/src/jit-test/tests/jaeger/bug606662-1.js
js/src/jslock.cpp
js/src/jslocko.asm
js/src/lock_sparcv8plus.il
js/src/lock_sparcv9.il
js/src/tests/e4x/extensions/regress-335051.js
js/src/tests/js1_5/extensions/regress-367630.js
js/src/tests/js1_5/extensions/regress-368859.js
js/src/tests/js1_5/extensions/regress-379523.js
js/src/tests/js1_8_1/extensions/regress-452498-224.js
js/src/tests/js1_8_5/extensions/regress-613452.js
js/src/tests/js1_8_5/extensions/regress-630377.js
js/src/vm/StackSpace.h
js/xpconnect/tests/chrome/test_ccbeginfail.xul
js/xpconnect/tests/chrome/test_ccdump.xul
mobile/android/base/resources/drawable/checkerboard.png
mobile/android/base/resources/drawable/favicon.png
parser/htmlparser/public/nsILoggingSink.h
parser/htmlparser/src/nsLoggingSink.cpp
parser/htmlparser/src/nsLoggingSink.h
--- a/.hgtags
+++ b/.hgtags
@@ -67,8 +67,9 @@ 9eae975b3d6fb7748fe5a3c0113d449b1c7cc0b2
 138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R14
 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R14
 5eb553dd2ceae5f88d80f27afc5ef3935c5d43b0 AURORA_BASE_20110705
 41b84b87c816403e1b74963d8094cff0406c989e AURORA_BASE_20110816
 c0983049bcaa9551e5f276d5a77ce154c151e0b0 AURORA_BASE_20110927
 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R15
 54bfd8bf682e295ffd7f22fa921ca343957b6c1c AURORA_BASE_20111108
 a8506ab2c65480cf2f85f54e203ea746522c62bb AURORA_BASE_20111220
+462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R16
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -611,16 +611,19 @@ nsAccessible::VisibilityState()
 
     const nsIView* view = frame->GetView();
     if (view && view->GetVisibility() == nsViewVisibility_kHide)
       return vstates;
     
   } while (accessible = accessible->Parent());
 
   nsIFrame* frame = GetFrame();
+  if (!frame)
+    return vstates;
+
   const nsCOMPtr<nsIPresShell> shell(GetPresShell());
 
   // We need to know if at least a kMinPixels around the object is visible,
   // otherwise it will be marked states::OFFSCREEN.
   const PRUint16 kMinPixels  = 12;
   const nsSize frameSize = frame->GetSize();
   const nsRectVisibility rectVisibility =
     shell->GetRectVisibility(frame, nsRect(nsPoint(0,0), frameSize),
--- a/accessible/src/html/nsHTMLTableAccessible.cpp
+++ b/accessible/src/html/nsHTMLTableAccessible.cpp
@@ -1340,17 +1340,17 @@ nsHTMLTableAccessible::HasDescendant(con
     return true;
 
   // Make sure that the item we found has contents and either has multiple
   // children or the found item is not a whitespace-only text node.
   nsCOMPtr<nsIContent> foundItemContent = do_QueryInterface(foundItem);
   if (foundItemContent->GetChildCount() > 1)
     return true; // Treat multiple child nodes as non-empty
 
-  nsIContent *innerItemContent = foundItemContent->GetChildAt(0);
+  nsIContent *innerItemContent = foundItemContent->GetFirstChild();
   if (innerItemContent && !innerItemContent->TextIsOnlyWhitespace())
     return true;
 
   // If we found more than one node then return true not depending on
   // aAllowEmpty flag.
   // XXX it might be dummy but bug 501375 where we changed this addresses
   // performance problems only. Note, currently 'aAllowEmpty' flag is used for
   // caption element only. On another hand we create accessible object for
--- a/accessible/src/mac/Makefile.in
+++ b/accessible/src/mac/Makefile.in
@@ -51,16 +51,17 @@ LIBXUL_LIBRARY = 1
 CMMSRCS = nsAccessNodeWrap.mm \
           nsDocAccessibleWrap.mm \
           nsRootAccessibleWrap.mm \
           nsAccessibleWrap.mm \
           mozAccessible.mm \
           mozDocAccessible.mm \
           mozActionElements.mm \
           mozTextAccessible.mm \
+          mozHTMLAccessible.mm \
           $(NULL)
           
 
 EXPORTS = \
   nsAccessNodeWrap.h \
   nsTextAccessibleWrap.h \
   nsAccessibleWrap.h \
   nsARIAGridAccessibleWrap.h \
new file mode 100644
--- /dev/null
+++ b/accessible/src/mac/mozHTMLAccessible.h
@@ -0,0 +1,45 @@
+/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=2:tabstop=2:
+ */
+/* ***** 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 mozilla.org code.
+ *
+ * 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):
+ *   Original Author: Hub Figuière <hub@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 GPL or the LGPL. 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 ***** */
+
+#import "mozAccessible.h"
+
+@interface mozHeadingAccessible : mozAccessible
+
+@end
new file mode 100644
--- /dev/null
+++ b/accessible/src/mac/mozHTMLAccessible.mm
@@ -0,0 +1,56 @@
+/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=2:tabstop=2:
+ */
+/* ***** 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 mozilla.org code.
+ *
+ * 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):
+ *   Original Author: Hub Figuière <hub@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 GPL or the LGPL. 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 ***** */
+
+#import "mozHTMLAccessible.h"
+
+#import "nsHyperTextAccessible.h"
+
+@implementation mozHeadingAccessible
+
+- (id)value
+{
+  if (!mGeckoAccessible || !mGeckoAccessible->IsHyperText())
+    return nil;
+
+  PRUint32 level = mGeckoAccessible->AsHyperText()->GetLevelInternal();
+  return [NSNumber numberWithInt:level];
+}
+
+@end
--- a/accessible/src/mac/mozTextAccessible.mm
+++ b/accessible/src/mac/mozTextAccessible.mm
@@ -1,22 +1,25 @@
 #include "nsAccessibleWrap.h"
+
+#include "nsCocoaUtils.h"
 #include "nsObjCExceptions.h"
 
 #import "mozTextAccessible.h"
 
 using namespace mozilla::a11y;
 
 @interface mozTextAccessible (Private)
 - (NSString*)subrole;
 - (NSString*)selectedText;
 - (NSValue*)selectedTextRange;
 - (long)textLength;
 - (BOOL)isReadOnly;
 - (void)setText:(NSString*)newText;
+- (NSString*)text;
 @end
 
 @implementation mozTextAccessible
 
 - (id)initWithAccessible:(nsAccessibleWrap*)accessible
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
@@ -74,18 +77,19 @@ using namespace mozilla::a11y;
   if ([attribute isEqualToString:NSAccessibilityNumberOfCharactersAttribute])
     return [NSNumber numberWithInt:[self textLength]];
   if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute])
     return [self selectedTextRange];
   if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute])
     return [self selectedText];
   // Apple's SpeechSynthesisServer expects AXValue to return an AXStaticText
   // object's AXSelectedText attribute.  See bug 674612.
+  // Also if there is no selected text, we return the full text.See bug 369710
   if ([attribute isEqualToString:NSAccessibilityValueAttribute])
-    return [self selectedText];
+    return [self selectedText] ? : [self text];
 
   // let mozAccessible handle all other attributes
   return [super accessibilityAttributeValue:attribute];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute
@@ -153,16 +157,30 @@ using namespace mozilla::a11y;
 
   if (mGeckoEditableTextAccessible) {
     mGeckoEditableTextAccessible->SetTextContents(NS_ConvertUTF8toUTF16([newString UTF8String]));
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
+- (NSString*)text
+{
+  if (!mGeckoTextAccessible)
+    return nil;
+    
+  nsAutoString text;
+  nsresult rv = 
+    mGeckoTextAccessible->GetText(0, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT,
+				  text);
+  NS_ENSURE_SUCCESS(rv, nil);
+
+  return text.IsEmpty() ? nil : nsCocoaUtils::ToNSString(text);
+}
+
 - (long)textLength
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
   return mGeckoTextAccessible ? mGeckoTextAccessible->CharacterCount() : 0;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
 }
--- a/accessible/src/mac/nsAccessibleWrap.mm
+++ b/accessible/src/mac/nsAccessibleWrap.mm
@@ -40,16 +40,17 @@
 #include "nsObjCExceptions.h"
 
 #import "nsRoleMap.h"
 
 #include "Role.h"
 
 #import "mozAccessible.h"
 #import "mozActionElements.h"
+#import "mozHTMLAccessible.h"
 #import "mozTextAccessible.h"
 
 using namespace mozilla::a11y;
 
 nsAccessibleWrap::
   nsAccessibleWrap(nsIContent *aContent, nsIWeakReference *aShell) :
   nsAccessible(aContent, aShell), mNativeObject(nil),
   mNativeInited(false)
@@ -103,20 +104,22 @@ nsAccessibleWrap::GetNativeType ()
              [mozButtonAccessible class];
     }
     
     case roles::CHECKBUTTON:
       return [mozCheckboxAccessible class];
       
     case roles::AUTOCOMPLETE:
       return [mozComboboxAccessible class];
-      
+
+    case roles::HEADING:
+      return [mozHeadingAccessible class];
+
     case roles::ENTRY:
     case roles::STATICTEXT:
-    case roles::HEADING:
     case roles::LABEL:
     case roles::CAPTION:
     case roles::ACCEL_LABEL:
     case roles::TEXT_LEAF:
       // normal textfield (static or editable)
       return [mozTextAccessible class]; 
       
     case roles::COMBOBOX:
--- a/accessible/src/mac/nsRoleMap.h
+++ b/accessible/src/mac/nsRoleMap.h
@@ -142,17 +142,17 @@ static const NSString* AXRoles [] = {
   NSAccessibilityGroupRole,                     // ROLE_FOOTER
   NSAccessibilityGroupRole,                     // ROLE_PARAGRAPH
   @"AXRuler",                                   // ROLE_RULER. 10.4+ only, so we re-define the constant.
   NSAccessibilityComboBoxRole,                  // ROLE_AUTOCOMPLETE
   NSAccessibilityTextFieldRole,                 // ROLE_EDITBAR
   NSAccessibilityTextFieldRole,                 // ROLE_ENTRY
   NSAccessibilityStaticTextRole,                // ROLE_CAPTION
   @"AXWebArea",                                 // ROLE_DOCUMENT_FRAME
-  NSAccessibilityStaticTextRole,                // ROLE_HEADING
+  @"AXHeading",                                 // ROLE_HEADING
   NSAccessibilityGroupRole,                     // ROLE_PAGE
   NSAccessibilityGroupRole,                     // ROLE_SECTION
   NSAccessibilityUnknownRole,                   // ROLE_REDUNDANT_OBJECT
   NSAccessibilityGroupRole,                     // ROLE_FORM
   NSAccessibilityUnknownRole,                   // ROLE_IME
   NSAccessibilityUnknownRole,                   // ROLE_APP_ROOT. unused on OS X
   NSAccessibilityMenuItemRole,                  // ROLE_PARENT_MENUITEM
   NSAccessibilityGroupRole,                     // ROLE_CALENDAR
--- a/browser/base/content/browser-appmenu.inc
+++ b/browser/base/content/browser-appmenu.inc
@@ -171,17 +171,18 @@
             <menuitem id="appmenu_printSetup"
                       label="&printSetupCmd.label;"
                       command="cmd_pageSetup"/>
           </menupopup>
       </splitmenu>
       <menuseparator class="appmenu-menuseparator"/>
       <menu id="appmenu_webDeveloper"
             label="&appMenuWebDeveloper.label;">
-        <menupopup id="appmenu_webDeveloper_popup">
+        <menupopup id="appmenu_webDeveloper_popup"
+                   onpopupshowing="onWebDeveloperMenuShowing();">
           <menuitem id="appmenu_webConsole"
                     label="&webConsoleCmd.label;"
                     type="checkbox"
                     command="Tools:WebConsole"
                     key="key_webConsole"/>
           <menuitem id="appmenu_pageInspect"
                     hidden="true"
                     label="&inspectMenu.label;"
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -526,17 +526,18 @@
                         accesskey="&syncSyncNowItem.accesskey;"
                         observes="sync-syncnow-state"
                         oncommand="gSyncUI.doSync(event);"/>
 #endif
               <menuseparator id="devToolsSeparator"/>
               <menu id="webDeveloperMenu"
                     label="&webDeveloperMenu.label;"
                     accesskey="&webDeveloperMenu.accesskey;">
-                <menupopup id="menuWebDeveloperPopup">
+                <menupopup id="menuWebDeveloperPopup"
+                           onpopupshowing="onWebDeveloperMenuShowing();">
                   <menuitem id="webConsole"
                             type="checkbox"
                             label="&webConsoleCmd.label;"
                             accesskey="&webConsoleCmd.accesskey;"
                             key="key_webConsole"
                             command="Tools:WebConsole"/>
                   <menuitem id="menu_pageinspect"
                             type="checkbox"
new file mode 100644
--- /dev/null
+++ b/browser/base/content/browser-thumbnails.js
@@ -0,0 +1,137 @@
+#ifdef 0
+/* 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/. */
+#endif
+
+/**
+ * Keeps thumbnails of open web pages up-to-date.
+ */
+let gBrowserThumbnails = {
+  _captureDelayMS: 2000,
+
+  /**
+   * Map of capture() timeouts assigned to their browsers.
+   */
+  _timeouts: null,
+
+  /**
+   * Cache for the PageThumbs module.
+   */
+  _pageThumbs: null,
+
+  /**
+   * List of tab events we want to listen for.
+   */
+  _tabEvents: ["TabClose", "TabSelect"],
+
+  init: function Thumbnails_init() {
+    gBrowser.addTabsProgressListener(this);
+
+    this._tabEvents.forEach(function (aEvent) {
+      gBrowser.tabContainer.addEventListener(aEvent, this, false);
+    }, this);
+
+    this._timeouts = new WeakMap();
+
+    XPCOMUtils.defineLazyModuleGetter(this, "_pageThumbs",
+      "resource:///modules/PageThumbs.jsm", "PageThumbs");
+  },
+
+  uninit: function Thumbnails_uninit() {
+    gBrowser.removeTabsProgressListener(this);
+
+    this._tabEvents.forEach(function (aEvent) {
+      gBrowser.tabContainer.removeEventListener(aEvent, this, false);
+    }, this);
+
+    this._timeouts = null;
+    this._pageThumbs = null;
+  },
+
+  handleEvent: function Thumbnails_handleEvent(aEvent) {
+    switch (aEvent.type) {
+      case "scroll":
+        let browser = aEvent.currentTarget;
+        if (this._timeouts.has(browser))
+          this._delayedCapture(browser);
+        break;
+      case "TabSelect":
+        this._delayedCapture(aEvent.target.linkedBrowser);
+        break;
+      case "TabClose": {
+        this._clearTimeout(aEvent.target.linkedBrowser);
+        break;
+      }
+    }
+  },
+
+  /**
+   * State change progress listener for all tabs.
+   */
+  onStateChange: function Thumbnails_onStateChange(aBrowser, aWebProgress,
+                                                   aRequest, aStateFlags, aStatus) {
+    if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+        aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)
+      this._delayedCapture(aBrowser);
+  },
+
+  _capture: function Thumbnails_capture(aBrowser) {
+    if (this._shouldCapture(aBrowser)) {
+      let canvas = this._pageThumbs.capture(aBrowser.contentWindow);
+      this._pageThumbs.store(aBrowser.currentURI.spec, canvas);
+    }
+  },
+
+  _delayedCapture: function Thumbnails_delayedCapture(aBrowser) {
+    if (this._timeouts.has(aBrowser))
+      clearTimeout(this._timeouts.get(aBrowser));
+    else
+      aBrowser.addEventListener("scroll", this, true);
+
+    let timeout = setTimeout(function () {
+      this._clearTimeout(aBrowser);
+      this._capture(aBrowser);
+    }.bind(this), this._captureDelayMS);
+
+    this._timeouts.set(aBrowser, timeout);
+  },
+
+  _shouldCapture: function Thumbnails_shouldCapture(aBrowser) {
+    let doc = aBrowser.contentDocument;
+
+    // FIXME Bug 720575 - Don't capture thumbnails for SVG or XML documents as
+    //       that currently regresses Talos SVG tests.
+    if (doc instanceof SVGDocument || doc instanceof XMLDocument)
+      return false;
+
+    // There's no point in taking screenshot of loading pages.
+    if (aBrowser.docShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE)
+      return false;
+
+    // Don't take screenshots of about: pages.
+    if (aBrowser.currentURI.schemeIs("about"))
+      return false;
+
+    let channel = aBrowser.docShell.currentDocumentChannel;
+
+    try {
+      // If the channel is a nsIHttpChannel get its http status code.
+      let httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
+
+      // Continue only if we have a 2xx status code.
+      return Math.floor(httpChannel.responseStatus / 100) == 2;
+    } catch (e) {
+      // Not a http channel, we just assume a success status code.
+      return true;
+    }
+  },
+
+  _clearTimeout: function Thumbnails_clearTimeout(aBrowser) {
+    if (this._timeouts.has(aBrowser)) {
+      aBrowser.removeEventListener("scroll", this, false);
+      clearTimeout(this._timeouts.get(aBrowser));
+      this._timeouts.delete(aBrowser);
+    }
+  }
+};
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -191,16 +191,17 @@ let gInitialPages = [
   "about:privatebrowsing",
   "about:sessionrestore"
 ];
 
 #include browser-fullZoom.js
 #include browser-places.js
 #include browser-tabPreviews.js
 #include browser-tabview.js
+#include browser-thumbnails.js
 
 #ifdef MOZ_SERVICES_SYNC
 #include browser-syncui.js
 #endif
 
 XPCOMUtils.defineLazyGetter(this, "Win7Features", function () {
 #ifdef XP_WIN
   const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
@@ -1496,16 +1497,18 @@ function prepareForStartup() {
   gBrowser.addEventListener("MozApplicationManifest",
                             OfflineApps, false);
 
   // setup simple gestures support
   gGestureSupport.init(true);
 }
 
 function delayedStartup(isLoadingBlank, mustLoadSidebar) {
+  Cu.import("resource:///modules/TelemetryTimestamps.jsm");
+  TelemetryTimestamps.add("delayedStartupStarted");
   gDelayedStartupTimeoutId = null;
 
   Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
   Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
   Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false);
   Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
   Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
   Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
@@ -1698,16 +1701,17 @@ function delayedStartup(isLoadingBlank, 
   if (document.mozFullScreen)
     onMozFullScreenChange();
 
 #ifdef MOZ_SERVICES_SYNC
   // initialize the sync UI
   gSyncUI.init();
 #endif
 
+  gBrowserThumbnails.init();
   TabView.init();
 
   setUrlAndSearchBarWidthForConditionalForwardButton();
   window.addEventListener("resize", function resizeHandler(event) {
     if (event.target == window)
       setUrlAndSearchBarWidthForConditionalForwardButton();
   });
 
@@ -1760,16 +1764,17 @@ function delayedStartup(isLoadingBlank, 
                                              Ci.nsIPrefLocalizedString).data)
     document.getElementById("appmenu_charsetMenu").hidden = true;
 #endif
 
   window.addEventListener("mousemove", MousePosTracker, false);
   window.addEventListener("dragover", MousePosTracker, false);
 
   Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
+  TelemetryTimestamps.add("delayedStartupFinished");
 }
 
 function BrowserShutdown() {
   // In certain scenarios it's possible for unload to be fired before onload,
   // (e.g. if the window is being closed after browser.js loads but before the
   // load completes). In that case, there's nothing to do here.
   if (!gStartupRan)
     return;
@@ -1819,16 +1824,17 @@ function BrowserShutdown() {
   } else {
     if (Win7Features)
       Win7Features.onCloseWindow();
 
     gPrefService.removeObserver(ctrlTab.prefName, ctrlTab);
     gPrefService.removeObserver(allTabs.prefName, allTabs);
     ctrlTab.uninit();
     TabView.uninit();
+    gBrowserThumbnails.uninit();
 
     try {
       FullZoom.destroy();
     }
     catch(ex) {
       Components.utils.reportError(ex);
     }
 
@@ -3104,30 +3110,23 @@ function FillInHTMLTooltip(tipElement)
     tipElement = tipElement.parentNode;
   }
 
   var tipNode = document.getElementById("aHTMLTooltip");
   tipNode.style.direction = direction;
 
   [titleText, XLinkTitleText, SVGTitleText].forEach(function (t) {
     if (t && /\S/.test(t)) {
-
-      // Per HTML 4.01 6.2 (CDATA section), literal CRs and tabs should be
-      // replaced with spaces, and LFs should be removed entirely.
-      // XXX Bug 322270: We don't preserve the result of entities like &#13;,
-      // which should result in a line break in the tooltip, because we can't
-      // distinguish that from a literal character in the source by this point.
-      t = t.replace(/[\r\t]/g, ' ');
-      t = t.replace(/\n/g, '');
+      // Make CRLF and CR render one line break each.  
+      t = t.replace(/\r\n?/g, '\n');
 
       tipNode.setAttribute("label", t);
       retVal = true;
     }
   });
-
   return retVal;
 }
 
 var browserDragAndDrop = {
   canDropLink: function (aEvent) Services.droppedLinkHandler.canDropLink(aEvent, true),
 
   dragOver: function (aEvent)
   {
@@ -4784,16 +4783,17 @@ var XULBrowserWindow = {
       this.asyncUpdateUI();
   },
 
   asyncUpdateUI: function () {
     FeedHandler.updateFeeds();
   },
 
   hideChromeForLocation: function(aLocation) {
+    aLocation = aLocation.toLowerCase();
     return this.inContentWhitelist.some(function(aSpec) {
       return aSpec == aLocation;
     });
   },
 
   onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) {
     this.status = aMessage;
     this.updateStatusField();
@@ -5196,17 +5196,17 @@ nsBrowserAccess.prototype = {
         // Pass all params to openDialog to ensure that "url" isn't passed through
         // loadOneOrMoreURIs, which splits based on "|"
         newWindow = openDialog(getBrowserURL(), "_blank", "all,dialog=no", url, null, null, null);
         break;
       case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB :
         let win, needToFocusWin;
 
         // try the current window.  if we're in a popup, fall back on the most recent browser window
-        if (!window.document.documentElement.getAttribute("chromehidden"))
+        if (window.toolbar.visible)
           win = window;
         else {
           win = Cc["@mozilla.org/browser/browserglue;1"]
                   .getService(Ci.nsIBrowserGlue)
                   .getMostRecentBrowserWindow();
           needToFocusWin = true;
         }
 
@@ -5968,22 +5968,22 @@ function handleDroppedLink(event, url, n
 };
 
 function MultiplexHandler(event)
 { try {
     var node = event.target;
     var name = node.getAttribute('name');
 
     if (name == 'detectorGroup') {
-        SetForcedDetector(true);
+        BrowserCharsetReload();
         SelectDetector(event, false);
     } else if (name == 'charsetGroup') {
         var charset = node.getAttribute('id');
         charset = charset.substring('charset.'.length, charset.length)
-        SetForcedCharset(charset);
+        BrowserSetForcedCharacterSet(charset);
     } else if (name == 'charsetCustomize') {
         //do nothing - please remove this else statement, once the charset prefs moves to the pref window
     } else {
         SetForcedCharset(node.getAttribute('id'));
     }
     } catch(ex) { alert(ex); }
 }
 
@@ -6004,42 +6004,29 @@ function SelectDetector(event, doReload)
         if (doReload)
           window.content.location.reload();
     }
     catch (ex) {
         dump("Failed to set the intl.charset.detector preference.\n");
     }
 }
 
-function SetForcedDetector(doReload)
-{
-    BrowserSetForcedDetector(doReload);
-}
-
-function SetForcedCharset(charset)
-{
-    BrowserSetForcedCharacterSet(charset);
-}
-
 function BrowserSetForcedCharacterSet(aCharset)
 {
-  var docCharset = gBrowser.docShell.QueryInterface(Ci.nsIDocCharset);
-  docCharset.charset = aCharset;
+  gBrowser.docShell.charset = aCharset;
   // Save the forced character-set
   PlacesUtils.history.setCharsetForURI(getWebNavigation().currentURI, aCharset);
+  BrowserCharsetReload();
+}
+
+function BrowserCharsetReload()
+{
   BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
 }
 
-function BrowserSetForcedDetector(doReload)
-{
-  gBrowser.documentCharsetInfo.forcedDetector = true;
-  if (doReload)
-    BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
-}
-
 function charsetMenuGetElement(parent, id) {
   return parent.getElementsByAttribute("id", id)[0];
 }
 
 function UpdateCurrentCharset(target) {
     // extract the charset from DOM
     var wnd = document.commandDispatcher.focusedWindow;
     if ((window == wnd) || (wnd == null)) wnd = window.content;
@@ -8917,19 +8904,20 @@ var TabContextMenu = {
 
     // Hide "Move to Group" if it's a pinned tab.
     document.getElementById("context_tabViewMenu").hidden =
       (this.contextTab.pinned || !TabView.firstUseExperienced);
   }
 };
 
 XPCOMUtils.defineLazyGetter(this, "HUDConsoleUI", function () {
-  Cu.import("resource:///modules/HUDService.jsm");
+  let tempScope = {};
+  Cu.import("resource:///modules/HUDService.jsm", tempScope);
   try {
-    return HUDService.consoleUI;
+    return tempScope.HUDService.consoleUI;
   }
   catch (ex) {
     Components.utils.reportError(ex);
   }
 });
 
 // Prompt user to restart the browser in safe mode 
 function safeModeRestart()
@@ -9071,16 +9059,21 @@ var StyleEditor = {
     let chromeWindow = Services.ww.openWindow(null, CHROME_URL, "_blank",
                                               CHROME_WINDOW_FLAGS,
                                               contentWindow);
     chromeWindow.focus();
     return chromeWindow;
   }
 };
 
+function onWebDeveloperMenuShowing() {
+  document.getElementById("Tools:WebConsole").setAttribute("checked", HUDConsoleUI.getOpenHUD() != null);
+}
+
+
 XPCOMUtils.defineLazyGetter(window, "gShowPageResizers", function () {
 #ifdef XP_WIN
   // Only show resizers on Windows 2000 and XP
   let sysInfo = Components.classes["@mozilla.org/system-info;1"]
                           .getService(Components.interfaces.nsIPropertyBag2);
   return parseFloat(sysInfo.getProperty("version")) < 6;
 #else
   return false;
@@ -9145,15 +9138,16 @@ var MousePosTracker = {
       if (listener.onMouseEnter)
         listener.onMouseEnter();
     } else {
       if (listener.onMouseLeave)
         listener.onMouseLeave();
     }
   }
 };
+
 function focusNextFrame(event) {
   let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
   let dir = event.shiftKey ? fm.MOVEFOCUS_BACKWARDDOC : fm.MOVEFOCUS_FORWARDDOC;
   let element = fm.moveFocus(window, null, dir, fm.FLAG_BYKEY);
   if (element.ownerDocument == document)
     focusAndSelectUrlBar();
 }
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2370,20 +2370,16 @@
       <property name="contentViewerEdit"
                 onget="return this.mCurrentBrowser.contentViewerEdit;"
                 readonly="true"/>
 
       <property name="contentViewerFile"
                 onget="return this.mCurrentBrowser.contentViewerFile;"
                 readonly="true"/>
 
-      <property name="documentCharsetInfo"
-                onget="return this.mCurrentBrowser.documentCharsetInfo;"
-                readonly="true"/>
-
       <property name="contentDocument"
                 onget="return this.mCurrentBrowser.contentDocument;"
                 readonly="true"/>
 
       <property name="contentTitle"
                 onget="return this.mCurrentBrowser.contentTitle;"
                 readonly="true"/>
 
@@ -3806,59 +3802,44 @@
         this.style.MozUserFocus = '';
       </handler>
     </handlers>
   </binding>
 
   <binding id="tabbrowser-alltabs-popup"
            extends="chrome://global/content/bindings/popup.xml#popup">
     <implementation implements="nsIDOMEventListener">
-      <method name="_menuItemOnCommand">
-        <parameter name="aEvent"/>
-        <body><![CDATA[
-          gBrowser.selectedTab = aEvent.target.tab;
-        ]]></body>
-      </method>
-
       <method name="_tabOnAttrModified">
         <parameter name="aEvent"/>
         <body><![CDATA[
           var tab = aEvent.target;
-          this._setMenuitemAttributes(tab.mCorrespondingMenuitem, tab);
+          if (tab.mCorrespondingMenuitem)
+            this._setMenuitemAttributes(tab.mCorrespondingMenuitem, tab);
         ]]></body>
       </method>
 
       <method name="_tabOnTabClose">
         <parameter name="aEvent"/>
         <body><![CDATA[
-          var menuItem = aEvent.target.mCorrespondingMenuitem;
-          if (menuItem)
-            this.removeChild(menuItem);
+          var tab = aEvent.target;
+          if (tab.mCorrespondingMenuitem)
+            this.removeChild(tab.mCorrespondingMenuitem);
         ]]></body>
       </method>
 
       <method name="handleEvent">
         <parameter name="aEvent"/>
         <body><![CDATA[
-          if (!aEvent.isTrusted)
-            return;
-
           switch (aEvent.type) {
-            case "command":
-              this._menuItemOnCommand(aEvent);
-              break;
             case "TabAttrModified":
               this._tabOnAttrModified(aEvent);
               break;
             case "TabClose":
               this._tabOnTabClose(aEvent);
               break;
-            case "TabOpen":
-              this._createTabMenuItem(aEvent.originalTarget);
-              break;
             case "scroll":
               this._updateTabsVisibilityStatus();
               break;
           }
         ]]></body>
       </method>
 
       <method name="_updateTabsVisibilityStatus">
@@ -3869,18 +3850,16 @@
             return;
 
           var tabstripBO = tabContainer.mTabstrip.scrollBoxObject;
           for (var i = 0; i < this.childNodes.length; i++) {
             let curTab = this.childNodes[i].tab;
             if (!curTab) // "Tab Groups" menuitem and its menuseparator
               continue;
             let curTabBO = curTab.boxObject;
-            if (!curTabBO) // "Tabs From Other Computers" menuitem
-              continue;
             if (curTabBO.screenX >= tabstripBO.screenX &&
                 curTabBO.screenX + curTabBO.width <= tabstripBO.screenX + tabstripBO.width)
               this.childNodes[i].setAttribute("tabIsVisible", "true");
             else
               this.childNodes[i].removeAttribute("tabIsVisible");
           }
         ]]></body>
       </method>
@@ -3891,24 +3870,20 @@
           var menuItem = document.createElementNS(
             "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
             "menuitem");
 
           menuItem.setAttribute("class", "menuitem-iconic alltabs-item menuitem-with-favicon");
 
           this._setMenuitemAttributes(menuItem, aTab);
 
-          // Keep some attributes of the menuitem in sync with its
-          // corresponding tab (e.g. the tab label)
           aTab.mCorrespondingMenuitem = menuItem;
           menuItem.tab = aTab;
-          menuItem.addEventListener("command", this, false);
 
           this.appendChild(menuItem);
-          return menuItem;
         ]]></body>
       </method>
 
       <method name="_setMenuitemAttributes">
         <parameter name="aMenuitem"/>
         <parameter name="aTab"/>
         <body><![CDATA[
           aMenuitem.setAttribute("label", aTab.label);
@@ -3933,46 +3908,43 @@
             aMenuitem.removeAttribute("selected");
         ]]></body>
       </method>
     </implementation>
 
     <handlers>
       <handler event="popupshowing">
       <![CDATA[
-        // set up the menu popup
         var tabcontainer = gBrowser.tabContainer;
-        let tabs = gBrowser.visibleTabs;
 
         // Listen for changes in the tab bar.
-        tabcontainer.addEventListener("TabOpen", this, false);
         tabcontainer.addEventListener("TabAttrModified", this, false);
         tabcontainer.addEventListener("TabClose", this, false);
         tabcontainer.mTabstrip.addEventListener("scroll", this, false);
 
+        let tabs = gBrowser.visibleTabs;
         for (var i = 0; i < tabs.length; i++) {
-          this._createTabMenuItem(tabs[i]);
+          if (!tabs[i].pinned)
+            this._createTabMenuItem(tabs[i]);
         }
         this._updateTabsVisibilityStatus();
       ]]></handler>
 
       <handler event="popuphidden">
       <![CDATA[
         // clear out the menu popup and remove the listeners
         for (let i = this.childNodes.length - 1; i > 0; i--) {
           let menuItem = this.childNodes[i];
           if (menuItem.tab) {
-            menuItem.removeEventListener("command", this, false);
             menuItem.tab.mCorrespondingMenuitem = null;
             this.removeChild(menuItem);
           }
         }
         var tabcontainer = gBrowser.tabContainer;
         tabcontainer.mTabstrip.removeEventListener("scroll", this, false);
-        tabcontainer.removeEventListener("TabOpen", this, false);
         tabcontainer.removeEventListener("TabAttrModified", this, false);
         tabcontainer.removeEventListener("TabClose", this, false);
       ]]></handler>
 
       <handler event="DOMMenuItemActive">
       <![CDATA[
         var tab = event.target.tab;
         if (tab) {
@@ -3983,16 +3955,21 @@
         }
       ]]></handler>
 
       <handler event="DOMMenuItemInactive">
       <![CDATA[
         XULBrowserWindow.setOverLink("", null);
       ]]></handler>
 
+      <handler event="command"><![CDATA[
+        if (event.target.tab)
+          gBrowser.selectedTab = event.target.tab;
+      ]]></handler>
+
     </handlers>
   </binding>
 
   <binding id="statuspanel" display="xul:hbox">
     <content>
       <xul:hbox class="statuspanel-inner">
         <xul:label class="statuspanel-label"
                    role="status"
--- a/browser/base/content/test/browser_bug329212.js
+++ b/browser/base/content/test/browser_bug329212.js
@@ -6,26 +6,26 @@ function test () {
 
     let doc = gBrowser.contentDocument;
     let tooltip = document.getElementById("aHTMLTooltip");
 
     ok(FillInHTMLTooltip(doc.getElementById("svg1"), "should get title"));
     is(tooltip.getAttribute("label"), "This is a non-root SVG element title");
 
     ok(FillInHTMLTooltip(doc.getElementById("text1"), "should get title"));
-    is(tooltip.getAttribute("label"), "    This            is a title    ");
+    is(tooltip.getAttribute("label"), "\n\n\n    This            is a title\n\n    ");
 
     ok(!FillInHTMLTooltip(doc.getElementById("text2"), "should not get title"));
 
     ok(!FillInHTMLTooltip(doc.getElementById("text3"), "should not get title"));
 
     ok(FillInHTMLTooltip(doc.getElementById("link1"), "should get title"));
-    is(tooltip.getAttribute("label"), "      This is a title    ");
+    is(tooltip.getAttribute("label"), "\n      This is a title\n    ");
     ok(FillInHTMLTooltip(doc.getElementById("text4"), "should get title"));
-    is(tooltip.getAttribute("label"), "      This is a title    ");
+    is(tooltip.getAttribute("label"), "\n      This is a title\n    ");
 
     ok(!FillInHTMLTooltip(doc.getElementById("link2"), "should not get title"));
 
     ok(FillInHTMLTooltip(doc.getElementById("link3"), "should get title"));
     ok(tooltip.getAttribute("label") != "");
 
     ok(FillInHTMLTooltip(doc.getElementById("link4"), "should get title"));
     is(tooltip.getAttribute("label"), "This is an xlink:title attribute");
--- a/browser/base/content/test/browser_clearplugindata.js
+++ b/browser/base/content/test/browser_clearplugindata.js
@@ -2,18 +2,20 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Test clearing plugin data using sanitize.js.
 const testURL1 = "http://mochi.test:8888/browser/browser/base/content/test/browser_clearplugindata.html";
 const testURL2 = "http://mochi.test:8888/browser/browser/base/content/test/browser_clearplugindata_noage.html";
 
+let tempScope = {};
 Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
-                                           .loadSubScript("chrome://browser/content/sanitize.js");
+                                           .loadSubScript("chrome://browser/content/sanitize.js", tempScope);
+let Sanitizer = tempScope.Sanitizer;
 
 const pluginHostIface = Ci.nsIPluginHost;
 var pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
 pluginHost.QueryInterface(pluginHostIface);
 
 var pluginTag;
 var s;
 
--- a/browser/base/content/test/browser_disablechrome.js
+++ b/browser/base/content/test/browser_disablechrome.js
@@ -99,17 +99,16 @@ function test() {
 
 function end_test() {
   gBrowser.removeTab(gNewTab);
   finish();
 }
 
 function test_url(aURL, aCanHide, aNextTest) {
   is_chrome_visible();
-
   info("Page load");
   load_page(aURL, aCanHide, function() {
     info("Switch away");
     gBrowser.selectedTab = gOldTab;
     is_chrome_visible();
 
     info("Switch back");
     gBrowser.selectedTab = gNewTab;
@@ -158,10 +157,35 @@ function run_chrome_about_test() {
 function run_http_test_2() {
   info("HTTP tests");
   test_url(HTTPSRC + "disablechrome.html", false, run_chrome_about_test_2);
 }
 
 // Should not hide the chrome
 function run_chrome_about_test_2() {
   info("Chrome about: tests");
-  test_url("about:addons", true, end_test);
+  test_url("about:addons", true, run_http_test3);
+}
+
+function run_http_test3() {
+  info("HTTP tests");
+  test_url(HTTPSRC + "disablechrome.html", false, run_chrome_about_test_3);
 }
+
+// Should not hide the chrome
+function run_chrome_about_test_3() {
+  info("Chrome about: tests");
+  test_url("about:Addons", true, function(){
+    info("Tabs on top");
+    TabsOnTop.enabled = true;
+    run_http_test4();
+  });
+}
+
+function run_http_test4() {
+  info("HTTP tests");
+  test_url(HTTPSRC + "disablechrome.html", false, run_chrome_about_test_4);
+}
+
+function run_chrome_about_test_4() {
+  info("Chrome about: tests");
+  test_url("about:Addons", true, end_test);
+}
--- a/browser/base/content/test/browser_sanitize-passwordDisabledHosts.js
+++ b/browser/base/content/test/browser_sanitize-passwordDisabledHosts.js
@@ -1,13 +1,15 @@
 // Bug 474792 - Clear "Never remember passwords for this site" when
 // clearing site-specific settings in Clear Recent History dialog
 
+let tempScope = {};
 Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
-                                           .loadSubScript("chrome://browser/content/sanitize.js");
+                                           .loadSubScript("chrome://browser/content/sanitize.js", tempScope);
+let Sanitizer = tempScope.Sanitizer;
 
 function test() {
 
   var pwmgr = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
 
   // Add a disabled host
   pwmgr.setLoginSavingEnabled("http://example.com", false);
   
--- a/browser/base/content/test/browser_sanitize-sitepermissions.js
+++ b/browser/base/content/test/browser_sanitize-sitepermissions.js
@@ -1,12 +1,14 @@
 // Bug 380852 - Delete permission manager entries in Clear Recent History
 
+let tempScope = {};
 Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
-                                           .loadSubScript("chrome://browser/content/sanitize.js");
+                                           .loadSubScript("chrome://browser/content/sanitize.js", tempScope);
+let Sanitizer = tempScope.Sanitizer;
 
 function test() {
   
   // Add a permission entry
   var pm = Services.perms;
   pm.add(makeURI("http://example.com"), "testing", pm.ALLOW_ACTION);
   
   // Sanity check
--- a/browser/base/content/test/browser_sanitize-timespans.js
+++ b/browser/base/content/test/browser_sanitize-timespans.js
@@ -1,17 +1,19 @@
 // Bug 453440 - Test the timespan-based logic of the sanitizer code
 var now_uSec = Date.now() * 1000;
 
 const dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
 const bhist = Cc["@mozilla.org/browser/global-history;2"].getService(Ci.nsIBrowserHistory);
 const formhist = Cc["@mozilla.org/satchel/form-history;1"].getService(Ci.nsIFormHistory2);
 
+let tempScope = {};
 Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
-                                           .loadSubScript("chrome://browser/content/sanitize.js");
+                                           .loadSubScript("chrome://browser/content/sanitize.js", tempScope);
+let Sanitizer = tempScope.Sanitizer;
 
 function test() {
   
   var hoursSinceMidnight = new Date().getHours();
   var minutesSinceMidnight = hoursSinceMidnight * 60 + new Date().getMinutes();
 
   setupHistory();
   setupFormHistory();
--- a/browser/base/content/test/browser_sanitizeDialog.js
+++ b/browser/base/content/test/browser_sanitizeDialog.js
@@ -45,19 +45,20 @@
  * browser/base/content/test/browser_sanitize-timespans.js does that.  This
  * test checks the UI of the dialog and makes sure it's correctly connected to
  * the sanitize timespan code.
  *
  * Some of this code, especially the history creation parts, was taken from
  * browser/base/content/test/browser_sanitize-timespans.js.
  */
 
-Cc["@mozilla.org/moz/jssubscript-loader;1"].
-  getService(Ci.mozIJSSubScriptLoader).
-  loadSubScript("chrome://browser/content/sanitize.js");
+let tempScope = {};
+Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
+                                           .loadSubScript("chrome://browser/content/sanitize.js", tempScope);
+let Sanitizer = tempScope.Sanitizer;
 
 const dm = Cc["@mozilla.org/download-manager;1"].
            getService(Ci.nsIDownloadManager);
 const formhist = Cc["@mozilla.org/satchel/form-history;1"].
                  getService(Ci.nsIFormHistory2);
 
 // Add tests here.  Each is a function that's called by doNextTest().
 var gAllTests = [
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -51,17 +51,17 @@ function getBrowserURL()
   return "chrome://browser/content/browser.xul";
 }
 
 function getTopWin(skipPopups) {
   // If this is called in a browser window, use that window regardless of
   // whether it's the frontmost window, since commands can be executed in
   // background windows (bug 626148).
   if (top.document.documentElement.getAttribute("windowtype") == "navigator:browser" &&
-      (!skipPopups || !top.document.documentElement.getAttribute("chromehidden")))
+      (!skipPopups || top.toolbar.visible))
     return top;
 
   if (skipPopups) {
     return Components.classes["@mozilla.org/browser/browserglue;1"]
                      .getService(Components.interfaces.nsIBrowserGlue)
                      .getMostRecentBrowserWindow();
   }
   return Services.wm.getMostRecentWindow("navigator:browser");
@@ -201,17 +201,17 @@ function openLinkIn(url, where, params) 
     saveURL(url, null, null, true, null, aReferrerURI);
     return;
   }
   const Cc = Components.classes;
   const Ci = Components.interfaces;
 
   var w = getTopWin();
   if ((where == "tab" || where == "tabshifted") &&
-      w && w.document.documentElement.getAttribute("chromehidden")) {
+      w && !w.toolbar.visible) {
     w = getTopWin(true);
     aRelatedToCurrent = false;
   }
 
   if (!w || where == "window") {
     var sa = Cc["@mozilla.org/supports-array;1"].
              createInstance(Ci.nsISupportsArray);
 
index 85eb545601f66f0aada8d7d02144ae7627ef53aa..c09d6c1344f7b281220ca2e9c3a55b15e97c8472
GIT binary patch
literal 15364
zc%1E-&2Jk;6u{p&ZfqxRviWLBQ_4b=CWzD@P0}C>DUK6pB3dQFX_}}B>Du1d+t|C-
z?mB4+0i#Mt91sVPP?5M4i4#Z&^}+!aR1VFFONHox@)r<=0}=-$^JaIP*p5kPL8J=v
zlV;!S%$v9K-h6n+3jidNO9TN#0C0mX1us;^&C(7WOg3O5*dDYPHG?<d>XtYh4u`|x
z>^sC3?)v@h28Y9W459mj$Yhwwye$c<cQENJX(KQxG8twvZ%e}T985ZybTcV1DKZ&m
zGH*+^X%Tk#AneJ6oluCtq{w9Wp<L#0{#V#%cX2$e#bfER^ZSRG+=W6cY-#u+FP|M{
z8s5pc8Ns!G3!re@t{oqlo``8$n%K9o6(h@(zldeiieUx<KM+Ta^H8nJjd<rSDr!Po
ziD)@BVO)x)w3VomkYkcQZ{<zN%o!0$w`wLOGZka`DSQUz=a}|IC1Iu(g!a|dzB8wL
z#b96{u+}TC_6G-g#lGO++8TYB@U6S;8Q%+IQ_|WSZ|2{!{Vwh!aJV8M*KH3LSCqh3
zKo-aq@^+0!iL1*@%hFg*87WQ2n{K;Ai6=GIhVDjAmaLlPVvUITe#onuD#tW4V_7R|
z0wjQJwO|o+zFUmo&q8A{hMu~5@8O0<p{cp0we85!C)$s79B)6dAhg7!_)=1*VI9%Z
znjT5#<QXNdsZr%Mc|I-0<n)9Z(M(gz%$V|3lNK)s%@wU)krS_O7foHuqE2gt&RJcS
z)k{fTNlYv*8nQW4lAoQ!z^sg$a%Nmz)X@I2BCkwjO+{0UX<0W=&Puv2sYy9<t<3az
zMoP-VX+<&?ghtB<_I*;9sR8Q&(X13#)MV$-racgy;mdZ$(;4nzMa4S7v8`4d-mQ#V
zfGIi%6HSiq=sjNXC#7~cr)#=Yb*ZfX@}MyG92^30bvJBx*_Tz5RrPnrb$Wf{tNG$x
z0l~TGs8Fi+Z9j)jM4VRZ8%Kw*rpeC#`Z2KIP**qh|7pAXp^|2y%APjB2~07NAP!5A
z1RXTUfr?Yl2U(O!XvF$j7{*dsr=w;wbofhb`YUa=K%;+JOXo7O;V(w8m87T(ctx+g
z=jpJSnvoL6Mk}1Wd;iKB#ubO;+y`}bYhNPsC^1Do`Teu?jXOV;$JHZN27gpGmP{?n
zVtV)f&9Ap(`p1)2Oue)}nBD<+zYBdhRq8_Mf`RimWSDX`C95H3E>zn41~>wBWl{8D
zj5AhzkD>MR)(9vVaTDteNZ}jL7>r`9Ma#IK8TT{e{u1LYK>Pr0*sBKWA5HGg@GSGr
z4bQ?5+M0$1w0#{uf=}T~xDEH<0sKZ>q=g(OUF0P3ljli@jF5BW0+}W=WZv23JZYP7
z6t~Bx$^+;0LvedD?adFoc1c!=oA!o9e;2Ilp6lt>_y6vCdhb;ExF4|fG(%m$D|+RM
znpl{WtHyP+BWkTD$35{MuP4Wk`Spz(pO?pVul2OPxpoJ3t|#mdrh0(abLc}4b7AkL
zwA)G&zy(oj1<~>($d9^y=<GndRmSnZf0+pe27|rgz`z;$GBY?B=oS0>@MXq>2;V&H
z%S>hDB%GDm^7lQU@+Kw^i$oK=ggcXt(OkrKSO12EIJHnH*z<$K;cz&QH8r*n>Hq&P
z{`L2N4u^AqA<oh0Xau%C2=0I$qeM)Xiw$7+4*?GQ53n9q7J%(`VJ7po+^3HL{QS?)
b|NQ*V;cz${4u`|xa5x+ehr{7;4k&*D(Qxb<
index ea1c05db0dc9a467a75998c7673ce327a8b01971..360fd08066b0a0f695363cabafc776db28210f48
GIT binary patch
literal 15364
zc%1E7-%le&9RF@vY_UC9ehD0xbA{k>9%+lfku(}=4?T$9ffhN)amQ`jrQKt@+w5-X
zAyy&A2NU1U_~wK72dIhhMU5I?z{L1sjQ8M=H=oX&F}`W)cXwuKDHsmCh~CX7nSN$>
zX1+V$&&+prrV9XEET8HJ5COmkgoOa?3Li~7;Gx`riNp4wCu}o#F|xZQ1_lNO1_p*h
z2S>=~KiqCGFfjZFVXqG&<tXJ<!W=F4Q06Pz2$V(2QOc`?Ia<#{nWyZdEKn9HM=7ro
zri&Iw4<8(TGDlA+oIqKm9Nm}83=H=a$k|;SSM_8<-FAL|6O*r0N<^I+!PwJhM`?|~
zWYUUZ?0*bU`jnK8pP8OW=(@_0%S6G<(aP^7a;jol;qbSdr-rY+&+9`3<}N5&N-xCp
zyp}RA#xr^$uB7CIWL$FcmSpA4m}EF5laiH5(EJoW1M_pV_5~$nW#)yBm6d_hkM@cE
z;h}J`Ph1)7AL<ha`iF}}`(eW8zSgebV`EcN@uinn*NETlIs!+k1bKycuuxFKTY}7U
zSGiYf>Xf9mL~A)U%z~<GX}o&Ny|a`tGrEBn50g7eGObf9_WH!r<diJAB_ierVO7&L
zIiXuw$G%$=AO+l33#uu9-PQC*p}|$P?tXvZNPUCQ*woz8`aoNI$I;GX9mnT|=7f}7
zOdGb)XLMCJVrpKVQIfhAS6-0kg_d&hcvecwPpFDy&RFs_i<CE2HGfV{T`u36TFz~^
z;nX>6$g*}ZZ78XUg#}Z#W-9WtbEr^Z+>*27+JcVumlU}$k+T$CGpA+4M4pulL(<Z6
zZ26KZC1iC%i|Ll7XXk|mr!Dq<(va<Tj@@`pN-A2qd*l`wn(pWol9_1qd}%ZO;>*H&
zUP)&xb$PcS*u42eY3vl#VyzSPWoLppXLR=qZ|~;KpWb(`N(h`~o~lazVEH-r6vOGU
z`AeG}ca0?9{wvV8qxS^%zl(N1Qqe9_)zx}9jwt~WBw-QKV1N#J&~OR`A%~pCXcxc>
z=Rv`I1}sov8JeIoRADq!?SC@}p=n*sXJs?wYS_Z5P86uNvVn8<09d-25ywWW+}yZv
zwTQ~%^nd0JKW!aMWe*}N!1}_oI8uM6<Rv4OG8c)mbz-&rqq4bZ={c(E#*Ot)%Br?K
z?x-5D`-AB<fa^WzL$K0?$nz#n#E5Chxs0qusJTdG@9Uu*{8c&zQRl3q@lmvY&KU&-
zRkv^yOspls7>uIY4>{&T)O?7V4{Z~t8MqKYEB373_R&t=(O%-;)<hRP<vH&<zm@R`
zFlDbOb-wQCta}em!U*<a9(z-S*Wn#_7e0aw_#S?S-#IVW%pK!;YJRKvgRkYiyuf$x
zU3@p+!#~2G;`@jRkGpI>xLtI92;4==B1<3y$eo;$w8*k$zb~BMJ)`l1ukUO$ez@jd
z-KEiZ*>(bdZDr$@Zs0<`tFpH`<4!bsz7hX%G<v>U!;$)Gf6=&ktM~=%j7IDaCO^Q7
zdGz5Vb>TJwagRu}^^H3bXy7=cK|Xl=*|P*K?=qhM{${5?Jlx+W4h@~QH#@__;XZM2
z05>~z2;sB+Zg!gBDOAruZ5Gg@Ydf@*aJJ^Q?+@V$rngHaGUkplx6?|(v~JIdQYjfd
z(41pnU|?Xl6R061+W-GQ`S;)d85kJuVsQLud^84I9|U*6J}!wswtWD5e+cl9zq@+r
zy)cy9MJcZmKBSKU?EKHp|CP80&;JLIl7WGNfq{X6fq{X6fq{X6fq{YHe}caNn2h^T
--- a/browser/components/Makefile.in
+++ b/browser/components/Makefile.in
@@ -66,16 +66,17 @@ PARALLEL_DIRS = \
   places \
   preferences \
   privatebrowsing \
   search \
   sessionstore \
   shell \
   sidebar \
   tabview \
+  thumbnails \
   migration \
   $(NULL)
 
 ifdef MOZ_SAFE_BROWSING
 PARALLEL_DIRS += safebrowsing
 endif
 
 TEST_DIRS += test
--- a/browser/components/migration/src/Makefile.in
+++ b/browser/components/migration/src/Makefile.in
@@ -44,26 +44,25 @@ include $(DEPTH)/config/autoconf.mk
 MODULE		= migration
 LIBRARY_NAME	= migration_s
 FORCE_STATIC_LIB = 1
 ifndef MOZ_MEMORY
 USE_STATIC_LIBS = 1
 endif
 
 
-CPPSRCS  = nsBrowserProfileMigratorUtils.cpp \
-           $(NULL)
-
 ifeq ($(OS_ARCH)_$(GNU_CXX),WINNT_)
 CPPSRCS += nsIEProfileMigrator.cpp \
+           nsBrowserProfileMigratorUtils.cpp \
            $(NULL)
 endif
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += nsSafariProfileMigrator.cpp \
+           nsBrowserProfileMigratorUtils.cpp \
            $(NULL)
 endif            
 
 EXTRA_PP_COMPONENTS = \
   ProfileMigrator.js \
   ChromeProfileMigrator.js \
   FirefoxProfileMigrator.js \
   $(NULL)
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -169,17 +169,17 @@ BrowserGlue.prototype = {
       case "final-ui-startup":
         this._onProfileStartup();
         break;
       case "browser-delayed-startup-finished":
         this._onFirstWindowLoaded();
         Services.obs.removeObserver(this, "browser-delayed-startup-finished");
         break;
       case "sessionstore-windows-restored":
-        this._onBrowserStartup();
+        this._onWindowsRestored();
         break;
       case "browser:purge-session-history":
         // reset the console service's error buffer
         Services.console.logStringMessage(null); // clear the console (in case it's open)
         Services.console.reset();
         break;
       case "quit-application-requested":
         this._onQuitRequest(subject, data);
@@ -368,18 +368,18 @@ BrowserGlue.prototype = {
   },
 
   // profile shutdown handler (contains profile cleanup routines)
   _onProfileShutdown: function BG__onProfileShutdown() {
     this._shutdownPlaces();
     this._sanitizer.onShutdown();
   },
 
-  // Browser startup complete. All initial windows have opened.
-  _onBrowserStartup: function BG__onBrowserStartup() {
+  // All initial windows have opened.
+  _onWindowsRestored: function BG__onWindowsRestored() {
     // Show about:rights notification, if needed.
     if (this._shouldShowRights()) {
       this._showRightsNotification();
 #ifdef MOZ_TELEMETRY_REPORTING
     } else {
       // Only show telemetry notification when about:rights notification is not shown.
       this._showTelemetryNotification();
 #endif
@@ -1437,17 +1437,17 @@ BrowserGlue.prototype = {
 #ifndef XP_WIN
 #define BROKEN_WM_Z_ORDER
 #endif
 
   // this returns the most recent non-popup browser window
   getMostRecentBrowserWindow: function BG_getMostRecentBrowserWindow() {
     function isFullBrowserWindow(win) {
       return !win.closed &&
-             !win.document.documentElement.getAttribute("chromehidden");
+             win.toolbar.visible;
     }
 
 #ifdef BROKEN_WM_Z_ORDER
     var win = Services.wm.getMostRecentWindow("navigator:browser");
 
     // if we're lucky, this isn't a popup, and we can just return this
     if (win && !isFullBrowserWindow(win)) {
       win = null;
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -125,16 +125,18 @@ const TAB_EVENTS = ["TabOpen", "TabClose
 #define BROKEN_WM_Z_ORDER
 #endif
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 // debug.js adds NS_ASSERT. cf. bug 669196
 Cu.import("resource://gre/modules/debug.js");
 
+Cu.import("resource:///modules/TelemetryTimestamps.jsm");
+
 XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
   Cu.import("resource://gre/modules/NetUtil.jsm");
   return NetUtil;
 });
 
 XPCOMUtils.defineLazyGetter(this, "ScratchpadManager", function() {
   Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
   return ScratchpadManager;
@@ -289,16 +291,17 @@ SessionStoreService.prototype = {
   },
 
 /* ........ Global Event Handlers .............. */
 
   /**
    * Initialize the component
    */
   initService: function() {
+    TelemetryTimestamps.add("sessionRestoreInitialized");
     OBSERVING.forEach(function(aTopic) {
       Services.obs.addObserver(this, aTopic, true);
     }, this);
 
     var pbs = Cc["@mozilla.org/privatebrowsing;1"].
               getService(Ci.nsIPrivateBrowsingService);
     this._inPrivateBrowsing = pbs.privateBrowsingEnabled;
 
@@ -828,24 +831,25 @@ SessionStoreService.prototype = {
 
     // and create its internal data object
     this._internalWindows[aWindow.__SSi] = { hosts: {} }
 
     if (!this._isWindowLoaded(aWindow))
       this._windows[aWindow.__SSi]._restoring = true;
     if (!aWindow.toolbar.visible)
       this._windows[aWindow.__SSi].isPopup = true;
-    
+
     // perform additional initialization when the first window is loading
     if (this._loadState == STATE_STOPPED) {
       this._loadState = STATE_RUNNING;
       this._lastSaveTime = Date.now();
       
       // restore a crashed session resp. resume the last session if requested
       if (this._initialState) {
+        TelemetryTimestamps.add("sessionRestoreRestoring");
         // make sure that the restored tabs are first in the window
         this._initialState._firstTabs = true;
         this._restoreCount = this._initialState.windows ? this._initialState.windows.length : 0;
         this.restoreWindow(aWindow, this._initialState,
                            this._isCmdLineEmpty(aWindow, this._initialState));
         delete this._initialState;
         
         // _loadState changed from "stopped" to "running"
--- a/browser/components/sessionstore/test/browser_248970_b.js
+++ b/browser/components/sessionstore/test/browser_248970_b.js
@@ -43,17 +43,17 @@ function test() {
       return aLambda() || true;
     } catch(ex) { }
     return false;
   }
 
   var file = Components.classes["@mozilla.org/file/directory_service;1"]
              .getService(Components.interfaces.nsIProperties)
              .get("TmpD", Components.interfaces.nsIFile);
-  filePath = file.path;
+  var filePath = file.path;
 
   let fieldList = {
     "//input[@name='input']":     Date.now().toString(),
     "//input[@name='spaced 1']":  Math.random().toString(),
     "//input[3]":                 "three",
     "//input[@type='checkbox']":  true,
     "//input[@name='uncheck']":   false,
     "//input[@type='radio'][1]":  false,
--- a/browser/components/sessionstore/test/browser_346337.js
+++ b/browser/components/sessionstore/test/browser_346337.js
@@ -37,23 +37,23 @@
 function test() {
   /** Test for Bug 346337 **/
 
   var file = Components.classes["@mozilla.org/file/directory_service;1"]
                .getService(Components.interfaces.nsIProperties)
                .get("TmpD", Components.interfaces.nsILocalFile);
   file.append("346337_test1.file");
   file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666);
-  filePath1 = file.path;
+  var filePath1 = file.path;
   file = Components.classes["@mozilla.org/file/directory_service;1"]
              .getService(Components.interfaces.nsIProperties)
              .get("TmpD", Components.interfaces.nsILocalFile);
   file.append("346337_test2.file");
   file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666);
-  filePath2 = file.path;
+  var filePath2 = file.path;
   
   let fieldList = {
     "//input[@name='input']":     Date.now().toString(),
     "//input[@name='spaced 1']":  Math.random().toString(),
     "//input[3]":                 "three",
     "//input[@type='checkbox']":  true,
     "//input[@name='uncheck']":   false,
     "//input[@type='radio'][1]":  false,
--- a/browser/components/tabview/iq.js
+++ b/browser/components/tabview/iq.js
@@ -736,16 +736,21 @@ iQClass.prototype = {
       let handler = func;
       if (elem.iQEventData && elem.iQEventData[type]) {
         let count = elem.iQEventData[type].length;
         for (let a = 0; a < count; a++) {
           let pair = elem.iQEventData[type][a];
           if (pair.original == func) {
             handler = pair.modified;
             elem.iQEventData[type].splice(a, 1);
+            if (!elem.iQEventData[type].length) {
+              delete elem.iQEventData[type];
+              if (!Object.keys(elem.iQEventData).length)
+                delete elem.iQEventData;
+            }
             break;
           }
         }
       }
 
       elem.removeEventListener(type, handler, false);
     }
 
@@ -760,20 +765,20 @@ iQClass.prototype = {
       let elem = this[i];
 
       for (let j = 0; j < elem.childElementCount; j++)
         iQ(elem.children[j]).unbindAll();
 
       if (!elem.iQEventData)
         continue;
 
-      for (let type in elem.iQEventData) {
-        while (elem.iQEventData[type].length)
+      Object.keys(elem.iQEventData).forEach(function (type) {
+        while (elem.iQEventData && elem.iQEventData[type])
           this.unbind(type, elem.iQEventData[type][0].original);
-      }
+      }, this);
     }
 
     return this;
   }
 };
 
 // ----------
 // Create various event aliases
new file mode 100644
--- /dev/null
+++ b/browser/components/thumbnails/BrowserPageThumbs.manifest
@@ -0,0 +1,2 @@
+component {5a4ae9b5-f475-48ae-9dce-0b4c1d347884} PageThumbsProtocol.js
+contract @mozilla.org/network/protocol;1?name=moz-page-thumb {5a4ae9b5-f475-48ae-9dce-0b4c1d347884}
new file mode 100644
--- /dev/null
+++ b/browser/components/thumbnails/Makefile.in
@@ -0,0 +1,28 @@
+# 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/.
+
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+EXTRA_COMPONENTS = \
+	BrowserPageThumbs.manifest \
+	PageThumbsProtocol.js \
+	$(NULL)
+
+EXTRA_PP_JS_MODULES = \
+	PageThumbs.jsm \
+	$(NULL)
+
+# FIXME Bug 721422 - Re-enable tests and make them work with URI_DANGEROUS_TO_LOAD
+#ifdef ENABLE_TESTS
+#	DIRS += test
+#endif
+
+include $(topsrcdir)/config/rules.mk
+
+XPIDL_FLAGS += -I$(topsrcdir)/browser/components/
new file mode 100644
--- /dev/null
+++ b/browser/components/thumbnails/PageThumbs.jsm
@@ -0,0 +1,259 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+let EXPORTED_SYMBOLS = ["PageThumbs", "PageThumbsCache"];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
+
+/**
+ * The default width for page thumbnails.
+ *
+ * Hint: This is the default value because the 'New Tab Page' is the only
+ *       client for now.
+ */
+const THUMBNAIL_WIDTH = 201;
+
+/**
+ * The default height for page thumbnails.
+ *
+ * Hint: This is the default value because the 'New Tab Page' is the only
+ *       client for now.
+ */
+const THUMBNAIL_HEIGHT = 127;
+
+/**
+ * The default background color for page thumbnails.
+ */
+const THUMBNAIL_BG_COLOR = "#fff";
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+  "resource://gre/modules/NetUtil.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Services",
+  "resource://gre/modules/Services.jsm");
+
+/**
+ * Singleton providing functionality for capturing web page thumbnails and for
+ * accessing them if already cached.
+ */
+let PageThumbs = {
+  /**
+   * The scheme to use for thumbnail urls.
+   */
+  get scheme() "moz-page-thumb",
+
+  /**
+   * The static host to use for thumbnail urls.
+   */
+  get staticHost() "thumbnail",
+
+  /**
+   * The thumbnails' image type.
+   */
+  get contentType() "image/png",
+
+  /**
+   * Gets the thumbnail image's url for a given web page's url.
+   * @param aUrl The web page's url that is depicted in the thumbnail.
+   * @return The thumbnail image's url.
+   */
+  getThumbnailURL: function PageThumbs_getThumbnailURL(aUrl) {
+    return this.scheme + "://" + this.staticHost +
+           "?url=" + encodeURIComponent(aUrl);
+  },
+
+  /**
+   * Creates a canvas containing a thumbnail depicting the given window.
+   * @param aWindow The DOM window to capture a thumbnail from.
+   * @return The newly created canvas containing the image data.
+   */
+  capture: function PageThumbs_capture(aWindow) {
+    let [sw, sh, scale] = this._determineCropSize(aWindow);
+
+    let canvas = this._createCanvas();
+    let ctx = canvas.getContext("2d");
+
+    // Scale the canvas accordingly.
+    ctx.scale(scale, scale);
+
+    try {
+      // Draw the window contents to the canvas.
+      ctx.drawWindow(aWindow, 0, 0, sw, sh, THUMBNAIL_BG_COLOR,
+                     ctx.DRAWWINDOW_DO_NOT_FLUSH);
+    } catch (e) {
+      // We couldn't draw to the canvas for some reason.
+    }
+
+    return canvas;
+  },
+
+  /**
+   * Stores the image data contained in the given canvas to the underlying
+   * storage.
+   * @param aKey The key to use for the storage.
+   * @param aCanvas The canvas containing the thumbnail's image data.
+   * @param aCallback The function to be called when the canvas data has been
+   *                  stored (optional).
+   */
+  store: function PageThumbs_store(aKey, aCanvas, aCallback) {
+    let self = this;
+
+    function finish(aSuccessful) {
+      if (aCallback)
+        aCallback(aSuccessful);
+    }
+
+    // Get a writeable cache entry.
+    PageThumbsCache.getWriteEntry(aKey, function (aEntry) {
+      if (!aEntry) {
+        finish(false);
+        return;
+      }
+
+      // Extract image data from the canvas.
+      self._readImageData(aCanvas, function (aData) {
+        let outputStream = aEntry.openOutputStream(0);
+
+        // Write the image data to the cache entry.
+        NetUtil.asyncCopy(aData, outputStream, function (aResult) {
+          let success = Components.isSuccessCode(aResult);
+          if (success)
+            aEntry.markValid();
+
+          aEntry.close();
+          finish(success);
+        });
+      });
+    });
+  },
+
+  /**
+   * Reads the image data from a given canvas and passes it to the callback.
+   * @param aCanvas The canvas to read the image data from.
+   * @param aCallback The function that the image data is passed to.
+   */
+  _readImageData: function PageThumbs_readImageData(aCanvas, aCallback) {
+    let dataUri = aCanvas.toDataURL(PageThumbs.contentType, "");
+    let uri = Services.io.newURI(dataUri, "UTF8", null);
+
+    NetUtil.asyncFetch(uri, function (aData, aResult) {
+      if (Components.isSuccessCode(aResult) && aData && aData.available())
+        aCallback(aData);
+    });
+  },
+
+  /**
+   * Determines the crop size for a given content window.
+   * @param aWindow The content window.
+   * @return An array containing width, height and scale.
+   */
+  _determineCropSize: function PageThumbs_determineCropSize(aWindow) {
+    let sw = aWindow.innerWidth;
+    let sh = aWindow.innerHeight;
+
+    let scale = Math.max(THUMBNAIL_WIDTH / sw, THUMBNAIL_HEIGHT / sh);
+    let scaledWidth = sw * scale;
+    let scaledHeight = sh * scale;
+
+    if (scaledHeight > THUMBNAIL_HEIGHT)
+      sh -= Math.floor(Math.abs(scaledHeight - THUMBNAIL_HEIGHT) * scale);
+
+    if (scaledWidth > THUMBNAIL_WIDTH)
+      sw -= Math.floor(Math.abs(scaledWidth - THUMBNAIL_WIDTH) * scale);
+
+    return [sw, sh, scale];
+  },
+
+  /**
+   * Creates a new hidden canvas element.
+   * @return The newly created canvas.
+   */
+  _createCanvas: function PageThumbs_createCanvas() {
+    let doc = Services.appShell.hiddenDOMWindow.document;
+    let canvas = doc.createElementNS(HTML_NAMESPACE, "canvas");
+    canvas.mozOpaque = true;
+    canvas.mozImageSmoothingEnabled = true;
+    canvas.width = THUMBNAIL_WIDTH;
+    canvas.height = THUMBNAIL_HEIGHT;
+    return canvas;
+  }
+};
+
+/**
+ * A singleton handling the storage of page thumbnails.
+ */
+let PageThumbsCache = {
+  /**
+   * Calls the given callback with a cache entry opened for reading.
+   * @param aKey The key identifying the desired cache entry.
+   * @param aCallback The callback that is called when the cache entry is ready.
+   */
+  getReadEntry: function Cache_getReadEntry(aKey, aCallback) {
+    // Try to open the desired cache entry.
+    this._openCacheEntry(aKey, Ci.nsICache.ACCESS_READ, aCallback);
+  },
+
+  /**
+   * Calls the given callback with a cache entry opened for writing.
+   * @param aKey The key identifying the desired cache entry.
+   * @param aCallback The callback that is called when the cache entry is ready.
+   */
+  getWriteEntry: function Cache_getWriteEntry(aKey, aCallback) {
+    // Try to open the desired cache entry.
+    this._openCacheEntry(aKey, Ci.nsICache.ACCESS_WRITE, aCallback);
+  },
+
+  /**
+   * Opens the cache entry identified by the given key.
+   * @param aKey The key identifying the desired cache entry.
+   * @param aAccess The desired access mode (see nsICache.ACCESS_* constants).
+   * @param aCallback The function to be called when the cache entry was opened.
+   */
+  _openCacheEntry: function Cache_openCacheEntry(aKey, aAccess, aCallback) {
+    function onCacheEntryAvailable(aEntry, aAccessGranted, aStatus) {
+      let validAccess = aAccess == aAccessGranted;
+      let validStatus = Components.isSuccessCode(aStatus);
+
+      // Check if a valid entry was passed and if the
+      // access we requested was actually granted.
+      if (aEntry && !(validAccess && validStatus)) {
+        aEntry.close();
+        aEntry = null;
+      }
+
+      aCallback(aEntry);
+    }
+
+    let listener = this._createCacheListener(onCacheEntryAvailable);
+    this._cacheSession.asyncOpenCacheEntry(aKey, aAccess, listener);
+  },
+
+  /**
+   * Returns a cache listener implementing the nsICacheListener interface.
+   * @param aCallback The callback to be called when the cache entry is available.
+   * @return The new cache listener.
+   */
+  _createCacheListener: function Cache_createCacheListener(aCallback) {
+    return {
+      onCacheEntryAvailable: aCallback,
+      QueryInterface: XPCOMUtils.generateQI([Ci.nsICacheListener])
+    };
+  }
+};
+
+/**
+ * Define a lazy getter for the cache session.
+ */
+XPCOMUtils.defineLazyGetter(PageThumbsCache, "_cacheSession", function () {
+  return Services.cache.createSession(PageThumbs.scheme,
+                                     Ci.nsICache.STORE_ON_DISK, true);
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/thumbnails/PageThumbsProtocol.js
@@ -0,0 +1,448 @@
+/* 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/. */
+
+/**
+ * PageThumbsProtocol.js
+ *
+ * This file implements the moz-page-thumb:// protocol and the corresponding
+ * channel delivering cached thumbnails.
+ *
+ * URL structure:
+ *
+ * moz-page-thumb://thumbnail?url=http%3A%2F%2Fwww.mozilla.org%2F
+ *
+ * This URL requests an image for 'http://www.mozilla.org/'.
+ */
+
+"use strict";
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Cr = Components.results;
+const Ci = Components.interfaces;
+
+Cu.import("resource:///modules/PageThumbs.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+  "resource://gre/modules/NetUtil.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Services",
+  "resource://gre/modules/Services.jsm");
+
+/**
+ * Implements the thumbnail protocol handler responsible for moz-page-thumb: URIs.
+ */
+function Protocol() {
+}
+
+Protocol.prototype = {
+  /**
+   * The scheme used by this protocol.
+   */
+  get scheme() PageThumbs.scheme,
+
+  /**
+   * The default port for this protocol (we don't support ports).
+   */
+  get defaultPort() -1,
+
+  /**
+   * The flags specific to this protocol implementation.
+   */
+  get protocolFlags() {
+    return Ci.nsIProtocolHandler.URI_DANGEROUS_TO_LOAD |
+           Ci.nsIProtocolHandler.URI_NORELATIVE |
+           Ci.nsIProtocolHandler.URI_NOAUTH;
+  },
+
+  /**
+   * Creates a new URI object that is suitable for loading by this protocol.
+   * @param aSpec The URI string in UTF8 encoding.
+   * @param aOriginCharset The charset of the document from which the URI originated.
+   * @return The newly created URI.
+   */
+  newURI: function Proto_newURI(aSpec, aOriginCharset) {
+    let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
+    uri.spec = aSpec;
+    return uri;
+  },
+
+  /**
+   * Constructs a new channel from the given URI for this protocol handler.
+   * @param aURI The URI for which to construct a channel.
+   * @return The newly created channel.
+   */
+  newChannel: function Proto_newChannel(aURI) {
+    return new Channel(aURI);
+  },
+
+  /**
+   * Decides whether to allow a blacklisted port.
+   * @return Always false, we'll never allow ports.
+   */
+  allowPort: function () false,
+
+  classID: Components.ID("{5a4ae9b5-f475-48ae-9dce-0b4c1d347884}"),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler])
+};
+
+let NSGetFactory = XPCOMUtils.generateNSGetFactory([Protocol]);
+
+/**
+ * A channel implementation responsible for delivering cached thumbnails.
+ */
+function Channel(aURI) {
+  this._uri = aURI;
+
+  // nsIChannel
+  this.originalURI = aURI;
+
+  // nsIHttpChannel
+  this._responseHeaders = {"content-type": PageThumbs.contentType};
+}
+
+Channel.prototype = {
+  /**
+   * Tracks if the channel has been opened, yet.
+   */
+  _wasOpened: false,
+
+  /**
+   * Opens this channel asynchronously.
+   * @param aListener The listener that receives the channel data when available.
+   * @param aContext A custom context passed to the listener's methods.
+   */
+  asyncOpen: function Channel_asyncOpen(aListener, aContext) {
+    if (this._wasOpened)
+      throw Cr.NS_ERROR_ALREADY_OPENED;
+
+    if (this.canceled)
+      return;
+
+    this._listener = aListener;
+    this._context = aContext;
+
+    this._isPending = true;
+    this._wasOpened = true;
+
+    // Try to read the data from the thumbnail cache.
+    this._readCache(function (aData) {
+      // Update response if there's no data.
+      if (!aData) {
+        this._responseStatus = 404;
+        this._responseText = "Not Found";
+      }
+
+      this._startRequest();
+
+      if (!this.canceled) {
+        this._addToLoadGroup();
+
+        if (aData)
+          this._serveData(aData);
+
+        if (!this.canceled)
+          this._stopRequest();
+      }
+    }.bind(this));
+  },
+
+  /**
+   * Reads a data stream from the cache entry.
+   * @param aCallback The callback the data is passed to.
+   */
+  _readCache: function Channel_readCache(aCallback) {
+    let {url} = parseURI(this._uri);
+
+    // Return early if there's no valid URL given.
+    if (!url) {
+      aCallback(null);
+      return;
+    }
+
+    // Try to get a cache entry.
+    PageThumbsCache.getReadEntry(url, function (aEntry) {
+      let inputStream = aEntry && aEntry.openInputStream(0);
+
+      function closeEntryAndFinish(aData) {
+        if (aEntry) {
+          aEntry.close();
+        }
+        aCallback(aData);
+      }
+
+      // Check if we have a valid entry and if it has any data.
+      if (!inputStream || !inputStream.available()) {
+        closeEntryAndFinish();
+        return;
+      }
+
+      try {
+        // Read the cache entry's data.
+        NetUtil.asyncFetch(inputStream, function (aData, aStatus) {
+          // We might have been canceled while waiting.
+          if (this.canceled)
+            return;
+
+          // Check if we have a valid data stream.
+          if (!Components.isSuccessCode(aStatus) || !aData.available())
+            aData = null;
+
+          closeEntryAndFinish(aData);
+        }.bind(this));
+      } catch (e) {
+        closeEntryAndFinish();
+      }
+    }.bind(this));
+  },
+
+  /**
+   * Calls onStartRequest on the channel listener.
+   */
+  _startRequest: function Channel_startRequest() {
+    try {
+      this._listener.onStartRequest(this, this._context);
+    } catch (e) {
+      // The listener might throw if the request has been canceled.
+      this.cancel(Cr.NS_BINDING_ABORTED);
+    }
+  },
+
+  /**
+   * Calls onDataAvailable on the channel listener and passes the data stream.
+   * @param aData The data to be delivered.
+   */
+  _serveData: function Channel_serveData(aData) {
+    try {
+      let available = aData.available();
+      this._listener.onDataAvailable(this, this._context, aData, 0, available);
+    } catch (e) {
+      // The listener might throw if the request has been canceled.
+      this.cancel(Cr.NS_BINDING_ABORTED);
+    }
+  },
+
+  /**
+   * Calls onStopRequest on the channel listener.
+   */
+  _stopRequest: function Channel_stopRequest() {
+    try {
+      this._listener.onStopRequest(this, this._context, this.status);
+    } catch (e) {
+      // This might throw but is generally ignored.
+    }
+
+    // The request has finished, clean up after ourselves.
+    this._cleanup();
+  },
+
+  /**
+   * Adds this request to the load group, if any.
+   */
+  _addToLoadGroup: function Channel_addToLoadGroup() {
+    if (this.loadGroup)
+      this.loadGroup.addRequest(this, this._context);
+  },
+
+  /**
+   * Removes this request from its load group, if any.
+   */
+  _removeFromLoadGroup: function Channel_removeFromLoadGroup() {
+    if (!this.loadGroup)
+      return;
+
+    try {
+      this.loadGroup.removeRequest(this, this._context, this.status);
+    } catch (e) {
+      // This might throw but is ignored.
+    }
+  },
+
+  /**
+   * Cleans up the channel when the request has finished.
+   */
+  _cleanup: function Channel_cleanup() {
+    this._removeFromLoadGroup();
+    this.loadGroup = null;
+
+    this._isPending = false;
+
+    delete this._listener;
+    delete this._context;
+  },
+
+  /* :::::::: nsIChannel ::::::::::::::: */
+
+  contentType: PageThumbs.contentType,
+  contentLength: -1,
+  owner: null,
+  contentCharset: null,
+  notificationCallbacks: null,
+
+  get URI() this._uri,
+  get securityInfo() null,
+
+  /**
+   * Opens this channel synchronously. Not supported.
+   */
+  open: function Channel_open() {
+    // Synchronous data delivery is not implemented.
+    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
+  /* :::::::: nsIHttpChannel ::::::::::::::: */
+
+  redirectionLimit: 10,
+  requestMethod: "GET",
+  allowPipelining: true,
+  referrer: null,
+
+  get requestSucceeded() true,
+
+  _responseStatus: 200,
+  get responseStatus() this._responseStatus,
+
+  _responseText: "OK",
+  get responseStatusText() this._responseText,
+
+  /**
+   * Checks if the server sent the equivalent of a "Cache-control: no-cache"
+   * response header.
+   * @return Always false.
+   */
+  isNoCacheResponse: function () false,
+
+  /**
+   * Checks if the server sent the equivalent of a "Cache-control: no-cache"
+   * response header.
+   * @return Always false.
+   */
+  isNoStoreResponse: function () false,
+
+  /**
+   * Returns the value of a particular request header. Not implemented.
+   */
+  getRequestHeader: function Channel_getRequestHeader() {
+    throw Cr.NS_ERROR_NOT_AVAILABLE;
+  },
+
+  /**
+   * This method is called to set the value of a particular request header.
+   * Not implemented.
+   */
+  setRequestHeader: function Channel_setRequestHeader() {
+    if (this._wasOpened)
+      throw Cr.NS_ERROR_IN_PROGRESS;
+  },
+
+  /**
+   * Call this method to visit all request headers. Not implemented.
+   */
+  visitRequestHeaders: function () {},
+
+  /**
+   * Gets the value of a particular response header.
+   * @param aHeader The case-insensitive name of the response header to query.
+   * @return The header value.
+   */
+  getResponseHeader: function Channel_getResponseHeader(aHeader) {
+    let name = aHeader.toLowerCase();
+    if (name in this._responseHeaders)
+      return this._responseHeaders[name];
+
+    throw Cr.NS_ERROR_NOT_AVAILABLE;
+  },
+
+  /**
+   * This method is called to set the value of a particular response header.
+   * @param aHeader The case-insensitive name of the response header to query.
+   * @param aValue The response header value to set.
+   */
+  setResponseHeader: function Channel_setResponseHeader(aHeader, aValue, aMerge) {
+    let name = aHeader.toLowerCase();
+    if (!aValue && !aMerge)
+      delete this._responseHeaders[name];
+    else
+      this._responseHeaders[name] = aValue;
+  },
+
+  /**
+   * Call this method to visit all response headers.
+   * @param aVisitor The header visitor.
+   */
+  visitResponseHeaders: function Channel_visitResponseHeaders(aVisitor) {
+    for (let name in this._responseHeaders) {
+      let value = this._responseHeaders[name];
+
+      try {
+        aVisitor.visitHeader(name, value);
+      } catch (e) {
+        // The visitor can throw to stop the iteration.
+        return;
+      }
+    }
+  },
+
+  /* :::::::: nsIRequest ::::::::::::::: */
+
+  loadFlags: Ci.nsIRequest.LOAD_NORMAL,
+  loadGroup: null,
+
+  get name() this._uri.spec,
+
+  _status: Cr.NS_OK,
+  get status() this._status,
+
+  _isPending: false,
+  isPending: function () this._isPending,
+
+  resume: function () {},
+  suspend: function () {},
+
+  /**
+   * Cancels this request.
+   * @param aStatus The reason for cancelling.
+   */
+  cancel: function Channel_cancel(aStatus) {
+    if (this.canceled)
+      return;
+
+    this._isCanceled = true;
+    this._status = aStatus;
+
+    this._cleanup();
+  },
+
+  /* :::::::: nsIHttpChannelInternal ::::::::::::::: */
+
+  documentURI: null,
+
+  _isCanceled: false,
+  get canceled() this._isCanceled,
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel,
+                                         Ci.nsIHttpChannel,
+                                         Ci.nsIHttpChannelInternal,
+                                         Ci.nsIRequest])
+};
+
+/**
+ * Parses a given URI and extracts all parameters relevant to this protocol.
+ * @param aURI The URI to parse.
+ * @return The parsed parameters.
+ */
+function parseURI(aURI) {
+  let {scheme, staticHost} = PageThumbs;
+  let re = new RegExp("^" + scheme + "://" + staticHost + ".*?\\?");
+  let query = aURI.spec.replace(re, "");
+  let params = {};
+
+  query.split("&").forEach(function (aParam) {
+    let [key, value] = aParam.split("=").map(decodeURIComponent);
+    params[key.toLowerCase()] = value;
+  });
+
+  return params;
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/thumbnails/test/Makefile.in
@@ -0,0 +1,21 @@
+# 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/.
+
+DEPTH		= ../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+relativesrcdir  = browser/components/thumbnails/test
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_BROWSER_FILES = \
+	browser_thumbnails_cache.js \
+	browser_thumbnails_capture.js \
+	head.js \
+	$(NULL)
+
+libs::	$(_BROWSER_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/components/thumbnails/test/browser_thumbnails_cache.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * These tests ensure that saving a thumbnail to the cache works. They also
+ * retrieve the thumbnail and display it using an <img> element to compare
+ * its pixel colors.
+ */
+function runTests() {
+  // Create a new tab with a red background.
+  yield addTab("data:text/html,<body bgcolor=ff0000></body>");
+  let cw = gBrowser.selectedTab.linkedBrowser.contentWindow;
+
+  // Capture a thumbnail for the tab.
+  let canvas = PageThumbs.capture(cw);
+
+  // Store the tab into the thumbnail cache.
+  yield PageThumbs.store("key", canvas, next);
+
+  let {width, height} = canvas;
+  let thumb = PageThumbs.getThumbnailURL("key", width, height);
+
+  // Create a new tab with an image displaying the previously stored thumbnail.
+  yield addTab("data:text/html,<img src='" + thumb + "'/>" + 
+               "<canvas width=" + width + " height=" + height + "/>");
+
+  cw = gBrowser.selectedTab.linkedBrowser.contentWindow;
+  let [img, canvas] = cw.document.querySelectorAll("img, canvas");
+
+  // Draw the image to a canvas and compare the pixel color values.
+  let ctx = canvas.getContext("2d");
+  ctx.drawImage(img, 0, 0, width, height);
+  checkCanvasColor(ctx, 255, 0, 0, "we have a red image and canvas");
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/thumbnails/test/browser_thumbnails_capture.js
@@ -0,0 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * These tests ensure that capturing a site's screenshot to a canvas actually
+ * works.
+ */
+function runTests() {
+  // Create a tab with a red background.
+  yield addTab("data:text/html,<body bgcolor=ff0000></body>");
+  checkCurrentThumbnailColor(255, 0, 0, "we have a red thumbnail");
+
+  // Load a page with a green background.
+  yield navigateTo("data:text/html,<body bgcolor=00ff00></body>");
+  checkCurrentThumbnailColor(0, 255, 0, "we have a green thumbnail");
+
+  // Load a page with a blue background.
+  yield navigateTo("data:text/html,<body bgcolor=0000ff></body>");
+  checkCurrentThumbnailColor(0, 0, 255, "we have a blue thumbnail");
+}
+
+/**
+ * Captures a thumbnail of the currently selected tab and checks the color of
+ * the resulting canvas.
+ * @param aRed The red component's intensity.
+ * @param aGreen The green component's intensity.
+ * @param aBlue The blue component's intensity.
+ * @param aMessage The info message to print when checking the pixel color.
+ */
+function checkCurrentThumbnailColor(aRed, aGreen, aBlue, aMessage) {
+  let tab = gBrowser.selectedTab;
+  let cw = tab.linkedBrowser.contentWindow;
+
+  let canvas = PageThumbs.capture(cw);
+  let ctx = canvas.getContext("2d");
+
+  checkCanvasColor(ctx, aRed, aGreen, aBlue, aMessage);
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/thumbnails/test/head.js
@@ -0,0 +1,93 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Cu.import("resource:///modules/PageThumbs.jsm");
+
+registerCleanupFunction(function () {
+  while (gBrowser.tabs.length > 1)
+    gBrowser.removeTab(gBrowser.tabs[1]);
+});
+
+/**
+ * Provide the default test function to start our test runner.
+ */
+function test() {
+  TestRunner.run();
+}
+
+/**
+ * The test runner that controls the execution flow of our tests.
+ */
+let TestRunner = {
+  /**
+   * Starts the test runner.
+   */
+  run: function () {
+    waitForExplicitFinish();
+
+    this._iter = runTests();
+    this.next();
+  },
+
+  /**
+   * Runs the next available test or finishes if there's no test left.
+   */
+  next: function () {
+    try {
+      TestRunner._iter.next();
+    } catch (e if e instanceof StopIteration) {
+      finish();
+    }
+  }
+};
+
+/**
+ * Continues the current test execution.
+ */
+function next() {
+  TestRunner.next();
+}
+
+/**
+ * Creates a new tab with the given URI.
+ * @param aURI The URI that's loaded in the tab.
+ */
+function addTab(aURI) {
+  let tab = gBrowser.selectedTab = gBrowser.addTab(aURI);
+  whenBrowserLoaded(tab.linkedBrowser);
+}
+
+/**
+ * Loads a new URI into the currently selected tab.
+ * @param aURI The URI to load.
+ */
+function navigateTo(aURI) {
+  let browser = gBrowser.selectedTab.linkedBrowser;
+  whenBrowserLoaded(browser);
+  browser.loadURI(aURI);
+}
+
+/**
+ * Continues the current test execution when a load event for the given browser
+ * has been received
+ * @param aBrowser The browser to listen on.
+ */
+function whenBrowserLoaded(aBrowser) {
+  aBrowser.addEventListener("load", function onLoad() {
+    aBrowser.removeEventListener("load", onLoad, true);
+    executeSoon(next);
+  }, true);
+}
+
+/**
+ * Checks the top-left pixel of a given canvas' 2d context for a given color.
+ * @param aContext The 2D context of a canvas.
+ * @param aRed The red component's intensity.
+ * @param aGreen The green component's intensity.
+ * @param aBlue The blue component's intensity.
+ * @param aMessage The info message to print when comparing the pixel color.
+ */
+function checkCanvasColor(aContext, aRed, aGreen, aBlue, aMessage) {
+  let [r, g, b] = aContext.getImageData(0, 0, 1, 1).data;
+  ok(r == aRed && g == aGreen && b == aBlue, aMessage);
+}
--- a/browser/devtools/highlighter/TreePanel.jsm
+++ b/browser/devtools/highlighter/TreePanel.jsm
@@ -136,17 +136,17 @@ TreePanel.prototype = {
     this.treePanelDiv = this.treeBrowserDocument.createElement("div");
     this.treeBrowserDocument.body.appendChild(this.treePanelDiv);
     this.treePanelDiv.ownerPanel = this;
     this.ioBox = new InsideOutBox(this, this.treePanelDiv);
     this.ioBox.createObjectBox(this.IUI.win.document.documentElement);
     this.treeLoaded = true;
     this.treeIFrame.addEventListener("click", this.onTreeClick.bind(this), false);
     this.treeIFrame.addEventListener("dblclick", this.onTreeDblClick.bind(this), false);
-    this.treeIFrame.addEventListener("keypress", this.IUI, false);
+    this.treeIFrame.focus();
     delete this.initializingTreePanel;
     Services.obs.notifyObservers(null,
       this.IUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, null);
     if (this.IUI.selection)
       this.select(this.IUI.selection, true);
   },
 
   /**
@@ -228,17 +228,17 @@ TreePanel.prototype = {
     treeBox.id = "inspector-tree-box";
     treeBox.state = "open"; // for the registerTools API.
     try {
       treeBox.height =
         Services.prefs.getIntPref("devtools.inspector.htmlHeight");
     } catch(e) {
       treeBox.height = 112;
     }
-                      
+
     treeBox.minHeight = 64;
     treeBox.flex = 1;
     toolbarParent.insertBefore(treeBox, toolbar);
 
     this.IUI.toolbar.setAttribute("treepanel-open", "true");
 
     treeBox.appendChild(this.treeIFrame);
 
@@ -461,19 +461,16 @@ TreePanel.prototype = {
     // position the editor
     editor.style.left = editorLeft + "px";
     editor.style.top = editorTop + "px";
 
     // set and select the text
     editorInput.value = aAttrVal;
     editorInput.select();
 
-    // remove tree key navigation events
-    this.treeIFrame.removeEventListener("keypress", this.IUI, false);
-
     // listen for editor specific events
     this.bindEditorEvent(editor, "click", function(aEvent) {
       aEvent.stopPropagation();
     });
     this.bindEditorEvent(editor, "dblclick", function(aEvent) {
       aEvent.stopPropagation();
     });
     this.bindEditorEvent(editor, "keypress",
@@ -556,19 +553,16 @@ TreePanel.prototype = {
     this.unbindEditorEvent(editor, "keypress");
 
     // clean up after the editor
     editorInput.value = "";
     editorInput.blur();
     this.editingContext = null;
     this.editingEvents = {};
 
-    // re-add navigation listener
-    this.treeIFrame.addEventListener("keypress", this.IUI, false);
-
     // event notification
     Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED,
                                   null);
   },
 
   /**
    * Commit the edits made in the editor, then close it.
    */
@@ -695,17 +689,16 @@ TreePanel.prototype = {
       this.treePanelDiv.ownerPanel = null;
       let parent = this.treePanelDiv.parentNode;
       parent.removeChild(this.treePanelDiv);
       delete this.treePanelDiv;
       delete this.treeBrowserDocument;
     }
 
     if (this.treeIFrame) {
-      this.treeIFrame.removeEventListener("keypress", this.IUI, false);
       this.treeIFrame.removeEventListener("dblclick", this.onTreeDblClick, false);
       this.treeIFrame.removeEventListener("click", this.onTreeClick, false);
       let parent = this.treeIFrame.parentNode;
       parent.removeChild(this.treeIFrame);
       delete this.treeIFrame;
     }
 
     if (this.ioBox) {
--- a/browser/devtools/highlighter/highlighter.jsm
+++ b/browser/devtools/highlighter/highlighter.jsm
@@ -693,22 +693,24 @@ Highlighter.prototype = {
     this.browser.removeEventListener("mousedown", this, true);
     this.browser.removeEventListener("mouseup", this, true);
   },
 
   attachPageListeners: function Highlighter_attachPageListeners()
   {
     this.browser.addEventListener("resize", this, true);
     this.browser.addEventListener("scroll", this, true);
+    this.browser.addEventListener("MozAfterPaint", this, true);
   },
 
   detachPageListeners: function Highlighter_detachPageListeners()
   {
     this.browser.removeEventListener("resize", this, true);
     this.browser.removeEventListener("scroll", this, true);
+    this.browser.removeEventListener("MozAfterPaint", this, true);
   },
 
   attachKeysListeners: function Highlighter_attachKeysListeners()
   {
     this.browser.addEventListener("keypress", this, true);
     this.highlighterContainer.addEventListener("keypress", this, true);
   },
 
@@ -729,98 +731,36 @@ Highlighter.prototype = {
     switch (aEvent.type) {
       case "click":
         this.handleClick(aEvent);
         break;
       case "mousemove":
         this.handleMouseMove(aEvent);
         break;
       case "resize":
+        this.computeZoomFactor();
+        break;
+      case "MozAfterPaint":
       case "scroll":
-        this.computeZoomFactor();
         this.brieflyDisableTransitions();
         this.invalidateSize();
         break;
       case "dblclick":
       case "mousedown":
       case "mouseup":
         aEvent.stopPropagation();
         aEvent.preventDefault();
         break;
-        break;
       case "keypress":
         switch (aEvent.keyCode) {
           case this.chromeWin.KeyEvent.DOM_VK_RETURN:
             this.locked ? this.unlock() : this.lock();
             aEvent.preventDefault();
             aEvent.stopPropagation();
             break;
-          case this.chromeWin.KeyEvent.DOM_VK_LEFT:
-            let node;
-            if (this.node) {
-              node = this.node.parentNode;
-            } else {
-              node = this.defaultSelection;
-            }
-            if (node && this.isNodeHighlightable(node)) {
-              this.highlight(node);
-            }
-            aEvent.preventDefault();
-            aEvent.stopPropagation();
-            break;
-          case this.chromeWin.KeyEvent.DOM_VK_RIGHT:
-            if (this.node) {
-              // Find the first child that is highlightable.
-              for (let i = 0; i < this.node.childNodes.length; i++) {
-                node = this.node.childNodes[i];
-                if (node && this.isNodeHighlightable(node)) {
-                  break;
-                }
-              }
-            } else {
-              node = this.defaultSelection;
-            }
-            if (node && this.isNodeHighlightable(node)) {
-              this.highlight(node, true);
-            }
-            aEvent.preventDefault();
-            aEvent.stopPropagation();
-            break;
-          case this.chromeWin.KeyEvent.DOM_VK_UP:
-            if (this.node) {
-              // Find a previous sibling that is highlightable.
-              node = this.node.previousSibling;
-              while (node && !this.isNodeHighlightable(node)) {
-                node = node.previousSibling;
-              }
-            } else {
-              node = this.defaultSelection;
-            }
-            if (node && this.isNodeHighlightable(node)) {
-              this.highlight(node, true);
-            }
-            aEvent.preventDefault();
-            aEvent.stopPropagation();
-            break;
-          case this.chromeWin.KeyEvent.DOM_VK_DOWN:
-            if (this.node) {
-              // Find a next sibling that is highlightable.
-              node = this.node.nextSibling;
-              while (node && !this.isNodeHighlightable(node)) {
-                node = node.nextSibling;
-              }
-            } else {
-              node = this.defaultSelection;
-            }
-            if (node && this.isNodeHighlightable(node)) {
-              this.highlight(node, true);
-            }
-            aEvent.preventDefault();
-            aEvent.stopPropagation();
-            break;
         }
     }
   },
 
   /**
    * Disable the CSS transitions for a short time to avoid laggy animations
    * during scrolling or resizing.
    */
--- a/browser/devtools/highlighter/inspector.jsm
+++ b/browser/devtools/highlighter/inspector.jsm
@@ -281,16 +281,18 @@ InspectorUI.prototype = {
     this.isDirty = false;
 
     this.progressListener = new InspectorProgressListener(this);
 
     this.chromeWin.addEventListener("keypress", this, false);
 
     // initialize the highlighter
     this.highlighter = new Highlighter(this.chromeWin);
+
+    this.setupNavigationKeys();
     this.highlighterReady();
   },
 
   /**
    * Register the Rule View in the Sidebar.
    */
   registerRuleView: function IUI_registerRuleView()
   {
@@ -345,16 +347,46 @@ InspectorUI.prototype = {
       this.store.setValue(this.winID, "selectedNode", null);
       this.store.setValue(this.winID, "inspecting", true);
       this.store.setValue(this.winID, "isDirty", this.isDirty);
       this.win.addEventListener("pagehide", this, true);
     }
   },
 
   /**
+   * Browse nodes according to the breadcrumbs layout, only for some specific
+   * elements of the UI.
+   */
+   setupNavigationKeys: function IUI_setupNavigationKeys()
+   {
+     // UI elements that are arrow keys sensitive:
+     // - highlighter veil;
+     // - content window (when the highlighter `veil is pointer-events:none`;
+     // - the Inspector toolbar.
+
+     this.onKeypress = this.onKeypress.bind(this);
+
+     this.highlighter.highlighterContainer.addEventListener("keypress",
+       this.onKeypress, true);
+     this.win.addEventListener("keypress", this.onKeypress, true);
+     this.toolbar.addEventListener("keypress", this.onKeypress, true);
+   },
+
+  /**
+   * Remove the event listeners for the arrowkeys.
+   */
+   removeNavigationKeys: function IUI_removeNavigationKeys()
+   {
+      this.highlighter.highlighterContainer.removeEventListener("keypress",
+        this.onKeypress, true);
+      this.win.removeEventListener("keypress", this.onKeypress, true);
+      this.toolbar.removeEventListener("keypress", this.onKeypress, true);
+   },
+
+  /**
    * Close inspector UI and associated panels. Unhighlight and stop inspecting.
    * Remove event listeners for document scrolling, resize,
    * tabContainer.TabSelect and others.
    *
    * @param boolean aKeepStore
    *        Tells if you want the store associated to the current tab/window to
    *        be cleared or not. Set this to true to not clear the store, or false
    *        otherwise.
@@ -370,16 +402,18 @@ InspectorUI.prototype = {
       return;
     }
 
     let winId = new String(this.winID); // retain this to notify observers.
 
     this.closing = true;
     this.toolbar.hidden = true;
 
+    this.removeNavigationKeys();
+
     this.progressListener.destroy();
     delete this.progressListener;
 
     if (!aKeepStore) {
       this.store.deleteStore(this.winID);
       this.win.removeEventListener("pagehide", this, true);
     } else {
       // Update the store before closing.
@@ -587,16 +621,24 @@ InspectorUI.prototype = {
           }
         }
 
         if (this.store.isEmpty()) {
           this.tabbrowser.tabContainer.removeEventListener("TabSelect", this,
                                                          false);
         }
         break;
+      case "keypress":
+        switch (event.keyCode) {
+          case this.chromeWin.KeyEvent.DOM_VK_ESCAPE:
+            this.closeInspectorUI(false);
+            event.preventDefault();
+            event.stopPropagation();
+            break;
+      }
       case "pagehide":
         win = event.originalTarget.defaultView;
         // Skip iframes/frames.
         if (!win || win.frameElement || win.top != win) {
           break;
         }
 
         win.removeEventListener(event.type, this, true);
@@ -606,28 +648,76 @@ InspectorUI.prototype = {
           this.store.deleteStore(winID);
         }
 
         if (this.store.isEmpty()) {
           this.tabbrowser.tabContainer.removeEventListener("TabSelect", this,
                                                          false);
         }
         break;
-      case "keypress":
-        switch (event.keyCode) {
-          case this.chromeWin.KeyEvent.DOM_VK_ESCAPE:
-            this.closeInspectorUI(false);
-            event.preventDefault();
-            event.stopPropagation();
-            break;
+    }
+  },
+
+  /*
+   * handles "keypress" events.
+  */
+  onKeypress: function IUI_onKeypress(event)
+  {
+    let node = null;
+    let bc = this.breadcrumbs;
+    switch (event.keyCode) {
+      case this.chromeWin.KeyEvent.DOM_VK_LEFT:
+        if (bc.currentIndex != 0)
+          node = bc.nodeHierarchy[bc.currentIndex - 1].node;
+        if (node && this.highlighter.isNodeHighlightable(node))
+          this.highlighter.highlight(node);
+        event.preventDefault();
+        event.stopPropagation();
+        break;
+      case this.chromeWin.KeyEvent.DOM_VK_RIGHT:
+        if (bc.currentIndex < bc.nodeHierarchy.length - 1)
+          node = bc.nodeHierarchy[bc.currentIndex + 1].node;
+        if (node && this.highlighter.isNodeHighlightable(node)) {
+          this.highlighter.highlight(node);
         }
+        event.preventDefault();
+        event.stopPropagation();
+        break;
+      case this.chromeWin.KeyEvent.DOM_VK_UP:
+        if (this.selection) {
+          // Find a previous sibling that is highlightable.
+          node = this.selection.previousSibling;
+          while (node && !this.highlighter.isNodeHighlightable(node)) {
+            node = node.previousSibling;
+          }
+        }
+        if (node && this.highlighter.isNodeHighlightable(node)) {
+          this.highlighter.highlight(node, true);
+        }
+        event.preventDefault();
+        event.stopPropagation();
+        break;
+      case this.chromeWin.KeyEvent.DOM_VK_DOWN:
+        if (this.selection) {
+          // Find a next sibling that is highlightable.
+          node = this.selection.nextSibling;
+          while (node && !this.highlighter.isNodeHighlightable(node)) {
+            node = node.nextSibling;
+          }
+        }
+        if (node && this.highlighter.isNodeHighlightable(node)) {
+          this.highlighter.highlight(node, true);
+        }
+        event.preventDefault();
+        event.stopPropagation();
         break;
     }
   },
 
+
   /////////////////////////////////////////////////////////////////////////
   //// CssRuleView methods
 
   /**
    * Is the cssRuleView open?
    */
   isRuleViewOpen: function IUI_isRuleViewOpen()
   {
@@ -1715,16 +1805,18 @@ HTMLBreadcrumbs.prototype = {
   setCursor: function BC_setCursor(aIdx)
   {
     // Unselect the previously selected button
     if (this.currentIndex > -1 && this.currentIndex < this.nodeHierarchy.length) {
       this.nodeHierarchy[this.currentIndex].button.removeAttribute("checked");
     }
     if (aIdx > -1) {
       this.nodeHierarchy[aIdx].button.setAttribute("checked", "true");
+      if (this.hadFocus)
+        this.nodeHierarchy[aIdx].button.focus();
     }
     this.currentIndex = aIdx;
   },
 
   /**
    * Get the index of the node in the cache.
    *
    * @param aNode
@@ -1890,16 +1982,20 @@ HTMLBreadcrumbs.prototype = {
 
   /**
    * Update the breadcrumbs display when a new node is selected.
    */
   update: function BC_update()
   {
     this.menu.hidePopup();
 
+    let cmdDispatcher = this.IUI.chromeDoc.commandDispatcher;
+    this.hadFocus = (cmdDispatcher.focusedElement &&
+                     cmdDispatcher.focusedElement.parentNode == this.container);
+
     let selection = this.IUI.selection;
     let idx = this.indexOf(selection);
 
     // Is the node already displayed in the breadcrumbs?
     if (idx > -1) {
       // Yes. We select it.
       this.setCursor(idx);
     } else {
@@ -1919,17 +2015,18 @@ HTMLBreadcrumbs.prototype = {
       idx = this.indexOf(selection);
       this.setCursor(idx);
     }
     // Add the first child of the very last node of the breadcrumbs if possible.
     this.ensureFirstChild();
 
     // Make sure the selected node and its neighbours are visible.
     this.scroll();
-  }
+  },
+
 }
 
 /////////////////////////////////////////////////////////////////////////
 //// Initializers
 
 XPCOMUtils.defineLazyGetter(InspectorUI.prototype, "strings",
   function () {
     return Services.strings.createBundle(
--- a/browser/devtools/highlighter/test/Makefile.in
+++ b/browser/devtools/highlighter/test/Makefile.in
@@ -64,16 +64,17 @@ include $(topsrcdir)/config/rules.mk
 		browser_inspector_bug_672902_keyboard_shortcuts.js \
 		browser_inspector_keybindings.js \
 		browser_inspector_breadcrumbs.html \
 		browser_inspector_breadcrumbs.js \
 		browser_inspector_bug_699308_iframe_navigation.js \
 		browser_inspector_changes.js \
 		browser_inspector_ruleviewstore.js \
 		browser_inspector_duplicate_ruleview.js \
+		browser_inspector_invalidate.js \
 		head.js \
 		$(NULL)
 
 # Disabled due to constant failures
 # 		browser_inspector_treePanel_click.js \
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/highlighter/test/browser_inspector_invalidate.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let doc;
+let div;
+
+function createDocument()
+{
+  div = doc.createElement("div");
+  div.setAttribute("style", "width: 100px; height: 100px;");
+  doc.body.appendChild(div);
+
+  Services.obs.addObserver(runTest,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+  InspectorUI.toggleInspectorUI();
+}
+
+function runTest(subject)
+{
+  Services.obs.removeObserver(runTest,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+
+  InspectorUI.highlighter.highlight(div);
+
+  executeSoon(function() {
+    let veilBoxDims = InspectorUI.highlighter.veilTransparentBox;
+    is(veilBoxDims.style.width, "100px", "selection has the right width");
+
+    div.style.width = "200px";
+    setTimeout(function () {
+      let veilBoxDims = InspectorUI.highlighter.veilTransparentBox;
+      is(veilBoxDims.style.width, "200px", "selection updated");
+      InspectorUI.closeInspectorUI();
+      gBrowser.removeCurrentTab();
+      finish();
+    }, 1000);
+  });
+}
+
+function test()
+{
+  waitForExplicitFinish();
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function() {
+    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+    doc = content.document;
+    waitForFocus(createDocument, content);
+  }, true);
+
+  content.location = "data:text/html,basic tests for inspector";
+}
+
--- a/browser/devtools/highlighter/test/browser_inspector_ruleviewstore.js
+++ b/browser/devtools/highlighter/test/browser_inspector_ruleviewstore.js
@@ -87,18 +87,17 @@ function inspectorUIOpen1()
 
 function ruleViewOpened1()
 {
   let prop = InspectorUI.ruleView._elementStyle.rules[0].textProps[0];
   is(prop.name, "background-color", "First prop is the background color prop.");
   prop.setEnabled(false);
 
   // Open second tab and switch to it
-  tab2 = gBrowser.addTab();
-  gBrowser.selectedTab = tab2;
+  gBrowser.selectedTab = gBrowser.addTab();
 
   gBrowser.selectedBrowser.addEventListener("load", function(evt) {
     gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee,
                                                  true);
     waitForFocus(inspectorTabOpen2, content);
   }, true);
   content.location = "data:text/html,<p>tab 2: the inspector should close now";
 }
--- a/browser/devtools/highlighter/test/head.js
+++ b/browser/devtools/highlighter/test/head.js
@@ -32,17 +32,19 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. 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 ***** */
 
 const Cu = Components.utils;
-Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tempScope);
+let LayoutHelpers = tempScope.LayoutHelpers;
 
 function isHighlighting()
 {
   let veil = InspectorUI.highlighter.veilTransparentBox;
   return !(veil.style.visibility == "hidden");
 }
 
 function getHighlitNode()
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug684546_reset_undo.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug684546_reset_undo.js
@@ -1,14 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-Cu.import("resource://gre/modules/NetUtil.jsm");
-Cu.import("resource://gre/modules/FileUtils.jsm");
+let tempScope = {};
+Cu.import("resource://gre/modules/NetUtil.jsm", tempScope);
+Cu.import("resource://gre/modules/FileUtils.jsm", tempScope);
+let NetUtil = tempScope.NetUtil;
+let FileUtils = tempScope.FileUtils;
 
 // Reference to the Scratchpad chrome window object.
 let gScratchpadWindow;
 
 // Reference to the Scratchpad object.
 let gScratchpad;
 
 // Reference to the temporary nsIFile we will work with.
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug_653427_confirm_close.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug_653427_confirm_close.js
@@ -1,15 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-
-Cu.import("resource://gre/modules/NetUtil.jsm");
-Cu.import("resource://gre/modules/FileUtils.jsm");
+let tempScope = {};
+Cu.import("resource://gre/modules/NetUtil.jsm", tempScope);
+Cu.import("resource://gre/modules/FileUtils.jsm", tempScope);
+let NetUtil = tempScope.NetUtil;
+let FileUtils = tempScope.FileUtils;
 
 // only finish() when correct number of tests are done
 const expected = 5;
 var count = 0;
 function done()
 {
   if (++count == expected) {
     cleanup();
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug_699130_edit_ui_updates.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug_699130_edit_ui_updates.js
@@ -1,15 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-Cu.import("resource:///modules/source-editor.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/source-editor.jsm", tempScope);
+let SourceEditor = tempScope.SourceEditor;
 
 function test()
 {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
     gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
--- a/browser/devtools/scratchpad/test/browser_scratchpad_contexts.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_contexts.js
@@ -70,16 +70,17 @@ function runTests()
   ok(sp.getText(), "window.foobarBug636725 = 'aloha2';",
      "setText() worked");
 
   ok(!window.foobarBug636725, "no window.foobarBug636725");
 
   sp.run();
 
   is(window.foobarBug636725, "aloha2", "window.foobarBug636725 has been set");
+  delete window.foobarBug636725;
 
   sp.setText("gBrowser", 7);
 
   ok(sp.getText(), "window.gBrowser",
      "setText() worked with no end for the replace range");
 
   is(typeof sp.run()[2].addTab, "function",
      "chrome context has access to chrome objects");
--- a/browser/devtools/scratchpad/test/browser_scratchpad_files.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_files.js
@@ -1,14 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-Cu.import("resource://gre/modules/NetUtil.jsm");
-Cu.import("resource://gre/modules/FileUtils.jsm");
+let tempScope = {};
+Cu.import("resource://gre/modules/NetUtil.jsm", tempScope);
+Cu.import("resource://gre/modules/FileUtils.jsm", tempScope);
+let NetUtil = tempScope.NetUtil;
+let FileUtils = tempScope.FileUtils;
 
 // Reference to the Scratchpad object.
 let gScratchpad;
 
 // Reference to the temporary nsIFile we will work with.
 let gFile;
 
 // The temporary file content.
--- a/browser/devtools/shared/SplitView.jsm
+++ b/browser/devtools/shared/SplitView.jsm
@@ -64,21 +64,16 @@ function SplitView(aRoot)
   this._root = aRoot;
   this._controller = aRoot.querySelector(".splitview-controller");
   this._nav = aRoot.querySelector(".splitview-nav");
   this._side = aRoot.querySelector(".splitview-side-details");
   this._activeSummary = null
 
   this._mql = aRoot.ownerDocument.defaultView.matchMedia(LANDSCAPE_MEDIA_QUERY);
 
-  this._filter = aRoot.querySelector(".splitview-filter");
-  if (this._filter) {
-    this._setupFilterBox();
-  }
-
   // items list focus and search-on-type handling
   this._nav.addEventListener("keydown", function onKeyCatchAll(aEvent) {
     function getFocusedItemWithin(nav) {
       let node = nav.ownerDocument.activeElement;
       while (node && node.parentNode != nav) {
         node = node.parentNode;
       }
       return node;
@@ -111,23 +106,16 @@ function SplitView(aRoot)
     if (newFocusOrdinal !== undefined) {
       aEvent.stopPropagation();
       let el = this.getSummaryElementByOrdinal(newFocusOrdinal);
       if (el) {
         el.focus();
       }
       return false;
     }
-
-    // search-on-type when any non-whitespace character is pressed while list
-    // has the focus
-    if (this._filter &&
-        !/\s/.test(String.fromCharCode(aEvent.which))) {
-      this._filter.focus();
-    }
   }.bind(this), false);
 }
 
 SplitView.prototype = {
   /**
     * Retrieve whether the UI currently has a landscape orientation.
     *
     * @return boolean
@@ -222,20 +210,16 @@ SplitView.prototype = {
    *     - function(DOMElement summary, DOMElement details, object data) onCreate
    *         Called when the item has been added.
    *     - function(summary, details, data) onShow
    *         Called when the item is shown/active.
    *     - function(summary, details, data) onHide
    *         Called when the item is hidden/inactive.
    *     - function(summary, details, data) onDestroy
    *         Called when the item has been removed.
-   *     - function(summary, details, data, query) onFilterBy
-   *         Called when the user performs a filtering search.
-   *         If the function returns false, the item does not match query
-   *         string and will be hidden.
    *     - object data
    *         Object to pass to the callbacks above.
    *     - number ordinal
    *         Items with a lower ordinal are displayed before those with a
    *         higher ordinal.
    */
   appendItem: function ASV_appendItem(aSummary, aDetails, aOptions)
   {
@@ -323,81 +307,16 @@ SplitView.prototype = {
   removeAll: function ASV_removeAll()
   {
     while (this._nav.hasChildNodes()) {
       this.removeItem(this._nav.firstChild);
     }
   },
 
   /**
-    * Filter items by given string.
-    * Matching is performed on every item by calling onFilterBy when defined
-    * and then by searching aQuery in the summary element's text item.
-    * Non-matching item is hidden.
-    *
-    * If no item matches, 'splitview-all-filtered' class is set on the filter
-    * input element and the splitview-nav element.
-    *
-    * @param string aQuery
-    *        The query string. Use null to reset (no filter).
-    * @return number
-    *         The number of filtered (non-matching) item.
-    */
-  filterItemsBy: function ASV_filterItemsBy(aQuery)
-  {
-    if (!this._nav.hasChildNodes()) {
-      return 0;
-    }
-    if (aQuery) {
-      aQuery = aQuery.trim();
-    }
-    if (!aQuery) {
-      for (let i = 0; i < this._nav.childNodes.length; ++i) {
-        this._nav.childNodes[i].classList.remove("splitview-filtered");
-      }
-      this._filter.classList.remove("splitview-all-filtered");
-      this._nav.classList.remove("splitview-all-filtered");
-      return 0;
-    }
-
-    let count = 0;
-    let filteredCount = 0;
-    for (let i = 0; i < this._nav.childNodes.length; ++i) {
-      let summary = this._nav.childNodes[i];
-
-      let matches = false;
-      let binding = summary.getUserData(BINDING_USERDATA);
-      if (binding.onFilterBy) {
-        matches = binding.onFilterBy(summary, binding._details, binding.data, aQuery);
-      }
-      if (!matches) { // try text content
-        let content = summary.textContent.toUpperCase();
-        matches = (content.indexOf(aQuery.toUpperCase()) > -1);
-      }
-
-      count++;
-      if (!matches) {
-        summary.classList.add("splitview-filtered");
-        filteredCount++;
-      } else {
-        summary.classList.remove("splitview-filtered");
-      }
-    }
-
-    if (count > 0 && filteredCount == count) {
-      this._filter.classList.add("splitview-all-filtered");
-      this._nav.classList.add("splitview-all-filtered");
-    } else {
-      this._filter.classList.remove("splitview-all-filtered");
-      this._nav.classList.remove("splitview-all-filtered");
-    }
-    return filteredCount;
-  },
-
-  /**
    * Set the item's CSS class name.
    * This sets the class on both the summary and details elements, retaining
    * any SplitView-specific classes.
    *
    * @param DOMElement aSummary
    *        Summary element of the item to set.
    * @param string aClassName
    *        One or more space-separated CSS classes.
@@ -410,44 +329,9 @@ SplitView.prototype = {
     viewSpecific = aSummary.className.match(/(splitview\-[\w-]+)/g);
     viewSpecific = viewSpecific ? viewSpecific.join(" ") : "";
     aSummary.className = viewSpecific + " " + aClassName;
 
     viewSpecific = binding._details.className.match(/(splitview\-[\w-]+)/g);
     viewSpecific = viewSpecific ? viewSpecific.join(" ") : "";
     binding._details.className = viewSpecific + " " + aClassName;
   },
-
-  /**
-   * Set up filter search box.
-   */
-  _setupFilterBox: function ASV__setupFilterBox()
-  {
-    let clearFilter = function clearFilter(aEvent) {
-      this._filter.value = "";
-      this.filterItemsBy("");
-      return false;
-    }.bind(this);
-
-    this._filter.addEventListener("command", function onFilterInput(aEvent) {
-      this.filterItemsBy(this._filter.value);
-    }.bind(this), false);
-
-    this._filter.addEventListener("keyup", function onFilterKeyUp(aEvent) {
-      if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
-        clearFilter();
-      }
-      if (aEvent.keyCode == aEvent.DOM_VK_ENTER ||
-          aEvent.keyCode == aEvent.DOM_VK_RETURN) {
-        // autofocus matching item if there is only one
-        let matches = this._nav.querySelectorAll("* > li:not(.splitview-filtered)");
-        if (matches.length == 1) {
-          this.activeSummary = matches[0];
-        }
-      }
-    }.bind(this), false);
-
-    let clearButtons = this._root.querySelectorAll(".splitview-filter-clearButton");
-    for (let i = 0; i < clearButtons.length; ++i) {
-      clearButtons[i].addEventListener("click", clearFilter, false);
-    }
-  }
 };
--- a/browser/devtools/shared/splitview.css
+++ b/browser/devtools/shared/splitview.css
@@ -53,16 +53,17 @@ box,
 .splitview-controller,
 .splitview-main {
   -moz-box-flex: 0;
 }
 
 .splitview-controller {
   min-height: 3em;
   max-height: 14em;
+  max-width: 400px;
 }
 
 .splitview-nav {
   display: -moz-box;
   overflow-x: hidden;
   overflow-y: auto;
 }
 
@@ -118,9 +119,13 @@ ol.splitview-nav > li.splitview-filtered
 @media (max-aspect-ratio: 5/3) {
   #splitview-details-toolbar {
     display: none;
   }
 
   .splitview-portrait-resizer {
     display: -moz-box;
   }
+
+  .splitview-controller {
+    max-width: none;
+  }
 }
--- a/browser/devtools/shared/test/browser_promise_basic.js
+++ b/browser/devtools/shared/test/browser_promise_basic.js
@@ -1,14 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that our Promise implementation works properly
 
-Cu.import("resource:///modules/devtools/Promise.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/devtools/Promise.jsm", tempScope);
+let Promise = tempScope.Promise;
 
 function test() {
   addTab("about:blank", function() {
     info("Starting Promise Tests");
     testBasic();
   });
 }
 
--- a/browser/devtools/shared/test/browser_templater_basic.js
+++ b/browser/devtools/shared/test/browser_templater_basic.js
@@ -1,15 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that the DOM Template engine works properly
 
-Cu.import("resource:///modules/devtools/Templater.jsm");
-Cu.import("resource:///modules/devtools/Promise.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/devtools/Templater.jsm", tempScope);
+Cu.import("resource:///modules/devtools/Promise.jsm", tempScope);
+let template = tempScope.template;
+let Promise = tempScope.Promise;
 
 function test() {
   addTab("http://example.com/browser/browser/devtools/shared/test/browser_templater_basic.html", function() {
     info("Starting DOM Templater Tests");
     runTest(0);
   });
 }
 
--- a/browser/devtools/sourceeditor/orion/README
+++ b/browser/devtools/sourceeditor/orion/README
@@ -10,16 +10,18 @@ To upgrade Orion to a newer version see 
 
 Orion version: git clone from 2011-12-09
                commit hash d8a6dc01d9c561d6eb99f03b64c8c78ce785c59d
   + patch for Eclipse Bug 366312 - right-clicking outside of the selection causes the caret to move
     https://github.com/mihaisucan/orion.client/tree/bug-366312
       see https://bugs.eclipse.org/bugs/show_bug.cgi?id=366312
   + patch for Mozilla Bug 711737 - Orion should support all the CSS properties from CSS1, CSS2, CSS2.1 and CSS3
     https://bugzilla.mozilla.org/show_bug.cgi?id=711737
+  + patch for Mozilla Bug 719028 - Style Editor does not highlight a few CSS2.0 and CSS3 properties
+    https://bugzilla.mozilla.org/show_bug.cgi?id=719028
 
 # License
 
 The following files are licensed according to the contents in the LICENSE
 file:
   orion.js
   orion.css
 
--- a/browser/devtools/sourceeditor/orion/orion.js
+++ b/browser/devtools/sourceeditor/orion/orion.js
@@ -5,16 +5,17 @@
  * available under the terms of the Eclipse Public License v1.0 
  * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
  * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
  * 
  * Contributors: 
  *		Felipe Heidrich (IBM Corporation) - initial API and implementation
  *		Silenio Quarti (IBM Corporation) - initial API and implementation
  *		Mihai Sucan (Mozilla Foundation) - fix for Bug#364214
+ *		Alex Lakatos (Mozilla Contributor) - fix for Mozilla Bug#719028
  */
 
 /*global window */
 
 /**
  * Evaluates the definition function and mixes in the returned module with
  * the module specified by <code>moduleName</code>.
  * <p>
@@ -10483,23 +10484,24 @@ define(['orion/textview/annotations'], f
 		 "border-radius", "border-right", "border-right-color", "border-right-style", "border-right-width", "border-spacing", "border-style",
 		 "border-top", "border-top-color", "border-top-left-radius", "border-top-right-radius", "border-top-style", "border-top-width",
 		 "border-width", "bottom", "box-align", "box-decoration-break", "box-direction", "box-flex", "box-flex-group", "box-lines",
 		 "box-ordinal-group", "box-orient", "box-pack", "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
 		 "caption-side", "clear", "clip", "color", "color-profile", "column-count", "column-fill", "column-gap", "column-rule",
 		 "column-rule-color", "column-rule-style", "column-rule-width", "column-span", "column-width", "columns", "content", "counter-increment",
 		 "counter-reset", "crop", "cue", "cue-after", "cue-before", "cursor", "direction", "display", "dominant-baseline",
 		 "drop-initial-after-adjust", "drop-initial-after-align", "drop-initial-before-adjust", "drop-initial-before-align", "drop-initial-size",
-		 "drop-initial-value", "elevation", "empty-cells", "fit", "fit-position", "float", "float-offset", "font", "font-family", "font-size",
-		 "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "grid-columns", "grid-rows", "hanging-punctuation",
-		 "height", "hyphenate-after", "hyphenate-before", "hyphenate-character", "hyphenate-lines", "hyphenate-resource", "hyphens", "icon",
-		 "image-orientation", "image-rendering", "image-resolution", "inline-box-align", "left", "letter-spacing", "line-height",
-		 "line-stacking", "line-stacking-ruby", "line-stacking-shift", "line-stacking-strategy", "list-style", "list-style-image",
-		 "list-style-position", "list-style-type", "margin", "margin-bottom", "margin-left", "margin-right", "margin-top", "mark", "mark-after",
-		 "mark-before", "marks", "marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
+		 "drop-initial-value", "elevation", "empty-cells", "fit", "fit-position", "flex-align", "flex-flow", "flex-inline-pack", "flex-order",
+		 "flex-pack", "float", "float-offset", "font", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style",
+		 "font-variant", "font-weight", "grid-columns", "grid-rows", "hanging-punctuation", "height", "hyphenate-after",
+		 "hyphenate-before", "hyphenate-character", "hyphenate-lines", "hyphenate-resource", "hyphens", "icon", "image-orientation",
+		 "image-rendering", "image-resolution", "inline-box-align", "left", "letter-spacing", "line-height", "line-stacking",
+		 "line-stacking-ruby", "line-stacking-shift", "line-stacking-strategy", "list-style", "list-style-image", "list-style-position",
+		 "list-style-type", "margin", "margin-bottom", "margin-left", "margin-right", "margin-top", "mark", "mark-after", "mark-before",
+		 "marker-offset", "marks", "marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
 		 "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index", "nav-left", "nav-right", "nav-up", "opacity", "orphans",
 		 "outline", "outline-color", "outline-offset", "outline-style", "outline-width", "overflow", "overflow-style", "overflow-x",
 		 "overflow-y", "padding", "padding-bottom", "padding-left", "padding-right", "padding-top", "page", "page-break-after", "page-break-before",
 		 "page-break-inside", "page-policy", "pause", "pause-after", "pause-before", "perspective", "perspective-origin", "phonemes", "pitch",
 		 "pitch-range", "play-during", "position", "presentation-level", "punctuation-trim", "quotes", "rendering-intent", "resize",
 		 "rest", "rest-after", "rest-before", "richness", "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang", "ruby-position",
 		 "ruby-span", "size", "speak", "speak-header", "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", "table-layout",
 		 "target", "target-name", "target-new", "target-position", "text-align", "text-align-last", "text-decoration", "text-emphasis",
--- a/browser/devtools/sourceeditor/test/browser_bug650345_find.js
+++ b/browser/devtools/sourceeditor/test/browser_bug650345_find.js
@@ -1,15 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-Cu.import("resource:///modules/source-editor.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/source-editor.jsm", tempScope);
+let SourceEditor = tempScope.SourceEditor;
 
 let testWin;
 let editor;
 
 function test()
 {
   waitForExplicitFinish();
 
--- a/browser/devtools/sourceeditor/test/browser_bug684546_reset_undo.js
+++ b/browser/devtools/sourceeditor/test/browser_bug684546_reset_undo.js
@@ -1,15 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-Cu.import("resource:///modules/source-editor.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/source-editor.jsm", tempScope);
+let SourceEditor = tempScope.SourceEditor;
 
 let testWin;
 let editor;
 
 function test()
 {
   waitForExplicitFinish();
 
--- a/browser/devtools/sourceeditor/test/browser_bug684862_paste_html.js
+++ b/browser/devtools/sourceeditor/test/browser_bug684862_paste_html.js
@@ -1,15 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-Cu.import("resource:///modules/source-editor.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/source-editor.jsm", tempScope);
+let SourceEditor = tempScope.SourceEditor;
 
 let testWin;
 let editor;
 
 function test()
 {
   waitForExplicitFinish();
 
--- a/browser/devtools/sourceeditor/test/browser_bug687160_line_api.js
+++ b/browser/devtools/sourceeditor/test/browser_bug687160_line_api.js
@@ -1,15 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-Cu.import("resource:///modules/source-editor.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/source-editor.jsm", tempScope);
+let SourceEditor = tempScope.SourceEditor;
 
 let testWin;
 let editor;
 
 function test()
 {
   waitForExplicitFinish();
 
--- a/browser/devtools/sourceeditor/test/browser_bug687568_pagescroll.js
+++ b/browser/devtools/sourceeditor/test/browser_bug687568_pagescroll.js
@@ -1,15 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-Cu.import("resource:///modules/source-editor.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/source-editor.jsm", tempScope);
+let SourceEditor = tempScope.SourceEditor;
 
 let testWin;
 let editor;
 
 function test()
 {
   let component = Services.prefs.getCharPref(SourceEditor.PREFS.COMPONENT);
   if (component != "orion") {
--- a/browser/devtools/sourceeditor/test/browser_bug687573_vscroll.js
+++ b/browser/devtools/sourceeditor/test/browser_bug687573_vscroll.js
@@ -1,15 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-Cu.import("resource:///modules/source-editor.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/source-editor.jsm", tempScope);
+let SourceEditor = tempScope.SourceEditor;
 
 let testWin;
 let editor;
 
 function test()
 {
   let component = Services.prefs.getCharPref(SourceEditor.PREFS.COMPONENT);
   if (component == "textarea") {
--- a/browser/devtools/sourceeditor/test/browser_bug687580_drag_and_drop.js
+++ b/browser/devtools/sourceeditor/test/browser_bug687580_drag_and_drop.js
@@ -1,15 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-Cu.import("resource:///modules/source-editor.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/source-editor.jsm", tempScope);
+let SourceEditor = tempScope.SourceEditor;
 
 let testWin;
 let editor;
 
 function test()
 {
   let component = Services.prefs.getCharPref(SourceEditor.PREFS.COMPONENT);
   if (component != "orion") {
--- a/browser/devtools/sourceeditor/test/browser_bug695035_middle_click_paste.js
+++ b/browser/devtools/sourceeditor/test/browser_bug695035_middle_click_paste.js
@@ -1,15 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-Cu.import("resource:///modules/source-editor.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/source-editor.jsm", tempScope);
+let SourceEditor = tempScope.SourceEditor;
 
 let testWin;
 let editor;
 
 function test()
 {
   if (Services.appinfo.OS != "Linux") {
     ok(true, "this test only applies to Linux, skipping.")
--- a/browser/devtools/sourceeditor/test/browser_sourceeditor_initialization.js
+++ b/browser/devtools/sourceeditor/test/browser_sourceeditor_initialization.js
@@ -1,15 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-Cu.import("resource:///modules/source-editor.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/source-editor.jsm", tempScope);
+let SourceEditor = tempScope.SourceEditor;
 
 let testWin;
 let testDoc;
 let editor;
 
 function test()
 {
   waitForExplicitFinish();
--- a/browser/devtools/styleeditor/StyleEditor.jsm
+++ b/browser/devtools/styleeditor/StyleEditor.jsm
@@ -224,16 +224,18 @@ StyleEditor.prototype = {
       placeholderText: this._state.text, //! this is initialText (bug 680371)
       showLineNumbers: true,
       mode: SourceEditor.MODES.CSS,
       readOnly: this._state.readOnly,
       keys: this._getKeyBindings()
     };
 
     sourceEditor.init(aElement, config, function onSourceEditorReady() {
+      setupBracketCompletion(sourceEditor);
+
       sourceEditor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
                                     function onTextChanged(aEvent) {
         this.updateStyleSheet();
       }.bind(this));
 
       this._sourceEditor = sourceEditor;
 
       if (this._focusOnSourceEditorReady) {
@@ -1127,8 +1129,53 @@ function prettifyCSS(aText)
   * @param string aText
   * @param number aCount
   * @return string
   */
 function repeat(aText, aCount)
 {
   return (new Array(aCount + 1)).join(aText);
 }
+
+/**
+ * Set up bracket completion on a given SourceEditor.
+ * This automatically closes the following CSS brackets: "{", "(", "["
+ *
+ * @param SourceEditor aSourceEditor
+ */
+function setupBracketCompletion(aSourceEditor)
+{
+  let editorElement = aSourceEditor.editorElement;
+  let pairs = {
+    123: { // {
+      closeString: "}",
+      closeKeyCode: Ci.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET
+    },
+    40: { // (
+      closeString: ")",
+      closeKeyCode: Ci.nsIDOMKeyEvent.DOM_VK_0
+    },
+    91: { // [
+      closeString: "]",
+      closeKeyCode: Ci.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET
+    },
+  };
+
+  editorElement.addEventListener("keypress", function onKeyPress(aEvent) {
+    let pair = pairs[aEvent.charCode];
+    if (!pair) {
+      return true;
+    }
+
+    // We detected an open bracket, sending closing character
+    let keyCode = pair.closeKeyCode;
+    let charCode = pair.closeString.charCodeAt(0);
+    let modifiers = 0;
+    let utils = editorElement.ownerDocument.defaultView.
+                  QueryInterface(Ci.nsIInterfaceRequestor).
+                  getInterface(Ci.nsIDOMWindowUtils);
+    let handled = utils.sendKeyEvent("keydown", keyCode, 0, modifiers);
+    utils.sendKeyEvent("keypress", 0, charCode, modifiers, !handled);
+    utils.sendKeyEvent("keyup", keyCode, 0, modifiers);
+    // and rewind caret
+    aSourceEditor.setCaretOffset(aSourceEditor.getCaretOffset() - 1);
+  }, false);
+}
--- a/browser/devtools/styleeditor/StyleEditorChrome.jsm
+++ b/browser/devtools/styleeditor/StyleEditorChrome.jsm
@@ -397,17 +397,19 @@ StyleEditorChrome.prototype = {
    */
   _updateSummaryForEditor: function SEC__updateSummaryForEditor(aEditor, aSummary)
   {
     let summary = aSummary || this.getSummaryElementForEditor(aEditor);
     let ruleCount = aEditor.styleSheet.cssRules.length;
 
     this._view.setItemClassName(summary, aEditor.flags);
 
-    text(summary, ".stylesheet-name", aEditor.getFriendlyName());
+    let label = summary.querySelector(".stylesheet-name > label");
+    label.setAttribute("value", aEditor.getFriendlyName());
+
     text(summary, ".stylesheet-title", aEditor.styleSheet.title || "");
     text(summary, ".stylesheet-rule-count",
       PluralForm.get(ruleCount, _("ruleCount.label")).replace("#1", ruleCount));
     text(summary, ".stylesheet-error-message", aEditor.errorMessage);
   },
 
   /**
    * IStyleEditorActionListener implementation
--- a/browser/devtools/styleeditor/styleeditor.css
+++ b/browser/devtools/styleeditor/styleeditor.css
@@ -15,16 +15,17 @@
  * The Original Code is Style Editor code.
  *
  * The Initial Developer of the Original Code is Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2011
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Cedric Vivier <cedricv@neonux.com> (original author)
+ *   Paul Rouget <paul@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
@@ -59,32 +60,27 @@ li.error > .stylesheet-info > .styleshee
   -moz-box-flex: 1;
 }
 
 .stylesheet-info > h1 {
   -moz-box-flex: 1;
 }
 
 .stylesheet-name {
-  /* clip the text at the beginning */
-  display: -moz-box;
-  direction: rtl;
-  text-align: left;
-  overflow: hidden;
+  outline: none;
+}
+
+.stylesheet-name > label {
+  cursor: pointer;
 }
 
 .splitview-nav > li > hgroup.stylesheet-info {
   -moz-box-pack: center;
 }
 
-.splitview-nav:-moz-locale-dir(ltr) > li.unsaved > hgroup .stylesheet-name:before,
-.splitview-nav:-moz-locale-dir(rtl) > li.unsaved > hgroup .stylesheet-name:after {
-  content: "* ";
-}
-
 .stylesheet-enabled {
   display: -moz-box;
 }
 
 .stylesheet-saveButton {
   display: none;
 }
 
@@ -102,17 +98,17 @@ li:hover > hgroup > .stylesheet-more > h
 @media (max-aspect-ratio: 5/3) {
   li.splitview-active > hgroup > .stylesheet-more > .stylesheet-rule-count,
   li:hover > hgroup > .stylesheet-more > .stylesheet-rule-count {
     display: none;
   }
 
   .stylesheet-more {
     -moz-box-flex: 1;
-    -moz-box-direction: reverse;
+    -moz-box-pack: end;
   }
 
   .splitview-nav > li > hgroup.stylesheet-info {
     -moz-box-orient: horizontal;
     -moz-box-flex: 1;
   }
 
   .stylesheet-more > spacer {
--- a/browser/devtools/styleeditor/styleeditor.xul
+++ b/browser/devtools/styleeditor/styleeditor.xul
@@ -49,84 +49,84 @@
         xmlns="http://www.w3.org/1999/xhtml"
         id="style-editor-chrome-window"
         title="&window.title;"
         windowtype="Tools:StyleEditor"
         width="800" height="280"
         persist="screenX screenY width height sizemode">
 <xul:script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
 
+<xul:commandset id="style-editor-commandset">
+  <xul:command id="style-editor-cmd-close" oncommand="window.close();"/>
+</xul:commandset>
+
+<xul:keyset id="style-editor-keyset">
+  <xul:key id="style-editor-key-close"
+           key="&closeCmd.key;"
+           command="style-editor-cmd-close"
+           modifiers="accel"/>
+</xul:keyset>
+
 <xul:box id="style-editor-chrome" class="splitview-root loading">
-  <xul:box class="splitview-controller" id="stylesheets-controller" persist="width height">
+  <xul:box class="splitview-controller">
     <xul:box class="splitview-main">
       <xul:toolbar class="devtools-toolbar">
         <xul:toolbarbutton class="style-editor-newButton devtools-toolbarbutton"
                     accesskey="&newButton.accesskey;"
                     tooltiptext="&newButton.tooltip;"
                     label="&newButton.label;"
                     disabled="true"/>
         <xul:toolbarbutton class="style-editor-importButton devtools-toolbarbutton"
                     accesskey="&importButton.accesskey;"
                     tooltiptext="&importButton.tooltip;"
                     label="&importButton.label;"
                     disabled="true"/>
-        <xul:spacer flex="1"/>
-        <xul:textbox class="splitview-filter devtools-searchinput"
-                     type="search" flex="1"
-                     tooltiptext="&searchInput.tooltip;"
-                     placeholder="&searchInput.placeholder;"/>
       </xul:toolbar>
     </xul:box>
-    <xul:box class="splitview-nav-container">
+    <xul:box id="splitview-resizer-target" class="splitview-nav-container"
+             persist="width height">
       <ol class="splitview-nav" tabindex="0"></ol>
       <div class="splitview-nav placeholder empty">
         <p><strong>&noStyleSheet.label;</strong></p>
         <p>&noStyleSheet-tip-start.label;
           <a href="#"
              class="style-editor-newButton">&noStyleSheet-tip-action.label;</a>
           &noStyleSheet-tip-end.label;</p>
       </div>
-      <div class="splitview-nav placeholder all-filtered">
-        <p><strong>&searchNoResults.label;</strong></p>
-        <p>
-          <a href="#"
-             class="splitview-filter-clearButton">&searchClearButton.label;</a>
-        </p>
-      </div>
     </xul:box> <!-- .splitview-nav-container -->
   </xul:box>   <!-- .splitview-controller -->
   <xul:box class="splitview-side-details"/>
 
   <div id="splitview-templates" hidden="true">
     <li id="splitview-tpl-summary-stylesheet" tabindex="0">
       <a class="stylesheet-enabled" tabindex="0" href="#"
          title="&visibilityToggle.tooltip;"
          accesskey="&saveButton.accesskey;"></a>
       <hgroup class="stylesheet-info">
-        <h1><a class="stylesheet-name" href="#"></a></h1>
+        <h1><a class="stylesheet-name" href="#"><xul:label crop="start"/></a></h1>
         <div class="stylesheet-more">
           <h3 class="stylesheet-title"></h3>
           <h3 class="stylesheet-rule-count"></h3>
           <h3 class="stylesheet-error-message"></h3>
           <xul:spacer/>
           <h3><a class="stylesheet-saveButton" href="#"
                  title="&saveButton.tooltip;"
                  accesskey="&saveButton.accesskey;">&saveButton.label;</a></h3>
         </div>
       </hgroup>
     </li>
 
     <xul:box id="splitview-tpl-details-stylesheet" class="splitview-details">
       <xul:resizer class="splitview-portrait-resizer"
-               dir="bottom"
-               element="stylesheets-controller"/>
+                   dir="bottom"
+                   element="splitview-resizer-target"/>
       <xul:toolbar id="splitview-details-toolbar" class="devtools-toolbar">
         <xul:resizer class="splitview-landscape-resizer"
                      dir="bottomend"
-                     element="stylesheets-controller"/>
+                     element="splitview-resizer-target"/>
       </xul:toolbar>
       <xul:box class="stylesheet-editor-input textbox"
                data-placeholder="&editorTextbox.placeholder;"/>
     </xul:box>
   </div> <!-- #splitview-templates -->
 </xul:box>   <!-- .splitview-root -->
 
 <xul:script type="application/javascript"><![CDATA[
--- a/browser/devtools/styleeditor/test/Makefile.in
+++ b/browser/devtools/styleeditor/test/Makefile.in
@@ -48,17 +48,16 @@ include $(topsrcdir)/config/rules.mk
                  browser_styleeditor_enabled.js \
                  browser_styleeditor_import.js \
                  browser_styleeditor_init.js \
                  browser_styleeditor_loading.js \
                  browser_styleeditor_new.js \
                  browser_styleeditor_pretty.js \
                  browser_styleeditor_readonly.js \
                  browser_styleeditor_reopen.js \
-                 browser_styleeditor_sv_filter.js \
                  browser_styleeditor_sv_keynav.js \
                  browser_styleeditor_sv_resize.js \
                  four.html \
                  head.js \
                  media.html \
                  media-small.css \
                  minified.html \
                  simple.css \
--- a/browser/devtools/styleeditor/test/browser_styleeditor_import.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_import.js
@@ -1,16 +1,19 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // http rather than chrome to improve coverage
 const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
 
-Components.utils.import("resource://gre/modules/FileUtils.jsm");
+let tempScope = {};
+Components.utils.import("resource://gre/modules/FileUtils.jsm", tempScope);
+let FileUtils = tempScope.FileUtils;
+
 const FILENAME = "styleeditor-import-test.css";
 const SOURCE = "body{background:red;}";
 
 
 function test()
 {
   waitForExplicitFinish();
 
--- a/browser/devtools/styleeditor/test/browser_styleeditor_init.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_init.js
@@ -80,17 +80,17 @@ function testFirstStyleSheetEditor(aChro
 
   ok(!aEditor.hasFlag("inline"),
      "first stylesheet does not have INLINE flag");
 
   let summary = aChrome.getSummaryElementForEditor(aEditor);
   ok(!summary.classList.contains("inline"),
      "first stylesheet UI does not have INLINE class");
 
-  let name = summary.querySelector(".stylesheet-name").textContent;
+  let name = summary.querySelector(".stylesheet-name > label").getAttribute("value");
   is(name, "simple.css",
      "first stylesheet's name is `simple.css`");
 
   let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
   is(parseInt(ruleCount), 1,
      "first stylesheet UI shows rule count as 1");
 
   ok(summary.classList.contains("splitview-active"),
@@ -108,17 +108,17 @@ function testSecondStyleSheetEditor(aChr
 
   ok(aEditor.hasFlag("inline"),
      "second stylesheet has INLINE flag");
 
   let summary = aChrome.getSummaryElementForEditor(aEditor);
   ok(summary.classList.contains("inline"),
      "second stylesheet UI has INLINE class");
 
-  let name = summary.querySelector(".stylesheet-name").textContent;
+  let name = summary.querySelector(".stylesheet-name > label").getAttribute("value");
   ok(/^<.*>$/.test(name),
      "second stylesheet's name is surrounded by `<>`");
 
   let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
   is(parseInt(ruleCount), 3,
      "second stylesheet UI shows rule count as 3");
 
   ok(!summary.classList.contains("splitview-active"),
--- a/browser/devtools/styleeditor/test/browser_styleeditor_new.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_new.js
@@ -1,16 +1,16 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TESTCASE_URI = TEST_BASE + "simple.html";
 
 const TRANSITION_CLASS = "moz-styleeditor-transitioning";
-
+const TESTCASE_CSS_SOURCE = "body{background-color:red;";
 
 function test()
 {
   waitForExplicitFinish();
 
   addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
     aChrome.addChromeListener({
       onContentAttach: run,
@@ -74,19 +74,23 @@ function testEditorAdded(aChrome, aEdito
         let ruleCount = summary.querySelector(".stylesheet-rule-count").textContent;
         is(parseInt(ruleCount), 0,
            "new editor initially shows 0 rules");
 
         let computedStyle = content.getComputedStyle(content.document.body, null);
         is(computedStyle.backgroundColor, "rgb(255, 255, 255)",
            "content's background color is initially white");
 
-        for each (let c in "body{background-color:red;}") {
+        for each (let c in TESTCASE_CSS_SOURCE) {
           EventUtils.synthesizeKey(c, {}, gChromeWindow);
         }
+
+        is(aEditor.sourceEditor.getText(), TESTCASE_CSS_SOURCE + "}",
+           "rule bracket has been auto-closed");
+
       }, gChromeWindow) ;
     },
 
     onUpdate: function (aEditor) {
       gUpdateCount++;
 
       ok(content.document.documentElement.classList.contains(TRANSITION_CLASS),
          "StyleEditor's transition class has been added to content");
--- a/browser/devtools/styleeditor/test/browser_styleeditor_reopen.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_reopen.js
@@ -2,17 +2,20 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // http rather than chrome to improve coverage
 const TESTCASE_URI = TEST_BASE_HTTP + "simple.gz.html";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
-Components.utils.import("resource://gre/modules/FileUtils.jsm");
+
+let tempScope = {};
+Components.utils.import("resource://gre/modules/FileUtils.jsm", tempScope);
+let FileUtils = tempScope.FileUtils;
 
 
 function test()
 {
   waitForExplicitFinish();
 
   addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
     aChrome.addChromeListener({
deleted file mode 100644
--- a/browser/devtools/styleeditor/test/browser_styleeditor_sv_filter.js
+++ /dev/null
@@ -1,101 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-const TESTCASE_URI = TEST_BASE + "simple.html";
-
-
-function test()
-{
-  waitForExplicitFinish();
-
-  addTabAndLaunchStyleEditorChromeWhenLoaded(function (aChrome) {
-    aChrome.addChromeListener({
-      onContentAttach: run
-    });
-    if (aChrome.isContentAttached) {
-      run(aChrome);
-    }
-  });
-
-  content.location = TESTCASE_URI;
-}
-
-function getFilteredItemsCount(nav)
-{
-  let matches = nav.querySelectorAll("*.splitview-filtered");
-  return matches ? matches.length : 0;
-}
-
-function run(aChrome)
-{
-  aChrome.editors[0].addActionListener({onAttach: onFirstEditorAttach});
-  aChrome.editors[1].addActionListener({onAttach: onSecondEditorAttach});
-}
-
-function onFirstEditorAttach(aEditor)
-{
-  let filter = gChromeWindow.document.querySelector(".splitview-filter");
-  // force the command event on input since it is not possible to disable
-  // the search textbox's timeout.
-  let forceCommandEvent = function forceCommandEvent() {
-    let evt = gChromeWindow.document.createEvent("XULCommandEvent");
-    evt.initCommandEvent("command", true, true, gChromeWindow, 0, false, false,
-                         false, false, null);
-    filter.dispatchEvent(evt);
-  }
-  filter.addEventListener("input", forceCommandEvent, false);
-
-  let nav = gChromeWindow.document.querySelector(".splitview-nav");
-  nav.focus();
-
-  is(getFilteredItemsCount(nav), 0,
-     "there is 0 filtered item initially");
-
-  waitForFocus(function () {
-    // Search [s] (type-on-search since we focused nav above - not filter directly)
-    EventUtils.synthesizeKey("s", {}, gChromeWindow);
-
-    // the search space is "simple.css" and "inline stylesheet #1" (2 sheets)
-    is(getFilteredItemsCount(nav), 0,
-       "there is 0 filtered item if searching for 's'");
-
-    EventUtils.synthesizeKey("i", {}, gChromeWindow); // Search [si]
-
-    is(getFilteredItemsCount(nav), 1, // inline stylesheet is filtered
-       "there is 1 filtered item if searching for 's'");
-
-    // use uppercase to check that filtering is case-insensitive
-    EventUtils.synthesizeKey("X", {}, gChromeWindow); // Search [siX]
-    is(getFilteredItemsCount(nav), 2,
-       "there is 2 filtered items if searching for 's'"); // no match
-
-    // clear the search
-    EventUtils.synthesizeKey("VK_ESCAPE", {}, gChromeWindow);
-
-    is(filter.value, "",
-       "filter is back to empty");
-    is(getFilteredItemsCount(nav), 0,
-       "there is 0 filtered item when filter is empty again");
-
-    for each (let c in "inline") {
-      EventUtils.synthesizeKey(c, {}, gChromeWindow);
-    }
-
-    is(getFilteredItemsCount(nav), 1, // simple.css is filtered
-       "there is 1 filtered item if searching for 'inline'");
-
-    // auto-select the only result (enter the editor)
-    EventUtils.synthesizeKey("VK_ENTER", {}, gChromeWindow);
-
-    filter.removeEventListener("input", forceCommandEvent, false);
-  }, gChromeWindow);
-}
-
-function onSecondEditorAttach(aEditor)
-{
-  ok(aEditor.sourceEditor.hasFocus(),
-     "second editor has been selected and focused automatically.");
-
-  finish();
-}
--- a/browser/devtools/styleinspector/CssRuleView.jsm
+++ b/browser/devtools/styleinspector/CssRuleView.jsm
@@ -1305,16 +1305,17 @@ InplaceEditor.prototype = {
       prevent = true;
       moveFocus(this.input.ownerDocument.defaultView, FOCUS_FORWARD);
     } else if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE) {
       // Cancel and blur ourselves.  |_onBlur| will call the user's
       // done handler for us.
       prevent = true;
       this.cancelled = true;
       this.input.blur();
+      aEvent.stopPropagation();
     } else if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_SPACE) {
       // No need for leading spaces here.  This is particularly
       // noticable when adding a property: it's very natural to type
       // <name>: (which advances to the next property) then spacebar.
       prevent = !this.input.value;
     }
 
     if (prevent) {
--- a/browser/devtools/styleinspector/test/browser_bug589375_keybindings.js
+++ b/browser/devtools/styleinspector/test/browser_bug589375_keybindings.js
@@ -9,17 +9,16 @@ let stylePanel;
 
 function createDocument()
 {
   doc.body.innerHTML = '<style type="text/css"> ' +
     '.matches {color: #F00;}</style>' +
     '<span class="matches">Some styled text</span>' +
     '</div>';
   doc.title = "Style Inspector key binding test";
-  ok(window.StyleInspector, "StyleInspector exists");
   stylePanel = new StyleInspector(window);
   Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false);
   stylePanel.createPanel(false, function() {
     stylePanel.open(doc.body);
   });
 }
 
 function runStyleInspectorTests()
--- a/browser/devtools/styleinspector/test/browser_bug683672.js
+++ b/browser/devtools/styleinspector/test/browser_bug683672.js
@@ -4,30 +4,32 @@
 
 // Tests that the style inspector works properly
 
 let doc;
 let stylePanel;
 
 const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/test/browser_bug683672.html";
 
-Cu.import("resource:///modules/devtools/CssHtmlTree.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/devtools/CssHtmlTree.jsm", tempScope);
+let CssHtmlTree = tempScope.CssHtmlTree;
+let PropertyView = tempScope.PropertyView;
 
 function test()
 {
   waitForExplicitFinish();
   addTab(TEST_URI);
   browser.addEventListener("load", tabLoaded, true);
 }
 
 function tabLoaded()
 {
   browser.removeEventListener("load", tabLoaded, true);
   doc = content.document;
-  ok(window.StyleInspector, "StyleInspector exists");
   // ok(StyleInspector.isEnabled, "style inspector preference is enabled");
   stylePanel = new StyleInspector(window);
   Services.obs.addObserver(runTests, "StyleInspector-opened", false);
   stylePanel.createPanel(false, function() {
     stylePanel.open(doc.body);
   });
 }
 
--- a/browser/devtools/styleinspector/test/browser_bug_692400_element_style.js
+++ b/browser/devtools/styleinspector/test/browser_bug_692400_element_style.js
@@ -7,17 +7,16 @@
 let doc;
 let stylePanel;
 
 function createDocument()
 {
   doc.body.innerHTML = "<div style='color:blue;'></div>";
 
   doc.title = "Style Inspector Selector Text Test";
-  ok(window.StyleInspector, "StyleInspector exists");
   stylePanel = new StyleInspector(window);
 
 
   stylePanel.createPanel(false, function() {
     Services.obs.addObserver(SI_checkText, "StyleInspector-populated", false);
 
     let span = doc.querySelector("div");
     ok(span, "captain, we have the test div");
--- a/browser/devtools/styleinspector/test/browser_csslogic_inherited.js
+++ b/browser/devtools/styleinspector/test/browser_csslogic_inherited.js
@@ -1,15 +1,17 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test that inherited properties are treated correctly.
 
-Cu.import("resource:///modules/devtools/CssLogic.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/devtools/CssLogic.jsm", tempScope);
+let CssLogic = tempScope.CssLogic;
 
 let doc;
 
 function createDocument()
 {
   doc.body.innerHTML = '<div style="margin-left:10px; font-size: 5px"><div id="innerdiv">Inner div</div></div>';
   doc.title = "Style Inspector Inheritance Test";
 
--- a/browser/devtools/styleinspector/test/browser_ruleview_editor.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_editor.js
@@ -1,13 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-Cu.import("resource:///modules/devtools/CssRuleView.jsm");
+let tempScope = {}
+Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope);
+let CssRuleView = tempScope.CssRuleView;
+let _ElementStyle = tempScope._ElementStyle;
+let _editableField = tempScope._editableField;
 
 let doc = content.document;
 
 function expectDone(aValue, aCommit, aNext)
 {
   return function(aDoneValue, aDoneCommit) {
     dump("aDoneValue: " + aDoneValue + " commit: " + aDoneCommit + "\n");
 
@@ -113,9 +117,9 @@ function test()
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function(evt) {
     gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
     doc = content.document;
     waitForFocus(testReturnCommit, content);
   }, true);
 
   content.location = "data:text/html,inline editor tests";
-}
\ No newline at end of file
+}
--- a/browser/devtools/styleinspector/test/browser_ruleview_inherit.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_inherit.js
@@ -1,13 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-Cu.import("resource:///modules/devtools/CssRuleView.jsm");
+let tempScope = {}
+Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope);
+let CssRuleView = tempScope.CssRuleView;
+let _ElementStyle = tempScope._ElementStyle;
+let _editableField = tempScope._editableField;
 
 let doc;
 
 function simpleInherit()
 {
   let style = '' +
     '#test2 {' +
     '  background-color: green;' +
--- a/browser/devtools/styleinspector/test/browser_ruleview_manipulation.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_manipulation.js
@@ -1,13 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-Cu.import("resource:///modules/devtools/CssRuleView.jsm");
+let tempScope = {}
+Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope);
+let CssRuleView = tempScope.CssRuleView;
+let _ElementStyle = tempScope._ElementStyle;
+let _editableField = tempScope._editableField;
 
 let doc;
 
 function simpleOverride()
 {
   doc.body.innerHTML = '<div id="testid">Styled Node</div>';
   let element = doc.getElementById("testid");
   let elementStyle = new _ElementStyle(element);
--- a/browser/devtools/styleinspector/test/browser_ruleview_override.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_override.js
@@ -1,13 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-Cu.import("resource:///modules/devtools/CssRuleView.jsm");
+let tempScope = {}
+Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope);
+let CssRuleView = tempScope.CssRuleView;
+let _ElementStyle = tempScope._ElementStyle;
+let _editableField = tempScope._editableField;
 
 let doc;
 
 function simpleOverride()
 {
   let style = '' +
     '#testid {' +
     '  background-color: blue;' +
--- a/browser/devtools/styleinspector/test/browser_ruleview_ui.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_ui.js
@@ -1,13 +1,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-Cu.import("resource:///modules/devtools/CssRuleView.jsm");
+let tempScope = {}
+Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope);
+let CssRuleView = tempScope.CssRuleView;
+let _ElementStyle = tempScope._ElementStyle;
+let _editableField = tempScope._editableField;
 
 let doc;
 let ruleDialog;
 let ruleView;
 
 function waitForEditorFocus(aParent, aCallback)
 {
   aParent.addEventListener("focus", function onFocus(evt) {
--- a/browser/devtools/styleinspector/test/browser_styleinspector.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector.js
@@ -21,17 +21,16 @@ function createDocument()
     'style list-items in the box at right. If you are reading this, ' +
     'you should go do something else instead. Maybe read a book. Or better ' +
     'yet, write some test-cases for another bit of code. ' +
     '<span style="font-style: italic">Maybe more inspector test-cases!</span></p>\n' +
     '<p id="closing">end transmission</p>\n' +
     '<p>Inspect using inspectstyle(document.querySelectorAll("span")[0])</p>' +
     '</div>';
   doc.title = "Style Inspector Test";
-  ok(window.StyleInspector, "StyleInspector exists");
   stylePanel = new StyleInspector(window);
   Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false);
   stylePanel.createPanel(false, function() {
     stylePanel.open(doc.body);
   });
 }
 
 function runStyleInspectorTests()
--- a/browser/devtools/styleinspector/test/browser_styleinspector_bug_672744_search_filter.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_bug_672744_search_filter.js
@@ -9,17 +9,16 @@ let stylePanel;
 
 function createDocument()
 {
   doc.body.innerHTML = '<style type="text/css"> ' +
     '.matches {color: #F00;}</style>' +
     '<span id="matches" class="matches">Some styled text</span>' +
     '</div>';
   doc.title = "Style Inspector Search Filter Test";
-  ok(window.StyleInspector, "StyleInspector exists");
   // ok(StyleInspector.isEnabled, "style inspector preference is enabled");
   stylePanel = new StyleInspector(window);
   Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false);
   stylePanel.createPanel(false, function() {
     stylePanel.open(doc.body);
   });
 }
 
--- a/browser/devtools/styleinspector/test/browser_styleinspector_bug_672746_default_styles.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_bug_672746_default_styles.js
@@ -9,17 +9,16 @@ let stylePanel;
 
 function createDocument()
 {
   doc.body.innerHTML = '<style type="text/css"> ' +
     '.matches {color: #F00;}</style>' +
     '<span id="matches" class="matches">Some styled text</span>' +
     '</div>';
   doc.title = "Style Inspector Default Styles Test";
-  ok(window.StyleInspector, "StyleInspector exists");
   // ok(StyleInspector.isEnabled, "style inspector preference is enabled");
   stylePanel = new StyleInspector(window);
   Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false);
   stylePanel.createPanel(false, function() {
     stylePanel.open(doc.body);
   });
 }
 
--- a/browser/devtools/styleinspector/test/browser_styleinspector_bug_689759_no_results_placeholder.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_bug_689759_no_results_placeholder.js
@@ -8,17 +8,16 @@ let doc;
 let stylePanel;
 
 function createDocument()
 {
   doc.body.innerHTML = '<style type="text/css"> ' +
     '.matches {color: #F00;}</style>' +
     '<span id="matches" class="matches">Some styled text</span>';
   doc.title = "Tests that the no results placeholder works properly";
-  ok(window.StyleInspector, "StyleInspector exists");
   stylePanel = new StyleInspector(window);
   Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false);
   stylePanel.createPanel(false, function() {
     stylePanel.open(doc.body);
   });
 }
 
 function runStyleInspectorTests()
--- a/browser/devtools/styleinspector/test/head.js
+++ b/browser/devtools/styleinspector/test/head.js
@@ -30,18 +30,22 @@
  * 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 GPL or the LGPL. 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 ***** */
 
-Cu.import("resource:///modules/devtools/StyleInspector.jsm");
-Cu.import("resource://gre/modules/HUDService.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/devtools/StyleInspector.jsm", tempScope);
+Cu.import("resource://gre/modules/HUDService.jsm", tempScope);
+let StyleInspector = tempScope.StyleInspector;
+let HUDService = tempScope.HUDService;
+let ConsoleUtils = tempScope.ConsoleUtils;
 
 function log(aMsg)
 {
   dump("*** WebConsoleTest: " + aMsg + "\n");
 }
 
 function pprint(aObj)
 {
--- a/browser/devtools/tilt/Tilt.jsm
+++ b/browser/devtools/tilt/Tilt.jsm
@@ -151,20 +151,25 @@ Tilt.prototype = {
 
       if (!aAnimateFlag) {
         finalize.call(this, aId);
         return;
       }
 
       let controller = this.visualizers[aId].controller;
       let presenter = this.visualizers[aId].presenter;
+
+      TiltUtils.setDocumentZoom(presenter.transforms.zoom);
+
       let content = presenter.contentWindow;
+      let pageXOffset = content.pageXOffset * TiltUtils.getDocumentZoom();
+      let pageYOffset = content.pageYOffset * TiltUtils.getDocumentZoom();
 
       controller.removeEventListeners();
-      controller.arcball.reset([-content.pageXOffset, -content.pageYOffset]);
+      controller.arcball.reset([-pageXOffset, -pageYOffset]);
       presenter.executeDestruction(finalize.bind(this, aId));
     }
   },
 
   /**
    * Handles any supplementary post-initialization work, done immediately
    * after a TILT_NOTIFICATIONS.INITIALIZED notification.
    */
--- a/browser/devtools/tilt/TiltUtils.jsm
+++ b/browser/devtools/tilt/TiltUtils.jsm
@@ -668,16 +668,26 @@ TiltUtils.getWindowId = function TU_getW
  * @return {Number} the zoom ammount
  */
 TiltUtils.getDocumentZoom = function TU_getDocumentZoom() {
   return TiltUtils.getBrowserWindow()
                   .gBrowser.selectedBrowser.markupDocumentViewer.fullZoom;
 };
 
 /**
+ * Sets the markup document viewer zoom for the currently selected browser.
+ *
+ * @param {Number} the zoom ammount
+ */
+TiltUtils.setDocumentZoom = function TU_getDocumentZoom(aZoom) {
+  TiltUtils.getBrowserWindow()
+           .gBrowser.selectedBrowser.markupDocumentViewer.fullZoom = aZoom;
+};
+
+/**
  * Performs a garbage collection.
  */
 TiltUtils.gc = function TU_gc()
 {
   TiltUtils.getBrowserWindow()
            .QueryInterface(Ci.nsIInterfaceRequestor)
            .getInterface(Ci.nsIDOMWindowUtils)
            .garbageCollect();
--- a/browser/devtools/tilt/TiltVisualizer.jsm
+++ b/browser/devtools/tilt/TiltVisualizer.jsm
@@ -55,21 +55,22 @@ const INVISIBLE_ELEMENTS = {
   "option": true,
   "script": true,
   "style": true,
   "title": true
 };
 
 const STACK_THICKNESS = 15;
 const WIREFRAME_COLOR = [0, 0, 0, 0.25];
-const INTRO_TRANSITION_DURATION = 80;
-const OUTRO_TRANSITION_DURATION = 50;
+const INTRO_TRANSITION_DURATION = 50;
+const OUTRO_TRANSITION_DURATION = 40;
 const INITIAL_Z_TRANSLATION = 400;
 
 const MOUSE_CLICK_THRESHOLD = 10;
+const MOUSE_INTRO_DELAY = 10;
 const ARCBALL_SENSITIVITY = 0.5;
 const ARCBALL_ROTATION_STEP = 0.15;
 const ARCBALL_TRANSLATION_STEP = 35;
 const ARCBALL_ZOOM_STEP = 0.1;
 const ARCBALL_ZOOM_MIN = -3000;
 const ARCBALL_ZOOM_MAX = 500;
 const ARCBALL_RESET_FACTOR = 0.9;
 const ARCBALL_RESET_INTERVAL = 1000 / 60;
@@ -291,17 +292,17 @@ TiltVisualizer.Presenter = function TV_P
     // only redraw if we really have to
     if (this.redraw) {
       this.redraw = false;
       this.drawVisualization();
     }
 
     // call the attached ondraw event handler if specified (by the controller)
     if ("function" === typeof this.ondraw) {
-      this.ondraw();
+      this.ondraw(this.frames);
     }
 
     if (!TiltVisualizer.Prefs.introTransition && !this.isExecutingDestruction) {
       this.frames = INTRO_TRANSITION_DURATION;
     }
     if (!TiltVisualizer.Prefs.outroTransition && this.isExecutingDestruction) {
       this.frames = OUTRO_TRANSITION_DURATION;
     }
@@ -475,16 +476,21 @@ TiltVisualizer.Presenter.prototype = {
     // this will be removed once the MOZ_window_region_texture bug #653656
     // is finished; currently just converting the document image to a texture
     // applied to the mesh
     this.texture = new renderer.Texture({
       source: TiltGL.TextureUtils.createContentImage(this.contentWindow,
                                                      this.maxTextureSize),
       format: "RGB"
     });
+
+    if ("function" === typeof this.onSetupTexture) {
+      this.onSetupTexture();
+      this.onSetupTexture = null;
+    }
   },
 
   /**
    * Create the combined mesh representing the document visualization by
    * traversing the document & adding a stack for each node that is drawable.
    *
    * @param {Object} aData
    *                 object containing the necessary mesh verts, texcoord etc.
@@ -497,16 +503,19 @@ TiltVisualizer.Presenter.prototype = {
     TiltUtils.destroyObject(this.meshStacks);
     TiltUtils.destroyObject(this.meshWireframe);
 
     // if the renderer was destroyed, don't continue setup
     if (!renderer || !renderer.context) {
       return;
     }
 
+    // save the mesh data for future use
+    this.meshData = aData;
+
     // create the visualization mesh using the vertices, texture coordinates
     // and indices computed when traversing the document object model
     this.meshStacks = {
       vertices: new renderer.VertexBuffer(aData.vertices, 3),
       texCoord: new renderer.VertexBuffer(aData.texCoord, 2),
       color: new renderer.VertexBuffer(aData.color, 3),
       indices: new renderer.IndexBuffer(aData.stacksIndices)
     };
@@ -519,29 +528,38 @@ TiltVisualizer.Presenter.prototype = {
     };
 
     // if there's no initial selection made, highlight the required node
     if (!this._initialSelection) {
       this._initialSelection = true;
       this.highlightNode(this.inspectorUI.selection);
     }
 
-    let zoom = TiltUtils.getDocumentZoom();
-    let width = Math.min(aData.meshWidth * zoom, renderer.width);
-    let height = Math.min(aData.meshHeight * zoom, renderer.height);
+    if (!this._initialMeshConfiguration) {
+      this._initialMeshConfiguration = true;
+
+      let zoom = TiltUtils.getDocumentZoom();
+      let width = Math.min(aData.meshWidth * zoom, renderer.width);
+      let height = Math.min(aData.meshHeight * zoom, renderer.height);
+
+      // set the necessary mesh offsets
+      this.transforms.offset[0] = -width * 0.5;
+      this.transforms.offset[1] = -height * 0.5;
 
-    // set the necessary mesh offsets
-    this.transforms.offset[0] = -width * 0.5;
-    this.transforms.offset[1] = -height * 0.5;
+      // make sure the canvas is opaque now that the initialization is finished
+      this.canvas.style.background = TiltVisualizerStyle.canvas.background;
 
-    // make sure the canvas is opaque now that the initialization is finished
-    this.canvas.style.background = TiltVisualizerStyle.canvas.background;
+      this.drawVisualization();
+      this.redraw = true;
+    }
 
-    this.drawVisualization();
-    this.redraw = true;
+    if ("function" === typeof this.onSetupMesh) {
+      this.onSetupMesh();
+      this.onSetupMesh = null;
+    }
   },
 
   /**
    * Computes the mesh vertices, texture coordinates etc.
    */
   setupMeshData: function TVP_setupMeshData()
   {
     let renderer = this.renderer;
@@ -621,39 +639,54 @@ TiltVisualizer.Presenter.prototype = {
   /**
    * Picks a stacked dom node at the x and y screen coordinates and highlights
    * the selected node in the mesh.
    *
    * @param {Number} x
    *                 the current horizontal coordinate of the mouse
    * @param {Number} y
    *                 the current vertical coordinate of the mouse
+   * @param {Object} aProperties
+   *                 an object containing the following properties:
+   *      {Function} onpick: function to be called after picking succeeded
+   *      {Function} onfail: function to be called after picking failed
    */
-  highlightNodeAt: function TVP_highlightNodeAt(x, y)
+  highlightNodeAt: function TVP_highlightNodeAt(x, y, aProperties)
   {
+    // make sure the properties parameter is a valid object
+    aProperties = aProperties || {};
+
     // try to pick a mesh node using the current x, y coordinates
     this.pickNode(x, y, {
 
       /**
        * Mesh picking failed (nothing was found for the picked point).
        */
       onfail: function TVP_onHighlightFail()
       {
         this.highlightNodeFor(-1);
+
+        if ("function" === typeof aProperties.onfail) {
+          aProperties.onfail();
+        }
       }.bind(this),
 
       /**
        * Mesh picking succeeded.
        *
        * @param {Object} aIntersection
        *                 object containing the intersection details
        */
       onpick: function TVP_onHighlightPick(aIntersection)
       {
         this.highlightNodeFor(aIntersection.index);
+
+        if ("function" === typeof aProperties.onpick) {
+          aProperties.onpick();
+        }
       }.bind(this)
     });
   },
 
   /**
    * Sets the corresponding highlight coordinates and color based on the
    * information supplied.
    *
@@ -697,16 +730,42 @@ TiltVisualizer.Presenter.prototype = {
     vec3.set([x,     y + h, z * STACK_THICKNESS], highlight.v3);
 
     this._currentSelection = aNodeIndex;
     this.inspectorUI.inspectNode(node, this.contentWindow.innerHeight < y ||
                                        this.contentWindow.pageYOffset > 0);
   },
 
   /**
+   * Deletes a node from the visualization mesh.
+   *
+   * @param {Number} aNodeIndex
+   *                 the index of the node in the this.traverseData array;
+   *                 if not specified, it will default to the current selection
+   */
+  deleteNode: function TVP_deleteNode(aNodeIndex)
+  {
+    // we probably don't want to delete the html or body node.. just sayin'
+    if ((aNodeIndex = aNodeIndex || this._currentSelection) < 1) {
+      return;
+    }
+
+    let renderer = this.renderer;
+    let meshData = this.meshData;
+
+    for (let i = 0, k = 36 * aNodeIndex; i < 36; i++) {
+      meshData.vertices[i + k] = 0;
+    }
+
+    this.meshStacks.vertices = new renderer.VertexBuffer(meshData.vertices, 3);
+    this.highlight.disabled = true;
+    this.redraw = true;
+  },
+
+  /**
    * Picks a stacked dom node at the x and y screen coordinates and issues
    * a callback function with the found intersection.
    *
    * @param {Number} x
    *                 the current horizontal coordinate of the mouse
    * @param {Number} y
    *                 the current vertical coordinate of the mouse
    * @param {Object} aProperties
@@ -882,16 +941,19 @@ TiltVisualizer.Controller = function TV_
   /**
    * The initial controller dimensions and offset, in pixels.
    */
   this.left = aPresenter.contentWindow.pageXOffset || 0;
   this.top = aPresenter.contentWindow.pageYOffset || 0;
   this.width = aCanvas.width;
   this.height = aCanvas.height;
 
+  this.left *= TiltUtils.getDocumentZoom();
+  this.top *= TiltUtils.getDocumentZoom();
+
   /**
    * Arcball used to control the visualization using the mouse.
    */
   this.arcball = new TiltVisualizer.Arcball(this.width, this.height, 0,
     [this.width + this.left < aPresenter.maxTextureSize ? -this.left : 0,
      this.height + this.top < aPresenter.maxTextureSize ? -this.top : 0]);
 
   /**
@@ -918,17 +980,16 @@ TiltVisualizer.Controller.prototype = {
   addEventListeners: function TVC_addEventListeners()
   {
     let canvas = this.canvas;
     let presenter = this.presenter;
 
     // bind commonly used mouse and keyboard events with the controller
     canvas.addEventListener("mousedown", this.onMouseDown, false);
     canvas.addEventListener("mouseup", this.onMouseUp, false);
-    canvas.addEventListener("click", this.onMouseClick, false);
     canvas.addEventListener("mousemove", this.onMouseMove, false);
     canvas.addEventListener("mouseover", this.onMouseOver, false);
     canvas.addEventListener("mouseout", this.onMouseOut, false);
     canvas.addEventListener("MozMousePixelScroll", this.onMozScroll, false);
     canvas.addEventListener("keydown", this.onKeyDown, false);
     canvas.addEventListener("keyup", this.onKeyUp, false);
     canvas.addEventListener("blur", this.onBlur, false);
 
@@ -941,101 +1002,103 @@ TiltVisualizer.Controller.prototype = {
    */
   removeEventListeners: function TVC_removeEventListeners()
   {
     let canvas = this.canvas;
     let presenter = this.presenter;
 
     canvas.removeEventListener("mousedown", this.onMouseDown, false);
     canvas.removeEventListener("mouseup", this.onMouseUp, false);
-    canvas.removeEventListener("click", this.onMouseClick, false);
     canvas.removeEventListener("mousemove", this.onMouseMove, false);
     canvas.removeEventListener("mouseover", this.onMouseOver, false);
     canvas.removeEventListener("mouseout", this.onMouseOut, false);
     canvas.removeEventListener("MozMousePixelScroll", this.onMozScroll, false);
     canvas.removeEventListener("keydown", this.onKeyDown, false);
     canvas.removeEventListener("keyup", this.onKeyUp, false);
     canvas.removeEventListener("blur", this.onBlur, false);
 
     presenter.contentWindow.removeEventListener("resize", this.onResize,false);
   },
 
   /**
    * Function called each frame, updating the visualization camera transforms.
+   *
+   * @param {Number} aFrames
+   *                 the current animation frame count
    */
-  update: function TVC_update()
+  update: function TVC_update(aFrames)
   {
+    this.frames = aFrames;
     this.coordinates = this.arcball.update();
 
     this.presenter.setRotation(this.coordinates.rotation);
     this.presenter.setTranslation(this.coordinates.translation);
   },
 
   /**
    * Called once after every time a mouse button is pressed.
    */
   onMouseDown: function TVC_onMouseDown(e)
   {
     e.target.focus();
     e.preventDefault();
     e.stopPropagation();
 
+    if (this.frames < MOUSE_INTRO_DELAY) {
+      return;
+    }
+
     // calculate x and y coordinates using using the client and target offset
+    let button = e.which;
     this._downX = e.clientX - e.target.offsetLeft;
     this._downY = e.clientY - e.target.offsetTop;
 
-    this.arcball.mouseDown(this._downX, this._downY, e.which);
+    this.arcball.mouseDown(this._downX, this._downY, button);
   },
 
   /**
    * Called every time a mouse button is released.
    */
   onMouseUp: function TVC_onMouseUp(e)
   {
     e.preventDefault();
     e.stopPropagation();
 
+    if (this.frames < MOUSE_INTRO_DELAY) {
+      return;
+    }
+
     // calculate x and y coordinates using using the client and target offset
     let button = e.which;
     let upX = e.clientX - e.target.offsetLeft;
     let upY = e.clientY - e.target.offsetTop;
 
-    this.arcball.mouseUp(upX, upY, button);
-  },
-
-  /**
-   * Called every time a mouse button is clicked.
-   */
-  onMouseClick: function TVC_onMouseClick(e)
-  {
-    e.preventDefault();
-    e.stopPropagation();
-
-    // calculate x and y coordinates using using the client and target offset
-    let button = e.which;
-    let clickX = e.clientX - e.target.offsetLeft;
-    let clickY = e.clientY - e.target.offsetTop;
-
     // a click in Tilt is issued only when the mouse pointer stays in
     // relatively the same position
-    if (Math.abs(this._downX - clickX) < MOUSE_CLICK_THRESHOLD &&
-        Math.abs(this._downY - clickY) < MOUSE_CLICK_THRESHOLD) {
+    if (Math.abs(this._downX - upX) < MOUSE_CLICK_THRESHOLD &&
+        Math.abs(this._downY - upY) < MOUSE_CLICK_THRESHOLD) {
 
-      this.presenter.highlightNodeAt(clickX, clickY);
+      this.presenter.highlightNodeAt(upX, upY);
     }
+
+    this.arcball.mouseUp(upX, upY, button);
   },
 
   /**
    * Called every time the mouse moves.
    */
   onMouseMove: function TVC_onMouseMove(e)
   {
     e.preventDefault();
     e.stopPropagation();
 
+    if (this.frames < MOUSE_INTRO_DELAY) {
+      return;
+    }
+
     // calculate x and y coordinates using using the client and target offset
     let moveX = e.clientX - e.target.offsetLeft;
     let moveY = e.clientY - e.target.offsetTop;
 
     this.arcball.mouseMove(moveX, moveY);
   },
 
   /**
@@ -1093,16 +1156,19 @@ TiltVisualizer.Controller.prototype = {
   onKeyUp: function TVC_onKeyUp(e)
   {
     let code = e.keyCode || e.which;
 
     if (code === e.DOM_VK_ESCAPE) {
       this.presenter.tiltUI.destroy(this.presenter.tiltUI.currentWindowId, 1);
       return;
     }
+    if (code === e.DOM_VK_X) {
+      this.presenter.deleteNode();
+    }
 
     if (!e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey) {
       e.preventDefault();
       e.stopPropagation();
       this.arcball.keyUp(code);
     }
   },
 
@@ -1190,31 +1256,31 @@ TiltVisualizer.Arcball = function TV_Arc
   this._endVec = vec3.create();
   this._pVec = vec3.create();
 
   /**
    * The corresponding rotation quaternions.
    */
   this._lastRot = quat4.create();
   this._deltaRot = quat4.create();
-  this._currentRot = quat4.create();
+  this._currentRot = quat4.create(aInitialRot);
 
   /**
    * The current camera translation coordinates.
    */
   this._lastTrans = vec3.create();
   this._deltaTrans = vec3.create();
-  this._currentTrans = vec3.create();
+  this._currentTrans = vec3.create(aInitialTrans);
   this._zoomAmount = 0;
 
   /**
    * Additional rotation and translation vectors.
    */
-  this._additionalRot = vec3.create(aInitialRot);
-  this._additionalTrans = vec3.create(aInitialTrans);
+  this._additionalRot = vec3.create();
+  this._additionalTrans = vec3.create();
   this._deltaAdditionalRot = quat4.create();
   this._deltaAdditionalTrans = vec3.create();
 
   // load the keys controlling the arcball
   this._loadKeys();
 
   // set the current dimensions of the arcball
   this.resize(aWidth, aHeight, aRadius);
@@ -1612,31 +1678,32 @@ TiltVisualizer.Arcball.prototype = {
    *
    * @param {Array} aFinalTranslation
    *                optional, final vector translation
    * @param {Array} aFinalRotation
    *                optional, final quaternion rotation
    */
   reset: function TVA_reset(aFinalTranslation, aFinalRotation)
   {
+    if ("function" === typeof this.onResetStart) {
+      this.onResetStart();
+      this.onResetStart = null;
+    }
+
     this.cancelMouseEvents();
     this.cancelKeyEvents();
+    this._cancelResetInterval();
 
-    if (!this._resetInterval) {
-      let window = TiltUtils.getBrowserWindow();
-      let func = this._nextResetIntervalStep.bind(this);
+    let window = TiltUtils.getBrowserWindow();
+    let func = this._nextResetIntervalStep.bind(this);
 
-      vec3.zero(this._additionalTrans);
-      vec3.zero(this._additionalRot);
-
-      this._save();
-      this._resetFinalTranslation = vec3.create(aFinalTranslation);
-      this._resetFinalRotation = quat4.create(aFinalRotation);
-      this._resetInterval = window.setInterval(func, ARCBALL_RESET_INTERVAL);
-    }
+    this._save();
+    this._resetFinalTranslation = vec3.create(aFinalTranslation);
+    this._resetFinalRotation = quat4.create(aFinalRotation);
+    this._resetInterval = window.setInterval(func, ARCBALL_RESET_INTERVAL);
   },
 
   /**
    * Cancels the current arcball reset animation if there is one.
    */
   _cancelResetInterval: function TVA__cancelResetInterval()
   {
     if (this._resetInterval) {
@@ -1661,29 +1728,33 @@ TiltVisualizer.Arcball.prototype = {
     let fDelta = EPSILON * EPSILON;
     let fTran = this._resetFinalTranslation;
     let fRot = this._resetFinalRotation;
 
     let t = vec3.create(fTran);
     let r = quat4.multiply(quat4.inverse(quat4.create(this._currentRot)), fRot);
 
     // reset the rotation quaternion and translation vector
-    vec3.lerp(this._currentTrans, t, ARCBALL_RESET_FACTOR);
+    vec3.lerp(this._currentTrans, t, ARCBALL_RESET_FACTOR / 4);
     quat4.slerp(this._currentRot, r, 1 - ARCBALL_RESET_FACTOR);
 
     // also reset any additional transforms by the keyboard or mouse
+    vec3.scale(this._additionalTrans, ARCBALL_RESET_FACTOR);
+    vec3.scale(this._additionalRot, ARCBALL_RESET_FACTOR);
     this._zoomAmount *= ARCBALL_RESET_FACTOR;
 
     // clear the loop if the all values are very close to zero
     if (vec3.length(vec3.subtract(this._lastRot, fRot, [])) < fDelta &&
         vec3.length(vec3.subtract(this._deltaRot, fRot, [])) < fDelta &&
         vec3.length(vec3.subtract(this._currentRot, fRot, [])) < fDelta &&
         vec3.length(vec3.subtract(this._lastTrans, fTran, [])) < fDelta &&
         vec3.length(vec3.subtract(this._deltaTrans, fTran, [])) < fDelta &&
-        vec3.length(vec3.subtract(this._currentTrans, fTran, [])) < fDelta) {
+        vec3.length(vec3.subtract(this._currentTrans, fTran, [])) < fDelta &&
+        vec3.length(this._additionalRot) < fDelta &&
+        vec3.length(this._additionalTrans) < fDelta) {
 
       this._cancelResetInterval();
     }
   },
 
   /**
    * Loads the keys to control this arcball.
    */
--- a/browser/devtools/tilt/TiltWorkerPicker.js
+++ b/browser/devtools/tilt/TiltWorkerPicker.js
@@ -76,16 +76,21 @@ self.onmessage = function TWP_onMessage(
     let v3f = [vertices[i + 9], vertices[i + 10], vertices[i + 11]];
 
     // the back quad
     let v0b = [v0f[0], v0f[1], v0f[2] - thickness];
     let v1b = [v1f[0], v1f[1], v1f[2] - thickness];
     let v2b = [v2f[0], v2f[1], v2f[2] - thickness];
     let v3b = [v3f[0], v3f[1], v3f[2] - thickness];
 
+    // don't do anything with degenerate quads
+    if (!v0f[0] && !v1f[0] && !v2f[0] && !v3f[0]) {
+      continue;
+    }
+
     // for each triangle in the stack box, check for the intersections
     if (self.intersect(v0f, v1f, v2f, ray, hit) || // front left
         self.intersect(v0f, v2f, v3f, ray, hit) || // front right
         self.intersect(v0b, v1b, v1f, ray, hit) || // left back
         self.intersect(v0b, v1f, v0f, ray, hit) || // left front
         self.intersect(v3f, v2b, v3b, ray, hit) || // right back
         self.intersect(v3f, v2f, v2b, ray, hit) || // right front
         self.intersect(v0b, v0f, v3f, ray, hit) || // top left
--- a/browser/devtools/tilt/test/Makefile.in
+++ b/browser/devtools/tilt/test/Makefile.in
@@ -68,16 +68,21 @@ include $(topsrcdir)/config/rules.mk
 	browser_tilt_gl08.js \
 	browser_tilt_math01.js \
 	browser_tilt_math02.js \
 	browser_tilt_math03.js \
 	browser_tilt_math04.js \
 	browser_tilt_math05.js \
 	browser_tilt_math06.js \
 	browser_tilt_math07.js \
+	browser_tilt_picking.js \
+	browser_tilt_picking_delete.js \
+	browser_tilt_picking_highlight01.js \
+	browser_tilt_picking_highlight02.js \
+	browser_tilt_picking_highlight03.js \
 	browser_tilt_utils01.js \
 	browser_tilt_utils02.js \
 	browser_tilt_utils03.js \
 	browser_tilt_utils04.js \
 	browser_tilt_utils05.js \
 	browser_tilt_utils06.js \
 	browser_tilt_visualizer.js \
 	browser_tilt_zoom.js \
--- a/browser/devtools/tilt/test/browser_tilt_arcball-reset-typeahead.js
+++ b/browser/devtools/tilt/test/browser_tilt_arcball-reset-typeahead.js
@@ -11,16 +11,17 @@ function test() {
     info("Skipping part of the arcball test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
     info("Skipping part of the arcball test because WebGL isn't supported.");
     return;
   }
 
+  requestLongerTimeout(10);
   waitForExplicitFinish();
   Services.prefs.setBoolPref("accessibility.typeaheadfind", true);
 
   createTab(function() {
     createTilt({
       onTiltOpen: function(instance)
       {
         performTest(instance.presenter.canvas,
@@ -60,16 +61,20 @@ function performTest(canvas, arcball, ca
       EventUtils.synthesizeKey("VK_W", { type: "keyup" });
       EventUtils.synthesizeKey("VK_LEFT", { type: "keyup" });
 
       // ok, transformations finished, we can now try to reset the model view
 
       executeSoon(function() {
         info("Synthesizing arcball reset key press.");
 
+        arcball.onResetStart = function() {
+          info("Starting arcball reset animation.");
+        };
+
         arcball.onResetFinish = function() {
           ok(isApproxVec(arcball._lastRot, [0, 0, 0, 1]),
             "The arcball _lastRot field wasn't reset correctly.");
           ok(isApproxVec(arcball._deltaRot, [0, 0, 0, 1]),
             "The arcball _deltaRot field wasn't reset correctly.");
           ok(isApproxVec(arcball._currentRot, [0, 0, 0, 1]),
             "The arcball _currentRot field wasn't reset correctly.");
 
--- a/browser/devtools/tilt/test/browser_tilt_arcball-reset.js
+++ b/browser/devtools/tilt/test/browser_tilt_arcball-reset.js
@@ -11,16 +11,17 @@ function test() {
     info("Skipping part of the arcball test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
     info("Skipping part of the arcball test because WebGL isn't supported.");
     return;
   }
 
+  requestLongerTimeout(10);
   waitForExplicitFinish();
 
   createTab(function() {
     createTilt({
       onTiltOpen: function(instance)
       {
         performTest(instance.presenter.canvas,
                     instance.controller.arcball, function() {
@@ -58,16 +59,20 @@ function performTest(canvas, arcball, ca
       EventUtils.synthesizeKey("VK_W", { type: "keyup" });
       EventUtils.synthesizeKey("VK_LEFT", { type: "keyup" });
 
       // ok, transformations finished, we can now try to reset the model view
 
       executeSoon(function() {
         info("Synthesizing arcball reset key press.");
 
+        arcball.onResetStart = function() {
+          info("Starting arcball reset animation.");
+        };
+
         arcball.onResetFinish = function() {
           ok(isApproxVec(arcball._lastRot, [0, 0, 0, 1]),
             "The arcball _lastRot field wasn't reset correctly.");
           ok(isApproxVec(arcball._deltaRot, [0, 0, 0, 1]),
             "The arcball _deltaRot field wasn't reset correctly.");
           ok(isApproxVec(arcball._currentRot, [0, 0, 0, 1]),
             "The arcball _currentRot field wasn't reset correctly.");
 
new file mode 100644
--- /dev/null
+++ b/browser/devtools/tilt/test/browser_tilt_picking.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*global ok, is, info, waitForExplicitFinish, finish, gBrowser */
+/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
+/*global Services, InspectorUI, TILT_DESTROYED */
+"use strict";
+
+function test() {
+  if (!isTiltEnabled()) {
+    info("Skipping picking test because Tilt isn't enabled.");
+    return;
+  }
+  if (!isWebGLSupported()) {
+    info("Skipping picking test because WebGL isn't supported.");
+    return;
+  }
+
+  waitForExplicitFinish();
+
+  createTab(function() {
+    createTilt({
+      onTiltOpen: function(instance)
+      {
+        let presenter = instance.presenter;
+        let canvas = presenter.canvas;
+
+        presenter.onSetupMesh = function() {
+
+          presenter.pickNode(canvas.width / 2, canvas.height / 2, {
+            onpick: function(data)
+            {
+              ok(data.index > 0,
+                "Simply picking a node didn't work properly.");
+              ok(!presenter.highlight.disabled,
+                "After only picking a node, it shouldn't be highlighted.");
+
+              Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
+              InspectorUI.closeInspectorUI();
+            }
+          });
+        };
+      }
+    });
+  });
+}
+
+function cleanup() {
+  Services.obs.removeObserver(cleanup, TILT_DESTROYED);
+  gBrowser.removeCurrentTab();
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/tilt/test/browser_tilt_picking_delete.js
@@ -0,0 +1,67 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*global ok, is, info, waitForExplicitFinish, finish, gBrowser */
+/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
+/*global Services, InspectorUI, TILT_DESTROYED */
+"use strict";
+
+function test() {
+  if (!isTiltEnabled()) {
+    info("Skipping picking delete test because Tilt isn't enabled.");
+    return;
+  }
+  if (!isWebGLSupported()) {
+    info("Skipping picking delete test because WebGL isn't supported.");
+    return;
+  }
+
+  waitForExplicitFinish();
+
+  createTab(function() {
+    createTilt({
+      onTiltOpen: function(instance)
+      {
+        let presenter = instance.presenter;
+        let canvas = presenter.canvas;
+
+        presenter.onSetupMesh = function() {
+
+          presenter.highlightNodeAt(canvas.width / 2, canvas.height / 2, {
+            onpick: function()
+            {
+              ok(presenter._currentSelection > 0,
+                "Highlighting a node didn't work properly.");
+              ok(!presenter.highlight.disabled,
+                "After highlighting a node, it should be highlighted. D'oh.");
+
+              presenter.deleteNode();
+
+              ok(presenter._currentSelection > 0,
+                "Deleting a node shouldn't change the current selection.");
+              ok(presenter.highlight.disabled,
+                "After deleting a node, it shouldn't be highlighted.");
+
+              let nodeIndex = presenter._currentSelection;
+              let meshData = presenter.meshData;
+
+              for (let i = 0, k = 36 * nodeIndex; i < 36; i++) {
+                is(meshData.vertices[i + k], 0,
+                  "The stack vertices weren't degenerated properly.");
+              }
+
+              Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
+              InspectorUI.closeInspectorUI();
+            }
+          });
+        };
+      }
+    });
+  });
+}
+
+function cleanup() {
+  Services.obs.removeObserver(cleanup, TILT_DESTROYED);
+  gBrowser.removeCurrentTab();
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/tilt/test/browser_tilt_picking_highlight01.js
@@ -0,0 +1,50 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*global ok, is, info, waitForExplicitFinish, finish, gBrowser */
+/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
+/*global Services, InspectorUI, TILT_DESTROYED */
+"use strict";
+
+function test() {
+  if (!isTiltEnabled()) {
+    info("Skipping highlight test because Tilt isn't enabled.");
+    return;
+  }
+  if (!isWebGLSupported()) {
+    info("Skipping highlight test because WebGL isn't supported.");
+    return;
+  }
+
+  waitForExplicitFinish();
+
+  createTab(function() {
+    createTilt({
+      onTiltOpen: function(instance)
+      {
+        let presenter = instance.presenter;
+
+        presenter.onSetupMesh = function() {
+          let contentDocument = presenter.contentWindow.document;
+          let body = contentDocument.getElementsByTagName("body")[0];
+
+          presenter.highlightNode(body);
+
+          ok(presenter._currentSelection > 0,
+            "Highlighting a node didn't work properly.");
+          ok(!presenter.highlight.disabled,
+            "After highlighting a node, it should be highlighted. D'oh.");
+
+          Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
+          InspectorUI.closeInspectorUI();
+        };
+      }
+    });
+  });
+}
+
+function cleanup() {
+  Services.obs.removeObserver(cleanup, TILT_DESTROYED);
+  gBrowser.removeCurrentTab();
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/tilt/test/browser_tilt_picking_highlight02.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*global ok, is, info, waitForExplicitFinish, finish, gBrowser */
+/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
+/*global Services, InspectorUI, TILT_DESTROYED */
+"use strict";
+
+function test() {
+  if (!isTiltEnabled()) {
+    info("Skipping highlight test because Tilt isn't enabled.");
+    return;
+  }
+  if (!isWebGLSupported()) {
+    info("Skipping highlight test because WebGL isn't supported.");
+    return;
+  }
+
+  waitForExplicitFinish();
+
+  createTab(function() {
+    createTilt({
+      onTiltOpen: function(instance)
+      {
+        let presenter = instance.presenter;
+        let canvas = presenter.canvas;
+
+        presenter.onSetupMesh = function() {
+
+          presenter.highlightNodeAt(canvas.width / 2, canvas.height / 2, {
+            onpick: function()
+            {
+              ok(presenter._currentSelection > 0,
+                "Highlighting a node didn't work properly.");
+              ok(!presenter.highlight.disabled,
+                "After highlighting a node, it should be highlighted. D'oh.");
+
+              Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
+              InspectorUI.closeInspectorUI();
+            }
+          });
+        };
+      }
+    });
+  });
+}
+
+function cleanup() {
+  Services.obs.removeObserver(cleanup, TILT_DESTROYED);
+  gBrowser.removeCurrentTab();
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/tilt/test/browser_tilt_picking_highlight03.js
@@ -0,0 +1,47 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*global ok, is, info, waitForExplicitFinish, finish, gBrowser */
+/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
+/*global Services, InspectorUI, TILT_DESTROYED */
+"use strict";
+
+function test() {
+  if (!isTiltEnabled()) {
+    info("Skipping highlight test because Tilt isn't enabled.");
+    return;
+  }
+  if (!isWebGLSupported()) {
+    info("Skipping highlight test because WebGL isn't supported.");
+    return;
+  }
+
+  waitForExplicitFinish();
+
+  createTab(function() {
+    createTilt({
+      onTiltOpen: function(instance)
+      {
+        let presenter = instance.presenter;
+
+        presenter.onSetupMesh = function() {
+          presenter.highlightNodeFor(1);
+
+          ok(presenter._currentSelection > 0,
+            "Highlighting a node didn't work properly.");
+          ok(!presenter.highlight.disabled,
+            "After highlighting a node, it should be highlighted. D'oh.");
+
+          Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
+          InspectorUI.closeInspectorUI();
+        };
+      }
+    });
+  });
+}
+
+function cleanup() {
+  Services.obs.removeObserver(cleanup, TILT_DESTROYED);
+  gBrowser.removeCurrentTab();
+  finish();
+}
--- a/browser/devtools/tilt/test/browser_tilt_zoom.js
+++ b/browser/devtools/tilt/test/browser_tilt_zoom.js
@@ -13,20 +13,21 @@ function setZoom(value) {
   gBrowser.selectedBrowser.markupDocumentViewer.fullZoom = value;
 }
 
 function getZoom() {
   return gBrowser.selectedBrowser.markupDocumentViewer.fullZoom;
 }
 
 function test() {
-  setZoom(Math.random());
+  TiltUtils.setDocumentZoom(Math.random());
   is(getZoom(), TiltUtils.getDocumentZoom(),
     "The getDocumentZoom utility function didn't return the expected results.");
 
+
   if (!isTiltEnabled()) {
     info("Skipping controller test because Tilt isn't enabled.");
     return;
   }
   if (!isWebGLSupported()) {
     info("Skipping controller test because WebGL isn't supported.");
     return;
   }
--- a/browser/devtools/tilt/test/head.js
+++ b/browser/devtools/tilt/test/head.js
@@ -1,19 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*global Services, Components, gBrowser, executeSoon, info */
 /*global InspectorUI, Tilt, TiltGL, EPSILON */
 "use strict";
 
-Components.utils.import("resource:///modules/devtools/TiltGL.jsm");
-Components.utils.import("resource:///modules/devtools/TiltMath.jsm");
-Components.utils.import("resource:///modules/devtools/TiltUtils.jsm");
-Components.utils.import("resource:///modules/devtools/TiltVisualizer.jsm");
+let tempScope = {};
+Components.utils.import("resource:///modules/devtools/TiltGL.jsm", tempScope);
+Components.utils.import("resource:///modules/devtools/TiltMath.jsm", tempScope);
+Components.utils.import("resource:///modules/devtools/TiltUtils.jsm", tempScope);
+Components.utils.import("resource:///modules/devtools/TiltVisualizer.jsm", tempScope);
+let TiltGL = tempScope.TiltGL;
+let EPSILON = tempScope.EPSILON;
+let TiltMath = tempScope.TiltMath;
+let vec3 = tempScope.vec3;
+let mat3 = tempScope.mat3;
+let mat4 = tempScope.mat4;
+let quat4 = tempScope.quat4;
+let TiltUtils = tempScope.TiltUtils;
+let TiltVisualizer = tempScope.TiltVisualizer;
 
 
 const DEFAULT_HTML = "data:text/html," +
   "<DOCTYPE html>" +
   "<html>" +
     "<head>" +
       "<title>Three Laws</title>" +
     "</head>" +
--- a/browser/devtools/webconsole/HUDService.jsm
+++ b/browser/devtools/webconsole/HUDService.jsm
@@ -1547,18 +1547,16 @@ HUD_SERVICE.prototype =
 
     let hudId = "hud_" + nBox.id;
     let hudRef = this.hudReferences[hudId];
 
     if (!aAnimated || hudRef.consolePanel) {
       this.disableAnimation(hudId);
     }
 
-    chromeDocument.getElementById("Tools:WebConsole").setAttribute("checked", "true");
-
     // Create a processing instruction for GCLIs CSS stylesheet, but only if
     // we don't have one for this document. Also record the context we're
     // adding this for so we know when to remove it.
     let procInstr = aContext.ownerDocument.gcliCssProcInstr;
     if (!procInstr) {
       procInstr = aContext.ownerDocument.createProcessingInstruction(
               "xml-stylesheet",
               "href='chrome://browser/skin/devtools/gcli.css' type='text/css'");
@@ -1598,18 +1596,16 @@ HUD_SERVICE.prototype =
       browser.webProgress.removeProgressListener(hud.progressListener);
       delete hud.progressListener;
 
       this.unregisterDisplay(hudId);
 
       window.focus();
     }
 
-    chromeDocument.getElementById("Tools:WebConsole").setAttribute("checked", "false");
-
     // Remove this context from the list of contexts that need the GCLI CSS
     // processing instruction and then remove the processing instruction if it
     // isn't needed any more.
     let procInstr = aContext.ownerDocument.gcliCssProcInstr;
     if (procInstr) {
       procInstr.contexts = procInstr.contexts.filter(function(id) {
         return id !== hudId;
       });
--- a/browser/devtools/webconsole/test/browser_gcli_inspect.js
+++ b/browser/devtools/webconsole/test/browser_gcli_inspect.js
@@ -2,17 +2,19 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // For more information on GCLI see:
 // - https://github.com/mozilla/gcli/blob/master/docs/index.md
 // - https://wiki.mozilla.org/DevTools/Features/GCLI
 
 // Tests that the inspect command works as it should
 
-Components.utils.import("resource:///modules/gcli.jsm");
+let tempScope = {};
+Components.utils.import("resource:///modules/gcli.jsm", tempScope);
+let gcli = tempScope.gcli;
 
 registerCleanupFunction(function() {
   gcliterm = undefined;
   requisition = undefined;
 
   Services.prefs.clearUserPref("devtools.gcli.enable");
 });
 
--- a/browser/devtools/webconsole/test/browser_gcli_integrate.js
+++ b/browser/devtools/webconsole/test/browser_gcli_integrate.js
@@ -3,17 +3,19 @@
 
 // For more information on GCLI see:
 // - https://github.com/mozilla/gcli/blob/master/docs/index.md
 // - https://wiki.mozilla.org/DevTools/Features/GCLI
 
 // Tests that source URLs in the Web Console can be clicked to display the
 // standard View Source window.
 
-Components.utils.import("resource:///modules/gcli.jsm");
+let tempScope = {};
+Components.utils.import("resource:///modules/gcli.jsm", tempScope);
+let gcli = tempScope.gcli;
 let require = gcli._internal.require;
 
 const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
 
 registerCleanupFunction(function() {
   require = undefined;
   Services.prefs.clearUserPref("devtools.gcli.enable");
 });
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_580454_timestamp_l10n.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_580454_timestamp_l10n.js
@@ -5,18 +5,16 @@
  *
  * Contributor(s):
  *  Patrick Walton <pcwalton@mozilla.com>
  *
  * ***** END LICENSE BLOCK ***** */
 
 // Tests that appropriately-localized timestamps are printed.
 
-Cu.import("resource:///modules/HUDService.jsm");
-
 const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
 
 function test() {
   addTab(TEST_URI);
   browser.addEventListener("DOMContentLoaded", testTimestamp, false);
 
   function testTimestamp()
   {
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js
@@ -32,16 +32,17 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. 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 ***** */
 
 const TEST_URI = "data:text/html,<p>bug 585991 - autocomplete popup keyboard usage test";
+let HUD;
 
 registerCleanupFunction(function() {
   Services.prefs.clearUserPref("devtools.gcli.enable");
 });
 
 function test() {
   Services.prefs.setBoolPref("devtools.gcli.enable", false);
   addTab(TEST_URI);
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_586388_select_all.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_586388_select_all.js
@@ -67,13 +67,11 @@ function testSelectionWhenMovingBetweenB
   selectAllItem.dispatchEvent(commandEvent);
 
   is(outputNode.selectedCount, outputNode.childNodes.length, "all console " +
      "messages are selected after performing a select-all operation from " +
      "the context menu");
 
   outputNode.selectedIndex = -1;
 
-  commandEvent = contextMenu = groupNode = range = null;
-
   finishTest();
 }
 
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_594497_history_arrow_keys.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_594497_history_arrow_keys.js
@@ -12,17 +12,17 @@ let inputNode, values;
 
 function tabLoad(aEvent) {
   browser.removeEventListener(aEvent.type, arguments.callee, true);
 
   waitForFocus(function() {
     openConsole();
 
     let hudId = HUDService.getHudIdByWindow(content);
-    HUD = HUDService.hudReferences[hudId];
+    let HUD = HUDService.hudReferences[hudId];
 
     inputNode = HUD.jsterm.inputNode;
 
     inputNode.focus();
 
     ok(!inputNode.value, "inputNode.value is empty");
 
     values = ["document", "window", "document.body"];
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_598357_jsterm_output.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_598357_jsterm_output.js
@@ -86,16 +86,17 @@ let inputValues = [
   // 17
   [true, "({a:'b', c:'d', e:1, f:'2'})", '({a:"b", c:"d", e:1, f:"2"})',
     "[object Object",
     '({a:"b", c:"d", e:1, f:"2"})'],
 ];
 
 let eventHandlers = [];
 let popupShown = [];
+let HUD;
 
 function tabLoad(aEvent) {
   browser.removeEventListener(aEvent.type, arguments.callee, true);
 
   waitForFocus(function () {
     openConsole();
 
     let hudId = HUDService.getHudIdByWindow(content);
@@ -216,17 +217,16 @@ function testEnd() {
   }
 
   for (let i = 0; i < inputValues.length; i++) {
     if (inputValues[i][0] && !popupShown[i]) {
       ok(false, "the property panel failed to show for inputValues[" + i + "]");
     }
   }
 
-  eventHandlers = popupshown = null;
   executeSoon(finishTest);
 }
 
 registerCleanupFunction(function() {
   Services.prefs.clearUserPref("devtools.gcli.enable");
 });
 
 function test() {
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_642108_pruneTest.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_642108_pruneTest.js
@@ -16,31 +16,31 @@ const SEVERITY_WARNING = 1;
 function test() {
   addTab(TEST_URI);
   browser.addEventListener("DOMContentLoaded", testCSSPruning, false);
 }
 
 function populateConsoleRepeats(aHudRef) {
   let hud = aHudRef.HUDBox;
 
-  for (i = 0; i < 5; i++) {
+  for (let i = 0; i < 5; i++) {
     let node = ConsoleUtils.createMessageNode(hud.ownerDocument,
                                               CATEGORY_CSS,
                                               SEVERITY_WARNING,
                                               "css log x",
                                               aHudRef.hudId);
     ConsoleUtils.outputMessageNode(node, aHudRef.hudId);
   }
 }
 
 
 function populateConsole(aHudRef) {
   let hud = aHudRef.HUDBox;
 
-  for (i = 0; i < LOG_LIMIT + 5; i++) {
+  for (let i = 0; i < LOG_LIMIT + 5; i++) {
     let node = ConsoleUtils.createMessageNode(hud.ownerDocument,
                                               CATEGORY_CSS,
                                               SEVERITY_WARNING,
                                               "css log " + i,
                                               aHudRef.hudId);
     ConsoleUtils.outputMessageNode(node, aHudRef.hudId);
   }
 }
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_651501_document_body_autocomplete.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_651501_document_body_autocomplete.js
@@ -1,17 +1,22 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Tests that document.body autocompletes in the web console.
 
-Cu.import("resource:///modules/PropertyPanel.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/PropertyPanel.jsm", tempScope);
+let PropertyPanel = tempScope.PropertyPanel;
+let PropertyTreeView = tempScope.PropertyTreeView;
+let namesAndValuesOf = tempScope.namesAndValuesOf;
+let isNonNativeGetter = tempScope.isNonNativeGetter;
 
 registerCleanupFunction(function() {
   Services.prefs.clearUserPref("devtools.gcli.enable");
 });
 
 function test() {
   Services.prefs.setBoolPref("devtools.gcli.enable", false);
   addTab("data:text/html,Web Console autocompletion bug in document.body");
--- a/browser/devtools/webconsole/test/head.js
+++ b/browser/devtools/webconsole/test/head.js
@@ -31,17 +31,20 @@
  * 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 GPL or the LGPL. 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 ***** */
 
-Cu.import("resource:///modules/HUDService.jsm");
+let tempScope = {};
+Cu.import("resource:///modules/HUDService.jsm", tempScope);
+let HUDService = tempScope.HUDService;
+let ConsoleUtils = tempScope.ConsoleUtils;
 
 function log(aMsg)
 {
   dump("*** WebConsoleTest: " + aMsg + "\n");
 }
 
 function pprint(aObj)
 {
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -280,16 +280,17 @@
 @BINPATH@/components/fuelApplication.js
 @BINPATH@/components/WebContentConverter.js
 @BINPATH@/components/BrowserComponents.manifest
 @BINPATH@/components/nsBrowserContentHandler.js
 @BINPATH@/components/nsBrowserGlue.js
 @BINPATH@/components/nsSetDefaultBrowser.manifest
 @BINPATH@/components/nsSetDefaultBrowser.js
 @BINPATH@/components/BrowserPlaces.manifest
+@BINPATH@/components/BrowserPageThumbs.manifest
 @BINPATH@/components/nsPrivateBrowsingService.manifest
 @BINPATH@/components/nsPrivateBrowsingService.js
 @BINPATH@/components/toolkitsearch.manifest
 @BINPATH@/components/nsSearchService.js
 @BINPATH@/components/nsSearchSuggestions.js
 @BINPATH@/components/passwordmgr.manifest
 @BINPATH@/components/nsLoginInfo.js
 @BINPATH@/components/nsLoginManager.js
@@ -343,16 +344,17 @@
 @BINPATH@/components/toolkitplaces.manifest
 @BINPATH@/components/nsLivemarkService.js
 @BINPATH@/components/nsTaggingService.js
 @BINPATH@/components/nsPlacesAutoComplete.manifest
 @BINPATH@/components/nsPlacesAutoComplete.js
 @BINPATH@/components/nsPlacesExpiration.js
 @BINPATH@/components/PlacesProtocolHandler.js
 @BINPATH@/components/PlacesCategoriesStarter.js
+@BINPATH@/components/PageThumbsProtocol.js
 @BINPATH@/components/nsDefaultCLH.manifest
 @BINPATH@/components/nsDefaultCLH.js
 @BINPATH@/components/nsContentPrefService.manifest
 @BINPATH@/components/nsContentPrefService.js
 @BINPATH@/components/nsContentDispatchChooser.manifest
 @BINPATH@/components/nsContentDispatchChooser.js
 @BINPATH@/components/nsHandlerService.manifest
 @BINPATH@/components/nsHandlerService.js
--- a/browser/locales/en-US/chrome/browser/devtools/styleeditor.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/styleeditor.dtd
@@ -17,27 +17,16 @@
 <!ENTITY newButton.accesskey        "N">
 <!ENTITY newButton.commandkey       "n">
 
 <!ENTITY importButton.label         "Import…">
 <!ENTITY importButton.tooltip       "Import and append an existing style sheet to the document">
 <!ENTITY importButton.accesskey     "I">
 <!ENTITY importButton.commandkey    "i">
 
-<!ENTITY searchInput.tooltip        "Filter style sheets by name">
-<!ENTITY searchInput.placeholder    "Find style sheet">
-
-<!-- LOCALIZATION NOTE  (searchNoResults): This is shown when searching a term
-     that is not found in any stylesheet or stylesheet name. -->
-<!ENTITY searchNoResults.label      "No matching style sheet has been found.">
-
-<!-- LOCALIZATION NOTE  (searchClearButton): This button clears the search input
-     box and is visible only when a search term has been typed. -->
-<!ENTITY searchClearButton.label    "Clear">
-
 <!ENTITY visibilityToggle.tooltip   "Toggle style sheet visibility">
 <!ENTITY visibilityToggle.accesskey "V">
 
 <!ENTITY saveButton.label           "Save">
 <!ENTITY saveButton.tooltip         "Save this style sheet to a file">
 <!ENTITY saveButton.accesskey       "S">
 <!ENTITY saveButton.commandkey      "s">
 
@@ -55,8 +44,11 @@
      tip sentence shown when there is no stylesheet. It suggests to create a new
      stylesheet and provides an action link to do so. -->
 <!ENTITY noStyleSheet-tip-start.label  "Perhaps you'd like to ">
 <!-- LOCALICATION NOTE  (noStyleSheet-tip-action.label): This is text for the
      link that triggers creation of a new stylesheet. -->
 <!ENTITY noStyleSheet-tip-action.label "append a new style sheet">
 <!-- LOCALICATION NOTE  (noStyleSheet-tip-end.label): End of the tip sentence -->
 <!ENTITY noStyleSheet-tip-end.label    "?">
+
+<!-- LOCALIZATION NOTE  (closeCmd.key): Accel + this key closes the window. -->
+<!ENTITY closeCmd.key                  "W">
--- a/browser/modules/Makefile.in
+++ b/browser/modules/Makefile.in
@@ -47,16 +47,17 @@ include $(topsrcdir)/config/config.mk
 ifdef ENABLE_TESTS
 DIRS += test
 endif
 
 EXTRA_JS_MODULES = \
 	openLocationLastURL.jsm \
 	NetworkPrioritizer.jsm \
 	offlineAppCache.jsm \
+	TelemetryTimestamps.jsm \
 	$(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows) 
 EXTRA_JS_MODULES += \
 	WindowsPreviewPerTab.jsm \
 	WindowsJumpLists.jsm \
 	$(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/browser/modules/TelemetryTimestamps.jsm
@@ -0,0 +1,26 @@
+/* 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/. */
+
+let EXPORTED_SYMBOLS = ["TelemetryTimestamps"];
+
+let TelemetryTimestamps = {
+  timeStamps: {},
+  add: function TT_add(name, value) {
+    // Default to "now" if not specified
+    if (value == null)
+      value = Date.now();
+
+    if (isNaN(value))
+      throw new Error("Value must be a timestamp");
+
+    // If there's an existing value, just ignore the new value.
+    if (this.timeStamps.hasOwnProperty(name))
+      return;
+
+    this.timeStamps[name] = value;
+  },
+  get: function TT_get() {
+    return JSON.parse(JSON.stringify(this.timeStamps));
+  }
+};
--- a/browser/modules/test/Makefile.in
+++ b/browser/modules/test/Makefile.in
@@ -40,16 +40,17 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = browser/modules/test
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
                  browser_NetworkPrioritizer.js \
+                 browser_TelemetryTimestamps.js \
                  $(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows) 
 _BROWSER_FILES += \
                  browser_taskbar_preview.js \
                  $(NULL)
 endif
 
new file mode 100644
--- /dev/null
+++ b/browser/modules/test/browser_TelemetryTimestamps.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function getSimpleMeasurementsFromTelemetryPing() {
+  const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsIObserver);
+  let str = Cc['@mozilla.org/supports-string;1'].createInstance(Ci.nsISupportsString);
+  TelemetryPing.observe(str, "get-payload", "");
+
+  return JSON.parse(str.data).simpleMeasurements;
+}
+
+function test() {
+  // Test the module logic
+  Cu.import("resource:///modules/TelemetryTimestamps.jsm");
+  let now = Date.now();
+  TelemetryTimestamps.add("foo");
+  let fooValue = TelemetryTimestamps.get().foo;
+  ok(fooValue, "foo was added");
+  ok(fooValue >= now, "foo has a reasonable value");
+
+  // Add timestamp with value
+  TelemetryTimestamps.add("bar", 1);
+  ok(TelemetryTimestamps.get().bar, "bar was added");
+  is(TelemetryTimestamps.get().bar, 1, "bar has the right value");
+
+  // Can't add the same timestamp twice
+  TelemetryTimestamps.add("bar", 2);
+  is(TelemetryTimestamps.get().bar, 1, "bar wasn't overwritten");
+
+  let threw = false;
+  try {
+    TelemetryTimestamps.add("baz", "this isn't a number");
+  } catch (ex) {
+    threw = true;
+  }
+  ok(threw, "adding baz threw");
+  ok(!TelemetryTimestamps.get().baz, "no baz was added");
+
+  // Test that the data gets added to the telemetry ping properly
+  let simpleMeasurements = getSimpleMeasurementsFromTelemetryPing();
+  ok(simpleMeasurements, "got simple measurements from ping data");
+  is(simpleMeasurements.foo, fooValue, "foo was included");
+  is(simpleMeasurements.bar, 1, "bar was included");
+  ok(!simpleMeasurements.baz, "baz wasn't included since it wasn't added");
+
+  // Check browser timestamps that we add
+  let props = [
+    // These can't be reliably tested when the test is run alone
+    //"delayedStartupStarted",
+    //"delayedStartupFinished",
+    "sessionRestoreInitialized",
+    // This doesn't get hit in the testing profile
+    //"sessionRestoreRestoring"
+  ];
+
+  props.forEach(function (p) {
+    let value = simpleMeasurements[p];
+    ok(value, p + " exists");
+    ok(!isNaN(value), p + " is a number");
+    ok(value > 0 && value < Date.now(), p + " value is reasonable");
+  });
+}
--- a/build/autoconf/mozconfig2client-mk
+++ b/build/autoconf/mozconfig2client-mk
@@ -44,18 +44,16 @@
 # See mozconfig2configure for more details
 
 print_header() {
   _mozconfig=${MOZCONFIG:-$HOME/.mozconfig}
   cat >> $tmp_file <<EOF
 # gmake
 # This file is automatically generated for client.mk.
 # Do not edit. Edit $_mozconfig instead.
-# To create a new .mozconfig file, you can visit,
-#   http://webtools.mozilla.org/build/config.cgi
 
 EOF
 }
 
 ac_add_options() {
   echo "# $* is used by configure (not client.mk)" >> $tmp_file
 }
 
--- a/build/mobile/devicemanager.py
+++ b/build/mobile/devicemanager.py
@@ -57,252 +57,292 @@ class DMError(Exception):
 
   def __init__(self, msg= ''):
     self.msg = msg
 
   def __str__(self):
     return self.msg
 
 
+def abstractmethod(method):
+  line = method.func_code.co_firstlineno
+  filename = method.func_code.co_filename
+  def not_implemented(*args, **kwargs):
+    raise NotImplementedError('Abstract method %s at File "%s", line %s \
+                              should be implemented by a concrete class' %
+                              (repr(method), filename,line))
+    return not_implemented
+  
 class DeviceManager:
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
+  
+  @abstractmethod
   def pushFile(self, localname, destname):
-    assert 0 == 1
-    return False
-
-  # external function
-  # returns:
-  #  success: directory name
-  #  failure: None
+    """
+    external function
+    returns:
+    success: True
+    failure: False
+    """
+    
+  @abstractmethod
   def mkDir(self, name):
-      assert 0 == 1
-      return None
-
-  # make directory structure on the device
-  # external function
-  # returns:
-  #  success: directory structure that we created
-  #  failure: None
+    """
+    external function
+    returns:
+    success: directory name
+    failure: None
+    """
+    
+  @abstractmethod
   def mkDirs(self, filename):
-      assert 0 == 1
-      return None
-
-  # push localDir from host to remoteDir on the device
-  # external function
-  # returns:
-  #  success: remoteDir
-  #  failure: None
+    """
+    make directory structure on the device
+    external function
+    returns:
+    success: directory structure that we created
+    failure: None
+    """
+    
+  @abstractmethod
   def pushDir(self, localDir, remoteDir):
-    assert 0 == 1
-    return None
-
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
-  def dirExists(self, dirname):
-    assert 0 == 1
-    return False
-
-  # Because we always have / style paths we make this a lot easier with some
-  # assumptions
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
-  def fileExists(self, filepath):
-    assert 0 == 1
-    return False
+    """
+    push localDir from host to remoteDir on the device
+    external function
+    returns:
+    success: remoteDir
+    failure: None
+    """
 
-  # list files on the device, requires cd to directory first
-  # external function
-  # returns:
-  #  success: array of filenames, ['file1', 'file2', ...]
-  #  failure: []
+  @abstractmethod
+  def dirExists(self, dirname):
+    """
+    external function
+    returns:
+    success: True
+    failure: False
+    """
+    
+  @abstractmethod
+  def fileExists(self, filepath):
+    """
+    Because we always have / style paths we make this a lot easier with some
+    assumptions
+    external function
+    returns:
+    success: True
+    failure: False
+    """
+    
+  @abstractmethod
   def listFiles(self, rootdir):
-    assert 0 == 1
-    return []
-
-  # external function
-  # returns:
-  #  success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt"
-  #  failure: None
+    """
+    list files on the device, requires cd to directory first
+    external function
+    returns:
+    success: array of filenames, ['file1', 'file2', ...]
+    failure: None
+    """
+  
+  @abstractmethod
   def removeFile(self, filename):
-    assert 0 == 1
-    return False
-
-  # does a recursive delete of directory on the device: rm -Rf remoteDir
-  # external function
-  # returns:
-  #  success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt"
-  #  failure: None
+    """
+    external function
+    returns:
+    success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt"
+    failure: None
+    """
+    
+  @abstractmethod
   def removeDir(self, remoteDir):
-    assert 0 == 1
-    return None
-
-  # external function
-  # returns:
-  #  success: array of process tuples
-  #  failure: []
+    """
+    does a recursive delete of directory on the device: rm -Rf remoteDir
+    external function
+    returns:
+    success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt"
+    failure: None
+    """
+    
+  @abstractmethod
   def getProcessList(self):
-    assert 0 == 1
-    return []
-
-  # external function
-  # returns:
-  #  success: pid
-  #  failure: None
+    """
+    external function
+    returns:
+    success: array of process tuples
+    failure: None
+    """
+    
+  @abstractmethod
   def fireProcess(self, appname, failIfRunning=False):
-    assert 0 == 1
-    return None
-
-  # external function
-  # returns:
-  #  success: output filename
-  #  failure: None
+    """
+    external function
+    returns:
+    success: pid
+    failure: None
+    """
+    
+  @abstractmethod
   def launchProcess(self, cmd, outputFile = "process.txt", cwd = '', env = '', failIfRunning=False):
-    assert 0 == 1
-    return None
-
-  # loops until 'process' has exited or 'timeout' seconds is reached
-  # loop sleeps for 'interval' seconds between iterations
-  # external function
-  # returns:
-  #  success: [file contents, None]
-  #  failure: [None, None]
+    """
+    external function
+    returns:
+    success: output filename
+    failure: None
+    """
+    
   def communicate(self, process, timeout = 600, interval = 5):
+    """
+    loops until 'process' has exited or 'timeout' seconds is reached
+    loop sleeps for 'interval' seconds between iterations
+    external function
+    returns:
+    success: [file contents, None]
+    failure: [None, None]
+    """
+    
     timed_out = True
     if (timeout > 0):
       total_time = 0
       while total_time < timeout:
         time.sleep(interval)
         if self.processExist(process) == None:
           timed_out = False
           break
         total_time += interval
 
     if (timed_out == True):
       return [None, None]
 
     return [self.getFile(process, "temp.txt"), None]
 
-  # iterates process list and returns pid if exists, otherwise None
-  # external function
-  # returns:
-  #  success: pid
-  #  failure: None
   def processExist(self, appname):
+    """
+    iterates process list and returns pid if exists, otherwise None
+    external function
+    returns:
+    success: pid
+    failure: None
+    """
+    
     pid = None
 
     #filter out extra spaces
     parts = filter(lambda x: x != '', appname.split(' '))
     appname = ' '.join(parts)
 
     #filter out the quoted env string if it exists
     #ex: '"name=value;name2=value2;etc=..." process args' -> 'process args'
     parts = appname.split('"')
     if (len(parts) > 2):
       appname = ' '.join(parts[2:]).strip()
   
     pieces = appname.split(' ')
     parts = pieces[0].split('/')
     app = parts[-1]
+    procre = re.compile('.*' + app + '.*')
 
     procList = self.getProcessList()
     if (procList == []):
       return None
       
     for proc in procList:
-      procName = proc[1].split('/')[-1]
-      if (procName == app):
+      if (procre.match(proc[1])):
         pid = proc[0]
         break
     return pid
 
-  # external function
-  # returns:
-  #  success: output from testagent
-  #  failure: None
+
+  @abstractmethod
   def killProcess(self, appname):
-    assert 0 == 1
-    return None
-
-  # external function
-  # returns:
-  #  success: filecontents
-  #  failure: None
+    """
+    external function
+    returns:
+    success: output from testagent
+    failure: None
+    """
+    
+  @abstractmethod
   def catFile(self, remoteFile):
-    assert 0 == 1
-    return None
-
-  # external function
-  # returns:
-  #  success: output of pullfile, string
-  #  failure: None
+    """
+    external function
+    returns:
+    success: filecontents
+    failure: None
+    """
+    
+  @abstractmethod
   def pullFile(self, remoteFile):
-    assert 0 == 1
-    return None
-
-  # copy file from device (remoteFile) to host (localFile)
-  # external function
-  # returns:
-  #  success: output of pullfile, string
-  #  failure: None
+    """
+    external function
+    returns:
+    success: output of pullfile, string
+    failure: None
+    """
+    
+  @abstractmethod
   def getFile(self, remoteFile, localFile = ''):
-    assert 0 == 1
-    return None
-
-  # copy directory structure from device (remoteDir) to host (localDir)
-  # external function
-  # checkDir exists so that we don't create local directories if the
-  # remote directory doesn't exist but also so that we don't call isDir
-  # twice when recursing.
-  # returns:
-  #  success: list of files, string
-  #  failure: None
+    """
+    copy file from device (remoteFile) to host (localFile)
+    external function
+    returns:
+    success: output of pullfile, string
+    failure: None
+    """
+    
+  @abstractmethod
   def getDirectory(self, remoteDir, localDir, checkDir=True):
-    assert 0 == 1
-    return None
-
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
-  #  Throws a FileError exception when null (invalid dir/filename)
+    """
+    copy directory structure from device (remoteDir) to host (localDir)
+    external function
+    checkDir exists so that we don't create local directories if the
+    remote directory doesn't exist but also so that we don't call isDir
+    twice when recursing.
+    returns:
+    success: list of files, string
+    failure: None
+    """
+    
+  @abstractmethod
   def isDir(self, remotePath):
-    assert 0 == 1
-    return False
-
-  # true/false check if the two files have the same md5 sum
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
+    """
+    external function
+    returns:
+    success: True
+    failure: False
+    Throws a FileError exception when null (invalid dir/filename)
+    """
+    
+  @abstractmethod
   def validateFile(self, remoteFile, localFile):
-    assert 0 == 1
-    return False
-
-  # return the md5 sum of a remote file
-  # internal function
-  # returns:
-  #  success: MD5 hash for given filename
-  #  failure: None
+    """
+    true/false check if the two files have the same md5 sum
+    external function
+    returns:
+    success: True
+    failure: False
+    """
+    
+  @abstractmethod
   def getRemoteHash(self, filename):
-    assert 0 == 1
-    return None
-
-  # return the md5 sum of a file on the host
-  # internal function
-  # returns:
-  #  success: MD5 hash for given filename
-  #  failure: None
+    """
+    return the md5 sum of a remote file
+    internal function
+    returns:
+    success: MD5 hash for given filename
+    failure: None
+    """
+    
   def getLocalHash(self, filename):
+    """
+    return the md5 sum of a file on the host
+    internal function
+    returns:
+    success: MD5 hash for given filename
+    failure: None
+    """
+    
     file = open(filename, 'rb')
     if (file == None):
       return None
 
     try:
       mdsum = hashlib.md5()
     except:
       return None
@@ -312,162 +352,201 @@ class DeviceManager:
       if not data:
         break
       mdsum.update(data)
 
     file.close()
     hexval = mdsum.hexdigest()
     if (self.debug >= 3): print "local hash returned: '" + hexval + "'"
     return hexval
-  # Gets the device root for the testing area on the device
-  # For all devices we will use / type slashes and depend on the device-agent
-  # to sort those out.  The agent will return us the device location where we
-  # should store things, we will then create our /tests structure relative to
-  # that returned path.
-  # Structure on the device is as follows:
-  # /tests
-  #       /<fennec>|<firefox>  --> approot
-  #       /profile
-  #       /xpcshell
-  #       /reftest
-  #       /mochitest
-  #
-  # external function
-  # returns:
-  #  success: path for device root
-  #  failure: None
+  
+  @abstractmethod
   def getDeviceRoot(self):
-    assert 0 == 1
+    """
+    Gets the device root for the testing area on the device
+    For all devices we will use / type slashes and depend on the device-agent
+    to sort those out.  The agent will return us the device location where we
+    should store things, we will then create our /tests structure relative to
+    that returned path.
+    Structure on the device is as follows:
+    /tests
+          /<fennec>|<firefox>  --> approot
+          /profile
+          /xpcshell
+          /reftest
+          /mochitest
+    external 
+    returns:
+    success: path for device root
+    failure: None
+    """
+  
+  def getAppRoot(self):
+    """
+    Either we will have /tests/fennec or /tests/firefox but we will never have
+    both.  Return the one that exists
+    TODO: ensure we can support org.mozilla.firefox
+    external function
+    returns:
+    success: path for app root
+    failure: None
+    """
+    
+    devroot = self.getDeviceRoot()
+    if (devroot == None):
+      return None
+
+    if (self.dirExists(devroot + '/fennec')):
+      return devroot + '/fennec'
+    elif (self.dirExists(devroot + '/firefox')):
+      return devroot + '/firefox'
+    elif (self.dirExsts('/data/data/org.mozilla.fennec')):
+      return 'org.mozilla.fennec'
+    elif (self.dirExists('/data/data/org.mozilla.firefox')):
+      return 'org.mozilla.firefox'
+    elif (self.dirExists('/data/data/org.mozilla.fennec_aurora')):
+      return 'org.mozilla.fennec_aurora'
+    elif (self.dirExists('/data/data/org.mozilla.firefox_beta')):
+      return 'org.mozilla.firefox_beta'
+
+    # Failure (either not installed or not a recognized platform)
     return None
 
-  # Either we will have /tests/fennec or /tests/firefox but we will never have
-  # both.  Return the one that exists
-  # TODO: ensure we can support org.mozilla.firefox
-  # external function
-  # returns:
-  #  success: path for app root
-  #  failure: None
-  def getAppRoot(self):
-    assert 0 == 1
-    return None
-
-  # Gets the directory location on the device for a specific test type
-  # Type is one of: xpcshell|reftest|mochitest
-  # external function
-  # returns:
-  #  success: path for test root
-  #  failure: None
   def getTestRoot(self, type):
+    """
+    Gets the directory location on the device for a specific test type
+    Type is one of: xpcshell|reftest|mochitest
+    external function
+    returns:
+    success: path for test root
+    failure: None
+    """
+    
     devroot = self.getDeviceRoot()
     if (devroot == None):
       return None
 
     if (re.search('xpcshell', type, re.I)):
       self.testRoot = devroot + '/xpcshell'
     elif (re.search('?(i)reftest', type)):
       self.testRoot = devroot + '/reftest'
     elif (re.search('?(i)mochitest', type)):
       self.testRoot = devroot + '/mochitest'
     return self.testRoot
 
-  # Sends a specific process ID a signal code and action.
-  # For Example: SIGINT and SIGDFL to process x
   def signal(self, processID, signalType, signalAction):
-    # currently not implemented in device agent - todo
+    """
+    Sends a specific process ID a signal code and action.
+    For Example: SIGINT and SIGDFL to process x
+    """
+    #currently not implemented in device agent - todo
+    
     pass
 
-  # Get a return code from process ending -- needs support on device-agent
   def getReturnCode(self, processID):
+    """Get a return code from process ending -- needs support on device-agent"""
     # TODO: make this real
+    
     return 0
 
-  # external function
-  # returns:
-  #  success: output of unzip command
-  #  failure: None
+  @abstractmethod
   def unpackFile(self, filename):
-    return None
-
-  # external function
-  # returns:
-  #  success: status from test agent
-  #  failure: None
+    """
+    external function
+    returns:
+    success: output of unzip command
+    failure: None
+    """
+    
+  @abstractmethod
   def reboot(self, ipAddr=None, port=30000):
-    assert 0 == 1
-    return None
-
-  # validate localDir from host to remoteDir on the device
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
+    """
+    external function
+    returns:
+    success: status from test agent
+    failure: None
+    """
+    
   def validateDir(self, localDir, remoteDir):
+    """
+    validate localDir from host to remoteDir on the device
+    external function
+    returns:
+    success: True
+    failure: False
+    """
+    
     if (self.debug >= 2): print "validating directory: " + localDir + " to " + remoteDir
     for root, dirs, files in os.walk(localDir):
       parts = root.split(localDir)
       for file in files:
         remoteRoot = remoteDir + '/' + parts[1]
         remoteRoot = remoteRoot.replace('/', '/')
         if (parts[1] == ""): remoteRoot = remoteDir
         remoteName = remoteRoot + '/' + file
         if (self.validateFile(remoteName, os.path.join(root, file)) <> True):
             return False
     return True
-
-  # Returns information about the device:
-  # Directive indicates the information you want to get, your choices are:
-  # os - name of the os
-  # id - unique id of the device
-  # uptime - uptime of the device
-  # systime - system time of the device
-  # screen - screen resolution
-  # memory - memory stats
-  # process - list of running processes (same as ps)
-  # disk - total, free, available bytes on disk
-  # power - power status (charge, battery temp)
-  # all - all of them - or call it with no parameters to get all the information
-  # returns:
-  #   success: dict of info strings by directive name
-  #   failure: {}
+    
+  @abstractmethod
   def getInfo(self, directive=None):
-    assert 0 == 1
-    return {}
-
-  # external function
-  # returns:
-  #  success: output from agent for inst command
-  #  failure: None
+    """
+    Returns information about the device:
+    Directive indicates the information you want to get, your choices are:
+    os - name of the os
+    id - unique id of the device
+    uptime - uptime of the device
+    systime - system time of the device
+    screen - screen resolution
+    memory - memory stats
+    process - list of running processes (same as ps)
+    disk - total, free, available bytes on disk
+    power - power status (charge, battery temp)
+    all - all of them - or call it with no parameters to get all the information
+    returns:
+    success: dict of info strings by directive name
+    failure: None
+    """
+    
+  @abstractmethod
   def installApp(self, appBundlePath, destPath=None):
-    assert 0 == 1
-    return None
-
-  # external function
-  # returns:
-  #  success: True
-  #  failure: None
+    """
+    external function
+    returns:
+    success: output from agent for inst command
+    failure: None
+    """
+    
+  @abstractmethod
   def uninstallAppAndReboot(self, appName, installPath=None):
-    assert 0 == 1
-    return None
-
-  # external function
-  # returns:
-  #  success: text status from command or callback server
-  #  failure: None
-  def updateApp(self, appBundlePath, processName=None, destPath=None, ipAddr=None, port=30000):
-    assert 0 == 1
-    return None
-
-  # external function
-  # returns:
-  #  success: time in ms
-  #  failure: None
+    """
+    external function
+    returns:
+    success: True
+    failure: None
+    """
+    
+  @abstractmethod
+  def updateApp(self, appBundlePath, processName=None,
+                destPath=None, ipAddr=None, port=30000):
+    """
+    external function
+    returns:
+    success: text status from command or callback server
+    failure: None
+    """
+  
+  @abstractmethod
   def getCurrentTime(self):
-    assert 0 == 1
-    return None
-
+    """
+    external function
+    returns:
+    success: time in ms
+    failure: None
+    """
 
 class NetworkTools:
   def __init__(self):
     pass
 
   # Utilities to get the local ip address
   def getInterfaceIp(self, ifname):
     if os.name != "nt":
@@ -478,20 +557,17 @@ class NetworkTools:
                               s.fileno(),
                               0x8915,  # SIOCGIFADDR
                               struct.pack('256s', ifname[:15])
                               )[20:24])
     else:
       return None
 
   def getLanIp(self):
-    try:
-      ip = socket.gethostbyname(socket.gethostname())
-    except socket.gaierror:
-      ip = socket.gethostbyname(socket.gethostname() + ".local") # for Mac OS X
+    ip = socket.gethostbyname(socket.gethostname())
     if ip.startswith("127.") and os.name != "nt":
       interfaces = ["eth0","eth1","eth2","wlan0","wlan1","wifi0","ath0","ath1","ppp0"]
       for ifname in interfaces:
         try:
           ip = self.getInterfaceIp(ifname)
           break;
         except IOError:
           pass
@@ -516,9 +592,8 @@ class NetworkTools:
           if seed > maxportnum:
             print "Could not find open port after checking 5000 ports"
           raise
         seed += 1
     except:
       print "Socket error trying to find open port"
         
     return seed
-
--- a/build/mobile/devicemanagerADB.py
+++ b/build/mobile/devicemanagerADB.py
@@ -110,24 +110,27 @@ class DeviceManagerADB(DeviceManager):
     # adb "push" accepts a directory as an argument, but if the directory
     # contains symbolic links, the links are pushed, rather than the linked
     # files; we either zip/unzip or push file-by-file to get around this 
     # limitation
     try:
       if (not self.dirExists(remoteDir)):
         self.mkDirs(remoteDir+"/x")
       if (self.useZip):
-        localZip = tempfile.mktemp()+".zip"
-        remoteZip = remoteDir + "/adbdmtmp.zip"
-        subprocess.check_output(["zip", "-r", localZip, '.'], cwd=localDir)
-        self.pushFile(localZip, remoteZip)
-        os.remove(localZip)
-        data = self.runCmdAs(["shell", "unzip", "-o", remoteZip, "-d", remoteDir]).stdout.read()
-        self.checkCmdAs(["shell", "rm", remoteZip])
-        if (re.search("unzip: exiting", data) or re.search("Operation not permitted", data)):
+        try:
+          localZip = tempfile.mktemp()+".zip"
+          remoteZip = remoteDir + "/adbdmtmp.zip"
+          subprocess.check_output(["zip", "-r", localZip, '.'], cwd=localDir)
+          self.pushFile(localZip, remoteZip)
+          os.remove(localZip)
+          data = self.runCmdAs(["shell", "unzip", "-o", remoteZip, "-d", remoteDir]).stdout.read()
+          self.checkCmdAs(["shell", "rm", remoteZip])
+          if (re.search("unzip: exiting", data) or re.search("Operation not permitted", data)):
+            raise Exception("unzip failed, or permissions error")
+        except:
           print "zip/unzip failure: falling back to normal push"
           self.useZip = False
           self.pushDir(localDir, remoteDir)
       else:
         for root, dirs, files in os.walk(localDir, followlinks='true'):
           relRoot = os.path.relpath(root, localDir)
           for file in files:
             localFile = os.path.join(root, file)
--- a/build/mobile/robocop/Makefile.in
+++ b/build/mobile/robocop/Makefile.in
@@ -40,17 +40,17 @@ topsrcdir   = @top_srcdir@
 srcdir      = @srcdir@
 VPATH       = @srcdir@
 TESTPATH    = $(topsrcdir)/mobile/android/base/tests
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = robocop
 
-ROBOTIUM_PATH = $(srcdir)/robotium-solo-3.0.jar
+ROBOTIUM_PATH = $(srcdir)/robotium-solo-3.1.jar
 
 JAVAFILES = \
   R.java \
 
 _JAVA_HARNESS = \
   Actions.java \
   Assert.java \
   Driver.java \
--- a/build/mobile/robocop/README
+++ b/build/mobile/robocop/README
@@ -1,9 +1,9 @@
 Robocop is a Mozilla project which uses Robotium to test Firefox on Android devices.
 
 Robotium is an open source tool licensed under the Apache 2.0 license and the original 
 source can be found here:
 http://code.google.com/p/robotium/
 
-We are including robotium-solo-3.0.jar as a binary and are not modifying it in anyway 
+We are including robotium-solo-3.1.jar as a binary and are not modifying it in anyway 
 from the original download found at: 
 http://code.google.com/p/robotium/
deleted file mode 100644
index 3305251f61d61702e0f3dd61f67eb11e984ac587..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0ba05859b90a8c29ca519e228674db9aebe69d90
GIT binary patch
literal 49691
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
KV6(Nqwf+y-lXzPI
--- a/build/unix/build-toolchain/build-gcc.py
+++ b/build/unix/build-toolchain/build-gcc.py
@@ -40,19 +40,43 @@ def build_package(package_source_dir, pa
     run_in(package_build_dir, ["make", "-j8"])
     run_in(package_build_dir, ["make", "install"])
 
 def build_tar(base_dir, tar_inst_dir):
     tar_build_dir = base_dir + '/tar_build'
     build_package(tar_source_dir, tar_build_dir,
                   ["--prefix=%s" % tar_inst_dir])
 
-def build_one_stage(env, stage_dir):
+def with_env(env, f):
     old_env = os.environ.copy()
     os.environ.update(env)
+    f()
+    os.environ.clear()
+    os.environ.update(old_env)
+
+def build_glibc(env, stage_dir, inst_dir):
+    def f():
+        build_glibc_aux(stage_dir, inst_dir)
+    with_env(env, f)
+
+def build_glibc_aux(stage_dir, inst_dir):
+    glibc_build_dir = stage_dir + '/glibc'
+    build_package(glibc_source_dir, glibc_build_dir,
+                  ["--disable-profile",
+                   "--enable-add-ons=nptl",
+                   "--without-selinux",
+                   "--enable-kernel=2.6.18",
+                   "--prefix=%s" % inst_dir])
+
+def build_one_stage(env, stage_dir, is_stage_one):
+    def f():
+        build_one_stage_aux(stage_dir, is_stage_one)
+    with_env(env, f)
+
+def build_one_stage_aux(stage_dir, is_stage_one):
     os.mkdir(stage_dir)
 
     lib_inst_dir = stage_dir + '/libinst'
 
     gmp_build_dir = stage_dir + '/gmp'
     build_package(gmp_source_dir, gmp_build_dir,
                   ["--prefix=%s" % lib_inst_dir, "--disable-shared"])
     mpfr_build_dir = stage_dir + '/mpfr'
@@ -67,98 +91,116 @@ def build_one_stage(env, stage_dir):
 
     tool_inst_dir = stage_dir + '/inst'
 
     binutils_build_dir = stage_dir + '/binutils'
     build_package(binutils_source_dir, binutils_build_dir,
                   ["--prefix=%s" % tool_inst_dir])
 
     gcc_build_dir = stage_dir + '/gcc'
-    build_package(gcc_source_dir, gcc_build_dir,
-                  ["--prefix=%s" % tool_inst_dir,
-                   "--enable-__cxa_atexit",
-                   "--with-gmp=%s" % lib_inst_dir,
-                   "--with-mpfr=%s" % lib_inst_dir,
-                   "--with-mpc=%s" % lib_inst_dir,
-                   "--enable-languages=c,c++",
-                   "--disable-bootstrap"])
-    os.environ.clear()
-    os.environ.update(old_env)
+    gcc_configure_args = ["--prefix=%s" % tool_inst_dir,
+                          "--enable-__cxa_atexit",
+                          "--with-gmp=%s" % lib_inst_dir,
+                          "--with-mpfr=%s" % lib_inst_dir,
+                          "--with-mpc=%s" % lib_inst_dir,
+                          "--disable-bootstrap"]
+    if is_stage_one:
+        gcc_configure_args.append("--enable-languages=c")
+    else:
+        gcc_configure_args.append("--enable-languages=c,c++")
+
+    build_package(gcc_source_dir, gcc_build_dir, gcc_configure_args)
+    build_glibc({"CC"  : tool_inst_dir + "/bin/gcc",
+                 "CXX" : tool_inst_dir + "/bin/g++"},
+                stage_dir, tool_inst_dir)
 
 def build_tar_package(tar, name, base, directory):
     name = os.path.realpath(name)
     run_in(base, [tar, "-cf", name, "--mtime=2012-01-01", "--owner=root",
                   directory])
 
 ##############################################
 
-source_dir = os.path.realpath('src')
+# The directories end up in the debug info, so the easy way of getting
+# a reproducible build is to run it in a know absolute directory.
+# We use a directory in /builds/slave because the mozilla infrastructure
+# cleans it up automatically.
+base_dir = "/builds/slave/moz-toolschain"
+
+source_dir = base_dir + "/src"
+build_dir  = base_dir + "/build"
 
 def build_source_dir(prefix, version):
     return source_dir + '/' + prefix + version
 
 binutils_version = "2.21.1"
+glibc_version = "2.13" #FIXME: should probably use 2.5.1
 tar_version = "1.26"
 gcc_version = "4.5.2"
 mpfr_version = "2.4.2"
 gmp_version = "5.0.1"
 mpc_version = "0.8.1"
 
 binutils_source_uri = "http://ftp.gnu.org/gnu/binutils/binutils-%sa.tar.bz2" % \
     binutils_version
+glibc_source_uri = "http://ftp.gnu.org/gnu/glibc/glibc-%s.tar.bz2" % \
+    glibc_version
 tar_source_uri = "http://ftp.gnu.org/gnu/tar/tar-%s.tar.bz2" % \
     tar_version
 gcc_source_uri = "http://ftp.gnu.org/gnu/gcc/gcc-%s/gcc-%s.tar.bz2" % \
     (gcc_version, gcc_version)
 mpfr_source_uri = "http://www.mpfr.org/mpfr-%s/mpfr-%s.tar.bz2" % \
     (mpfr_version, mpfr_version)
 gmp_source_uri = "http://ftp.gnu.org/gnu/gmp/gmp-%s.tar.bz2" % gmp_version
 mpc_source_uri = "http://www.multiprecision.org/mpc/download/mpc-%s.tar.gz" % \
     mpc_version
 
 binutils_source_tar = download_uri(binutils_source_uri)
+glibc_source_tar = download_uri(glibc_source_uri)
 tar_source_tar = download_uri(tar_source_uri)
 mpc_source_tar = download_uri(mpc_source_uri)
 mpfr_source_tar = download_uri(mpfr_source_uri)
 gmp_source_tar = download_uri(gmp_source_uri)
 gcc_source_tar = download_uri(gcc_source_uri)
 
-build_dir = os.path.realpath('build')
-
 binutils_source_dir  = build_source_dir('binutils-', binutils_version)
+glibc_source_dir  = build_source_dir('glibc-', glibc_version)
 tar_source_dir  = build_source_dir('tar-', tar_version)
 mpc_source_dir  = build_source_dir('mpc-', mpc_version)
 mpfr_source_dir = build_source_dir('mpfr-', mpfr_version)
 gmp_source_dir  = build_source_dir('gmp-', gmp_version)
 gcc_source_dir  = build_source_dir('gcc-', gcc_version)
 
 if not os.path.exists(source_dir):
-    os.mkdir(source_dir)
+    os.makedirs(source_dir)
     extract(binutils_source_tar, source_dir)
     patch('binutils-deterministic.patch', 1, binutils_source_dir)
+    extract(glibc_source_tar, source_dir)
+    patch('glibc-deterministic.patch', 1, glibc_source_dir)
     extract(tar_source_tar, source_dir)
     extract(mpc_source_tar, source_dir)
     extract(mpfr_source_tar, source_dir)
     extract(gmp_source_tar, source_dir)
     extract(gcc_source_tar, source_dir)
     patch('plugin_finish_decl.diff', 0, gcc_source_dir)
     patch('pr49911.diff', 1, gcc_source_dir)
     patch('r159628-r163231-r171807.patch', 1, gcc_source_dir)
 
 if os.path.exists(build_dir):
     shutil.rmtree(build_dir)
-os.mkdir(build_dir)
+os.makedirs(build_dir)
 
 tar_inst_dir = build_dir + '/tar_inst'
 build_tar(build_dir, tar_inst_dir)
 
 stage1_dir = build_dir + '/stage1'
-build_one_stage({"CC": "gcc", "CXX" : "g++"}, stage1_dir)
+build_one_stage({"CC": "gcc", "CXX" : "g++"}, stage1_dir, True)
 
 stage1_tool_inst_dir = stage1_dir + '/inst'
 stage2_dir = build_dir + '/stage2'
 build_one_stage({"CC"     : stage1_tool_inst_dir + "/bin/gcc",
                  "CXX"    : stage1_tool_inst_dir + "/bin/g++",
                  "AR"     : stage1_tool_inst_dir + "/bin/ar",
-                 "RANLIB" : "true" })
+                 "RANLIB" : "true" },
+                stage2_dir, False)
 
 build_tar_package(tar_inst_dir + "/bin/tar",
                   "toolchain.tar", stage2_dir, "inst")
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -143,16 +143,17 @@ MOZ_UPDATER	= @MOZ_UPDATER@
 MOZ_UPDATE_CHANNEL	= @MOZ_UPDATE_CHANNEL@
 MOZ_UPDATE_PACKAGING	= @MOZ_UPDATE_PACKAGING@
 MOZ_DISABLE_PARENTAL_CONTROLS = @MOZ_DISABLE_PARENTAL_CONTROLS@
 NS_ENABLE_TSF = @NS_ENABLE_TSF@
 MOZ_SPELLCHECK = @MOZ_SPELLCHECK@
 MOZ_ANDROID_HISTORY = @MOZ_ANDROID_HISTORY@
 MOZ_WEBSMS_BACKEND = @MOZ_WEBSMS_BACKEND@
 MOZ_JAVA_COMPOSITOR = @MOZ_JAVA_COMPOSITOR@
+MOZ_ONLY_TOUCH_EVENTS = @MOZ_ONLY_TOUCH_EVENTS@
 MOZ_TOUCH = @MOZ_TOUCH@
 MOZ_PROFILELOCKING = @MOZ_PROFILELOCKING@
 MOZ_FEEDS = @MOZ_FEEDS@
 MOZ_TOOLKIT_SEARCH = @MOZ_TOOLKIT_SEARCH@
 MOZ_PLACES = @MOZ_PLACES@
 MOZ_SAFE_BROWSING = @MOZ_SAFE_BROWSING@
 MOZ_URL_CLASSIFIER = @MOZ_URL_CLASSIFIER@
 MOZ_ZIPWRITER = @MOZ_ZIPWRITER@
--- a/config/expandlibs.py
+++ b/config/expandlibs.py
@@ -81,16 +81,21 @@ def relativize(path):
         del abspath[0]
     if not curdir and not abspath:
         return '.'
     relpath = os.path.join(*[os.pardir for i in curdir] + abspath)
     if len(path) > len(relpath):
         return relpath
     return path
 
+def isObject(path):
+    '''Returns whether the given path points to an object file, that is,
+    ends with OBJ_SUFFIX or .i_o'''
+    return os.path.splitext(path)[1] in [conf.OBJ_SUFFIX, '.i_o']
+
 class LibDescriptor(dict):
     KEYS = ['OBJS', 'LIBS']
 
     def __init__(self, content=None):
         '''Creates an instance of a lib descriptor, initialized with contents
         from a list of strings when given. This is intended for use with
         file.readlines()'''
         if isinstance(content, list) and all([isinstance(item, str) for item in content]):
--- a/config/expandlibs_exec.py
+++ b/config/expandlibs_exec.py
@@ -51,17 +51,17 @@ See https://bugzilla.mozilla.org/show_bu
 
 With the --reorder argument, followed by a file name, it will reorder the
 object files from the command line according to the order given in the file.
 Implies --extract.
 '''
 from __future__ import with_statement
 import sys
 import os
-from expandlibs import ExpandArgs, relativize
+from expandlibs import ExpandArgs, relativize, isObject
 import expandlibs_config as conf
 from optparse import OptionParser
 import subprocess
 import tempfile
 import shutil
 
 class ExpandArgsMore(ExpandArgs):
     ''' Meant to be used as 'with ExpandArgsMore(args) as ...: '''
@@ -92,29 +92,29 @@ class ExpandArgsMore(ExpandArgs):
                 if os.path.exists(arg + conf.LIBS_DESC_SUFFIX):
                     newlist += self._extract(self._expand_desc(arg))
                 elif os.path.exists(arg) and len(ar_extract):
                     tmp = tempfile.mkdtemp(dir=os.curdir)
                     self.tmp.append(tmp)
                     subprocess.call(ar_extract + [os.path.abspath(arg)], cwd=tmp)
                     objs = []
                     for root, dirs, files in os.walk(tmp):
-                        objs += [relativize(os.path.join(root, f)) for f in files if os.path.splitext(f)[1] in [conf.OBJ_SUFFIX, '.i_o']]
+                        objs += [relativize(os.path.join(root, f)) for f in files if isObject(f)]
                     newlist += objs
                 else:
                     newlist += [arg]
             else:
                 newlist += [arg]
         return newlist
 
     def makelist(self):
         '''Replaces object file names with a temporary list file, using a
         list format depending on the EXPAND_LIBS_LIST_STYLE variable
         '''
-        objs = [o for o in self if os.path.splitext(o)[1] == conf.OBJ_SUFFIX]
+        objs = [o for o in self if isObject(o)]
         if not len(objs): return
         fd, tmp = tempfile.mkstemp(suffix=".list",dir=os.curdir)
         if conf.EXPAND_LIBS_LIST_STYLE == "linkerscript":
             content = ["INPUT(%s)\n" % obj for obj in objs]
             ref = tmp
         elif conf.EXPAND_LIBS_LIST_STYLE == "list":
             content = ["%s\n" % obj for obj in objs]
             ref = "@" + tmp
@@ -129,25 +129,25 @@ class ExpandArgsMore(ExpandArgs):
         newlist = self[0:idx] + [ref] + [item for item in self[idx:] if item not in objs]
         self[0:] = newlist
 
     def reorder(self, order_list):
         '''Given a list of file names without OBJ_SUFFIX, rearrange self
         so that the object file names it contains are ordered according to
         that list.
         '''
-        objs = [o for o in self if o.endswith(conf.OBJ_SUFFIX)]
+        objs = [o for o in self if isObject(o)]
         if not objs: return
         idx = self.index(objs[0])
         # Keep everything before the first object, then the ordered objects,
         # then any other objects, then any non-objects after the first object
         objnames = dict([(os.path.splitext(os.path.basename(o))[0], o) for o in objs])
         self[0:] = self[0:idx] + [objnames[o] for o in order_list if o in objnames] + \
                    [o for o in objs if os.path.splitext(os.path.basename(o))[0] not in order_list] + \
-                   [x for x in self[idx:] if not x.endswith(conf.OBJ_SUFFIX)]
+                   [x for x in self[idx:] if not isObject(x)]
 
 
 def main():
     parser = OptionParser()
     parser.add_option("--extract", action="store_true", dest="extract",
         help="when a library has no descriptor file, extract it first, when possible")
     parser.add_option("--uselist", action="store_true", dest="uselist",
         help="use a list file for objects when executing a command")
--- a/config/expandlibs_gen.py
+++ b/config/expandlibs_gen.py
@@ -36,22 +36,22 @@
 # ***** END LICENSE BLOCK *****
 
 '''Given a list of object files and library names, prints a library
 descriptor to standard output'''
 
 import sys
 import os
 import expandlibs_config as conf
-from expandlibs import LibDescriptor
+from expandlibs import LibDescriptor, isObject
 
 def generate(args):
     desc = LibDescriptor()
     for arg in args:
-        if os.path.splitext(arg)[1] in [conf.OBJ_SUFFIX, '.i_o']:
+        if isObject(arg):
             desc['OBJS'].append(os.path.abspath(arg))
         elif os.path.splitext(arg)[1] == conf.LIB_SUFFIX and \
              (os.path.exists(arg) or os.path.exists(arg + conf.LIBS_DESC_SUFFIX)):
             desc['LIBS'].append(os.path.abspath(arg))
     return desc
 
 if __name__ == '__main__':
     print generate(sys.argv[1:])
--- a/config/system-headers
+++ b/config/system-headers
@@ -263,16 +263,21 @@ frame/req.h
 freetype/freetype.h
 freetype/ftcache.h
 freetype/ftglyph.h
 freetype/ftsynth.h
 freetype/ftoutln.h
 freetype/ttnameid.h
 freetype/tttables.h
 freetype/t1tables.h
+freetype/ftlcdfil.h
+freetype/ftsizes.h
+freetype/ftadvanc.h
+freetype/ftbitmap.h
+freetype/ftxf86.h
 fribidi/fribidi.h
 FSp_fopen.h
 fstream
 fstream.h
 ft2build.h
 fts.h
 gconf/gconf-client.h
 Gdiplus.h
--- a/configure.in
+++ b/configure.in
@@ -124,17 +124,17 @@ GTK2_VERSION=2.10.0
 WINDRES_VERSION=2.14.90
 W32API_VERSION=3.14
 GNOMEVFS_VERSION=2.0
 GNOMEUI_VERSION=2.2.0
 GCONF_VERSION=1.2.1
 GIO_VERSION=2.18
 STARTUP_NOTIFICATION_VERSION=0.8
 DBUS_VERSION=0.60
-SQLITE_VERSION=3.7.7.1
+SQLITE_VERSION=3.7.10
 LIBNOTIFY_VERSION=0.4
 
 MSMANIFEST_TOOL=
 
 dnl Set various checks
 dnl ========================================================
 MISSING_X=
 AC_PROG_AWK
@@ -332,16 +332,17 @@ if test -n "$gonkdir" ; then
         HOST_CXXFLAGS=" "
     fi
     if test -z "$HOST_LDFLAGS" ; then
         HOST_LDFLAGS=" "
     fi
 
     AC_DEFINE(ANDROID)
     AC_DEFINE(HAVE_SYS_UIO_H)
+    AC_DEFINE(HAVE_PTHREADS)
     CROSS_COMPILE=1
     MOZ_CHROME_FILE_FORMAT=omni
     ZLIB_DIR=yes
     direct_nspr_config=1
 else
 case "$target" in
 *-android*|*-linuxandroid*)
     if test -z "$android_ndk" ; then
@@ -663,39 +664,30 @@ dnl Special win32 checks
 dnl ========================================================
 WINVER=502
 dnl Target the Windows 7 SDK by default
 WINSDK_TARGETVER=601
 
 MOZ_ARG_WITH_STRING(windows-version,
 [  --with-windows-version=WINSDK_TARGETVER
                           Highest Windows version to target using this SDK
-                              502: Windows Server 2003
-                              600: Windows Vista
                               601: Windows 7],
   WINSDK_TARGETVER=$withval)
 
 case "$WINSDK_TARGETVER" in
-502|600|601)
+601)
     MOZ_WINSDK_TARGETVER=0${WINSDK_TARGETVER}0000
     ;;
 
 *)
-    AC_MSG_ERROR([Invalid value for --with-windows-version ($WINSDK_TARGETVER), must be 502, 600 or 601]);
+    AC_MSG_ERROR([Invalid value for --with-windows-version ($WINSDK_TARGETVER), must be 601]);
     ;;
 
 esac
 
-if test -n "$COMPILE_ENVIRONMENT"; then
-if test "$MOZ_WINSDK_TARGETVER" -lt "06000000"; then
-    # We can't build parental controls either
-    MOZ_DISABLE_PARENTAL_CONTROLS=1
-fi
-fi
-
 case "$target" in
 *-mingw*)
     if test "$GCC" != "yes"; then
         # Check to see if we are really running in a msvc environemnt
         _WIN32_MSVC=1
         AC_CHECK_PROGS(MIDL, midl)
 
         # Make sure compilers are valid
@@ -846,25 +838,18 @@ case "$target" in
 
 WINSDK_MAXVER
 EOF
                             ac_cv_winsdk_maxver=`$CPP conftest.h 2>/dev/null | tail -n1`
                             rm -f conftest.h
                            ])
             MOZ_WINSDK_MAXVER=${ac_cv_winsdk_maxver}
         else
-            # The Vista SDK is the only one to have sdkddkver.h but not
-            # WinSDKVer.h
-            MOZ_CHECK_HEADERS([sdkddkver.h])
-            if test "$ac_cv_header_sdkddkver_h" = "yes"; then
-                MOZ_WINSDK_MAXVER=0x06000000
-            else
-                # Assume the Server 2003 Platform SDK
-                MOZ_WINSDK_MAXVER=0x05020000
-            fi
+            # Any SDK which doesn't have WinSDKVer.h is too old.
+            AC_MSG_ERROR([Your SDK does not have WinSDKVer.h. It is probably too old. Please upgrade to a newer SDK or try running the Windows SDK Configuration Tool and selecting a newer SDK. See https://developer.mozilla.org/En/Windows_SDK_versions for more details on fixing this.])
         fi
 
         unset _MSVC_VER_FILTER
 
         AC_CACHE_CHECK(for std::_Throw, ac_cv_have_std__Throw,
             [
                 AC_LANG_SAVE
                 AC_LANG_CPLUSPLUS
@@ -1819,19 +1804,33 @@ if test "$OS_TARGET" = "Android"; then
        AC_MSG_ERROR([Couldn't find path to stlport in the android ndk])
     fi
     CPPFLAGS="$CPPFLAGS $STLPORT_CPPFLAGS"
     LDFLAGS="$LDFLAGS $STLPORT_LDFLAGS"
     LIBS="$LIBS $STLPORT_LIBS"
 fi
 
 dnl ========================================================
+dnl Suppress Clang Argument Warnings
+dnl ========================================================
+if test -n "$CLANG_CC"; then
+    _WARNINGS_CFLAGS="-Qunused-arguments ${_WARNINGS_CFLAGS}"
+    CPPFLAGS="-Qunused-arguments ${CPPFLAGS}"
+fi
+if test -n "$CLANG_CXX"; then
+    _WARNINGS_CXXFLAGS="-Qunused-arguments ${_WARNINGS_CXXFLAGS}"
+fi
+
+dnl ========================================================
 dnl GNU specific defaults
 dnl ========================================================
 if test "$GNU_CC"; then
+    # Per bug 719659 comment 2, some of the headers on ancient build machines
+    # require gnu89 inline semantics.  But otherwise, we use C99.
+    CFLAGS="$CFLAGS -std=gnu99 -fgnu89-inline"
     # FIXME: Let us build with strict aliasing. bug 414641.
     CFLAGS="$CFLAGS -fno-strict-aliasing"
     MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@'
     MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@'
     DSO_LDOPTS='-shared'
     if test "$GCC_USE_GNU_LD"; then
         # Don't allow undefined symbols in libraries
         DSO_LDOPTS="$DSO_LDOPTS -Wl,-z,defs"
@@ -4644,24 +4643,17 @@ case "${target}" in
 *)
     ACCESSIBILITY=1
     ;;
 esac
 
 case "$target_os" in
     mingw*)
         NS_ENABLE_TSF=1
-        if test -z "$GNU_CC"; then
-            if test "$MOZ_WINSDK_TARGETVER" -lt "06000000"; then
-                NS_ENABLE_TSF=
-            fi
-        fi
-        if test -n "$NS_ENABLE_TSF"; then
-            AC_DEFINE(NS_ENABLE_TSF)
-        fi
+        AC_DEFINE(NS_ENABLE_TSF)
         ;;
 esac
 
 case "${target}" in
     *-android*|*-linuxandroid*)
         if test "$CPU_ARCH" = "arm" ; then
           USE_ARM_KUSER=1
         fi
@@ -7326,29 +7318,26 @@ MOZ_ARG_DISABLE_BOOL(gcincremental,
 if test -n "$JSGC_INCREMENTAL"; then
     AC_DEFINE(JSGC_INCREMENTAL)
 fi
 
 dnl ========================================================
 dnl ETW - Event Tracing for Windows
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(ETW,
-[  --enable-ETW            Enable ETW (Event Tracing for Windows) event reporting
-                          (needs Windows Vista+ SDK)],
+[  --enable-ETW            Enable ETW (Event Tracing for Windows) event reporting],
     MOZ_ETW=1,
     MOZ_ETW= )
 if test -n "$MOZ_ETW"; then
     AC_DEFINE(MOZ_ETW)
 fi
 
 if test -n "$MOZ_ETW"; then
     if test -z "$MOZ_WINSDK_TARGETVER"; then
         AC_MSG_ERROR([--enable-ETW is only valid on Windows])
-    elif test "$MOZ_WINSDK_TARGETVER" -lt "06000000"; then
-        AC_MSG_ERROR([--enable-ETW requires the Windows Vista SDK or newer])
     fi
 fi
 
 dnl ========================================================
 dnl Zealous JavaScript GC
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(gczeal,
 [  --enable-gczeal         Enable zealous JavaScript GCing],
@@ -8036,17 +8025,17 @@ fi
 AC_SUBST(GLIB_CFLAGS)
 AC_SUBST(GLIB_LIBS)
 AC_SUBST(GLIB_GMODULE_LIBS)
 
 dnl ========================================================
 dnl Graphics checks.
 dnl ========================================================
 
-if test "${OS_ARCH}" = "Darwin" -o "${MOZ_WIDGET_TOOLKIT}" = "android"; then
+if test "${OS_ARCH}" = "Darwin" -o "${MOZ_WIDGET_TOOLKIT}" = "android" -o "${MOZ_WIDGET_TOOLKIT}" = "gtk2"; then
 MOZ_ENABLE_SKIA=1
 else
 MOZ_ENABLE_SKIA=
 fi
 
 MOZ_ARG_ENABLE_BOOL(skia,
 [  --enable-skia   Enable use of Skia],
 MOZ_ENABLE_SKIA=1,
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -989,16 +989,18 @@ public:
    *
    * @param aNode The node for which to get the eventlistener manager.
    * @param aCreateIfNotFound If false, returns a listener manager only if
    *                          one already exists.
    */
   static nsEventListenerManager* GetListenerManager(nsINode* aNode,
                                                     bool aCreateIfNotFound);
 
+  static void UnmarkGrayJSListenersInCCGenerationDocuments(PRUint32 aGeneration);
+
   /**
    * Remove the eventlistener manager for aNode.
    *
    * @param aNode The node for which to remove the eventlistener manager.
    */
   static void RemoveListenerManager(nsINode *aNode);
 
   static bool IsInitialized()
@@ -1447,23 +1449,16 @@ public:
   /**
    * Increases the count of blockers preventing scripts from running.
    * NOTE: You might want to use nsAutoScriptBlocker rather than calling
    * this directly
    */
   static void AddScriptBlocker();
 
   /**
-   * Increases the count of blockers preventing scripts from running.
-   * Also, while this script blocker is active, script runners must not be
-   * added --- we'll assert if one is, and ignore it.
-   */
-  static void AddScriptBlockerAndPreventAddingRunners();
-
-  /**
    * Decreases the count of blockers preventing scripts from running.
    * NOTE: You might want to use nsAutoScriptBlocker rather than calling
    * this directly
    *
    * WARNING! Calling this function could synchronously execute scripts.
    */
   static void RemoveScriptBlocker();
 
@@ -2126,23 +2121,16 @@ public:
 #ifdef IS_BIG_ENDIAN
 #define DOUBLE_NaN {{DOUBLE_HI32_EXPMASK | DOUBLE_HI32_MANTMASK,   \
                         0xffffffff}}
 #else
 #define DOUBLE_NaN {{0xffffffff,                                         \
                         DOUBLE_HI32_EXPMASK | DOUBLE_HI32_MANTMASK}}
 #endif
 
-#if defined(XP_WIN)
-#define DOUBLE_COMPARE(LVAL, OP, RVAL)                                  \
-    (!DOUBLE_IS_NaN(LVAL) && !DOUBLE_IS_NaN(RVAL) && (LVAL) OP (RVAL))
-#else
-#define DOUBLE_COMPARE(LVAL, OP, RVAL) ((LVAL) OP (RVAL))
-#endif
-
 /*
  * In the following helper macros we exploit the fact that the result of a
  * series of additions will not be finite if any one of the operands in the
  * series is not finite.
  */
 #define NS_ENSURE_FINITE(f, rv)                                               \
   if (!NS_finite(f)) {                                                        \
     return (rv);                                                              \
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -1595,16 +1595,18 @@ public:
   enum DeprecatedOperations {
 #include "nsDeprecatedOperationList.h"
     eDeprecatedOperationCount
   };
 #undef DEPRECATED_OPERATION
   void WarnOnceAbout(DeprecatedOperations aOperation);
 
   virtual void PostVisibilityUpdateEvent() = 0;
+  
+  bool IsSyntheticDocument() { return mIsSyntheticDocument; }
 
   void SetNeedLayoutFlush() {
     mNeedLayoutFlush = true;
     if (mDisplayDocument) {
       mDisplayDocument->SetNeedLayoutFlush();
     }
   }
 
--- a/content/base/public/nsIFrameMessageManager.idl
+++ b/content/base/public/nsIFrameMessageManager.idl
@@ -59,36 +59,37 @@ interface nsIFrameMessageListener : nsIS
    * if the message is synchronous, possible return value is sent back
    * as JSON.
    *
    * When the listener is called, 'this' value is the target of the message.
    */
   void receiveMessage();
 };
 
-[scriptable, uuid(a27d8fcd-8de9-4a51-87f4-2b83bba901d5)]
+[scriptable, builtinclass, uuid(9be42627-a5db-456f-8de2-9097da45a8c3)]
 interface nsIFrameMessageManager : nsISupports
 {
   void addMessageListener(in AString aMessage, in nsIFrameMessageListener aListener);
   void removeMessageListener(in AString aMessage, in nsIFrameMessageListener aListener);
   [implicit_jscontext,optional_argc]
   void sendAsyncMessage([optional] in AString messageName, [optional] in jsval obj);
+  [notxpcom] boolean markForCC();
 };
 
-[scriptable, uuid(21e5d940-d457-4c0f-bb5e-35c159ed19e3)]
+[scriptable, builtinclass, uuid(28a36ac7-2868-4fa0-ae24-be957d7dce10)]
 interface nsISyncMessageSender : nsIFrameMessageManager
 {
   /**
    * Returns an array of JSON objects.
    */
   [implicit_jscontext,optional_argc]
   jsval sendSyncMessage([optional] in AString messageName, [optional] in jsval obj);
 };
 
-[scriptable, uuid(78a1d024-60e3-4b7b-98cd-4c6b84b4f060)]
+[scriptable, builtinclass, uuid(a83f4393-e3cf-44da-8867-1f9174c2c595)]
 interface nsIContentFrameMessageManager : nsISyncMessageSender
 {
   /**
    * The current top level window in the frame or null.
    */
   readonly attribute nsIDOMWindow content;
 
   /**
@@ -109,30 +110,30 @@ interface nsIContentFrameMessageManager 
 
    /**
     * Ascii base64 data to binary data and vice versa
     */
    DOMString atob(in DOMString aAsciiString);
    DOMString btoa(in DOMString aBase64Data);
 };
 
-[uuid(1f7af930-a232-4a84-a049-73eaa45f2db5)]
+[uuid(f0936c56-e92c-4927-a85b-e289c3d4a01c)]
 interface nsIInProcessContentFrameMessageManager : nsIContentFrameMessageManager
 {
   [notxpcom] nsIContent getOwnerContent();
 };
 
-[scriptable, uuid(e91b0939-a74a-4c4f-8cfd-17dd42e8642a)]
+[scriptable, builtinclass, uuid(09f79e8c-101b-432b-a494-02f9b5e111c0)]
 interface nsITreeItemFrameMessageManager : nsIFrameMessageManager
 {
   readonly attribute unsigned long childCount;
   nsITreeItemFrameMessageManager getChildAt(in unsigned long aIndex);
 };
 
-[scriptable, uuid(14e1f147-793d-4788-bbbb-ae806ecdddbb)]
+[scriptable, builtinclass, uuid(a51597f0-d669-4260-83e6-1d426a8ac802)]
 interface nsIChromeFrameMessageManager : nsITreeItemFrameMessageManager
 {
   /**
    * Load a script in the (remote) frame. aURL must be the absolute URL.
    * data: URLs are also supported. For example data:,dump("foo\n");
    * If aAllowDelayedLoad is true, script will be loaded when the
    * remote frame becomes available. Otherwise the script will be loaded
    * only if the frame is already available.
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -374,16 +374,21 @@ public:
 
   /**
    * Return this node as an Element.  Should only be used for nodes
    * for which IsElement() is true.
    */
   mozilla::dom::Element* AsElement();
 
   /**
+   * Return if this node has any children.
+   */
+  bool HasChildren() const { return !!mFirstChild; }
+
+  /**
    * Get the number of children
    * @return the number of children
    */
   virtual PRUint32 GetChildCount() const = 0;
 
   /**
    * Get a child by index
    * @param aIndex the index of the child to get
--- a/content/base/public/nsISelectionController.idl
+++ b/content/base/public/nsISelectionController.idl
@@ -1,11 +1,10 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * ***** BEGIN LICENSE BLOCK *****
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** 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,
@@ -42,21 +41,22 @@
 #include "nsISelection.idl"
 #include "nsISelectionDisplay.idl"
 
 %{C++
 typedef short SelectionType;
 typedef short SelectionRegion;
 %}
 
+interface nsIContent;
 interface nsIDOMNode;
 interface nsISelection;
 interface nsISelectionDisplay;
 
-[scriptable, uuid(cf30315f-b65d-44c3-8c57-557e36d18fd2)]
+[scriptable, uuid(b1ff7faa-8097-431d-b7f1-b0615e3cd596)]
 interface nsISelectionController : nsISelectionDisplay
 {
    const short SELECTION_NONE=0;
    const short SELECTION_NORMAL=1;
    const short SELECTION_SPELLCHECK=2;
    const short SELECTION_IME_RAWINPUT=4;
    const short SELECTION_IME_SELECTEDRAWTEXT=8;
    const short SELECTION_IME_CONVERTEDTEXT=16;
@@ -271,15 +271,16 @@ interface nsISelectionController : nsISe
   /** CheckVisibility will return true if textnode and offsets are actually rendered 
    *  in the current precontext.
    *  @param aNode textNode to test
    *  @param aStartOffset  offset in dom to first char of textnode to test
    *  @param aEndOffset    offset in dom to last char of textnode to test
    *  @param aReturnBool   boolean returned TRUE if visible FALSE if not
    */
     boolean checkVisibility(in nsIDOMNode node, in short startOffset, in short endOffset);
+    [noscript,nostdcall] boolean checkVisibilityContent(in nsIContent node, in short startOffset, in short endOffset);
 
 };
 %{ C++
    #define NS_ISELECTIONCONTROLLER_CID \
    { 0x513b9460, 0xd56a, 0x4c4e, \
    { 0xb6, 0xf9, 0x0b, 0x8a, 0xe4, 0x37, 0x2a, 0x3b }}
 %}
--- a/content/base/public/nsIXMLHttpRequest.idl
+++ b/content/base/public/nsIXMLHttpRequest.idl
@@ -48,28 +48,29 @@ interface nsPIDOMWindow;
 interface nsIInputStream;
 interface nsIDOMBlob;
 
 %{C++
 // for jsval
 #include "jsapi.h"
 %}
 
-[scriptable, builtinclass, uuid(e2b59e48-3655-4429-a94c-b4332c346ba2)]
+[scriptable, builtinclass, uuid(5e346bf8-7083-4ef8-b9b9-736a1b5aa7ab)]
 interface nsIXMLHttpRequestEventTarget : nsIDOMEventTarget {
   // event handler attributes
   attribute nsIDOMEventListener onabort;
   attribute nsIDOMEventListener onerror;
   attribute nsIDOMEventListener onload;
   attribute nsIDOMEventListener onloadstart;
   attribute nsIDOMEventListener onprogress;
+  attribute nsIDOMEventListener ontimeout;
   attribute nsIDOMEventListener onloadend;
 };
 
-[scriptable, builtinclass, uuid(db9357fc-edf7-42b2-aab2-c24ab19ece20)]
+[scriptable, builtinclass, uuid(8dbd2448-740a-412c-b314-434f24a1c510)]
 interface nsIXMLHttpRequestUpload : nsIXMLHttpRequestEventTarget {
   // for future use
 };
 
 /**
  * Mozilla's XMLHttpRequest is modelled after Microsoft's IXMLHttpRequest
  * object. The goal has been to make Mozilla's version match Microsoft's
  * version as closely as possible, but there are bound to be some differences.
@@ -105,17 +106,17 @@ interface nsIXMLHttpRequestUpload : nsIX
  *   The 'onload', 'onerror', and 'onreadystatechange' attributes moved to
  *   nsIJSXMLHttpRequest, but if you're coding in C++ you should avoid using
  *   those.
  *
  * Conclusion: Do not use event listeners on XMLHttpRequest from C++, unless
  * you're aware of all the security implications.  And then think twice about
  * it.
  */
-[scriptable, uuid(5cf8d518-51d0-4cd6-a69a-c3674c2de599)]
+[scriptable, uuid(88ffc45a-22e2-44f4-9a6e-f4586fbde376)]
 interface nsIXMLHttpRequest : nsISupports
 {
   /**
    * The request uses a channel in order to perform the
    * request.  This attribute represents the channel used
    * for the request.  NULL if the channel has not yet been
    * created.
    *
@@ -274,16 +275,22 @@ interface nsIXMLHttpRequest : nsISupport
    * before setting the request headers.
    *
    * @param header The name of the header to set in the request.
    * @param value The body of the header.
    */
   void   setRequestHeader(in AUTF8String header, in AUTF8String value);
 
   /**
+   * The amount of milliseconds a request can take before being terminated.
+   * Initially zero. Zero means there is no timeout.
+   */
+  attribute unsigned long timeout;
+
+  /**
    * The state of the request.
    *
    * Possible values:
    *   0 UNSENT   open() has not been called yet.
    *   1 OPENED   send() has not been called yet.
    *   2 HEADERS_RECEIVED
    *              send() has been called, headers and status are available.
    *   3 LOADING  Downloading, responseText holds the partial data.
--- a/content/base/src/Link.h
+++ b/content/base/src/Link.h
@@ -46,18 +46,18 @@
 
 #include "mozilla/dom/Element.h"
 #include "mozilla/IHistory.h"
 
 namespace mozilla {
 namespace dom {
 
 #define MOZILLA_DOM_LINK_IMPLEMENTATION_IID \
-  { 0xa687a99c, 0x3893, 0x45c0, \
-    {0x8e, 0xab, 0xb8, 0xf7, 0xd7, 0x9e, 0x9e, 0x7b } }
+  { 0x7EA57721, 0xE373, 0x458E, \
+    {0x8F, 0x44, 0xF8, 0x96, 0x56, 0xB4, 0x14, 0xF5 } }
 
 class Link : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_LINK_IMPLEMENTATION_IID)
 
   static const nsLinkState defaultState = eLinkState_Unknown;
 
@@ -108,16 +108,34 @@ public:
    *        true if ResetLinkState should notify the owning document about style
    *        changes or false if it should not.
    */
   void ResetLinkState(bool aNotify);
   
   // This method nevers returns a null element.
   Element* GetElement() const { return mElement; }
 
+  /**
+   * DNS prefetch has been deferred until later, e.g. page load complete.
+   */
+  virtual void OnDNSPrefetchDeferred() { /*do nothing*/ }
+  
+  /**
+   * DNS prefetch has been submitted to Host Resolver.
+   */
+  virtual void OnDNSPrefetchRequested() { /*do nothing*/ }
+
+  /**
+   * Checks if DNS Prefetching is ok
+   * 
+   * @returns boolean
+   *          Defaults to true; should be overridden for specialised cases
+   */
+  virtual bool HasDeferredDNSPrefetchRequest() { return true; }
+
 protected:
   virtual ~Link();
 
   bool HasCachedURI() const { return !!mCachedURI; }
 
 private:
   /**
    * Unregisters from History so this node no longer gets notifications about
--- a/content/base/src/mozSanitizingSerializer.cpp
+++ b/content/base/src/mozSanitizingSerializer.cpp
@@ -312,22 +312,16 @@ mozSanitizingHTMLSerializer::AddLeaf(con
   eHTMLTags type = (eHTMLTags)aNode.GetNodeType();
   const nsAString& text = aNode.GetText();
 
   mParserNode = const_cast<nsIParserNode*>(&aNode);
   return DoAddLeaf(type, text);
 }
 
 NS_IMETHODIMP 
-mozSanitizingHTMLSerializer::AddDocTypeDecl(const nsIParserNode& aNode)
-{
-  return NS_OK;
-}
-
-NS_IMETHODIMP 
 mozSanitizingHTMLSerializer::SetDocumentCharset(nsACString& aCharset)
 {
   // No idea, if this works - it isn't invoked by |TestOutput|.
   Write(NS_LITERAL_STRING("\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=")
         /* Danger: breaking the line within the string literal, like
            "foo"\n"bar", breaks win32! */
         + nsAdoptingString(escape(NS_ConvertASCIItoUTF16(aCharset)))
         + NS_LITERAL_STRING("\">\n"));
--- a/content/base/src/mozSanitizingSerializer.h
+++ b/content/base/src/mozSanitizingSerializer.h
@@ -102,29 +102,24 @@ public:
   // nsIContentSink
   NS_IMETHOD WillParse(void) { return NS_OK; }
   NS_IMETHOD WillInterrupt(void) { return NS_OK; }
   NS_IMETHOD WillResume(void) { return NS_OK; }
   NS_IMETHOD SetParser(nsParserBase* aParser) { return NS_OK; }
   NS_IMETHOD OpenContainer(const nsIParserNode& aNode);
   NS_IMETHOD CloseContainer(const nsHTMLTag aTag);
   NS_IMETHOD AddLeaf(const nsIParserNode& aNode);
-  NS_IMETHOD AddComment(const nsIParserNode& aNode) { return NS_OK; }
-  NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode)
-                                                    { return NS_OK; }
-  NS_IMETHOD AddDocTypeDecl(const nsIParserNode& aNode);
   virtual void FlushPendingNotifications(mozFlushType aType) { }
   NS_IMETHOD SetDocumentCharset(nsACString& aCharset);
   virtual nsISupports *GetTarget() { return nsnull; }
 
   // nsIHTMLContentSink
   NS_IMETHOD OpenHead();
   NS_IMETHOD IsEnabled(PRInt32 aTag, bool* aReturn);
   NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode) { return NS_OK; }
-  NS_IMETHOD_(bool) IsFormOnStack() { return false; }
   NS_IMETHOD BeginContext(PRInt32 aPosition) { return NS_OK; }
   NS_IMETHOD EndContext(PRInt32 aPosition) { return NS_OK; }
   NS_IMETHOD DidProcessTokens(void) { return NS_OK; }
   NS_IMETHOD WillProcessAToken(void) { return NS_OK; }
   NS_IMETHOD DidProcessAToken(void) { return NS_OK; }
 
   // nsISanitizingHTMLSerializer
   NS_IMETHOD Initialize(nsAString* aOutString,
--- a/content/base/src/nsCCUncollectableMarker.h
+++ b/content/base/src/nsCCUncollectableMarker.h
@@ -46,21 +46,25 @@ class nsCCUncollectableMarker : public n
   /**
    * Inits a global nsCCUncollectableMarker. Should only be called once.
    */
   static nsresult Init();
 
   /**
    * Checks if we're collecting during a given generation
    */
+  static bool InGeneration(PRUint32 aGeneration)
+  {
+    return aGeneration && aGeneration == sGeneration;
+  }
+
   static bool InGeneration(nsCycleCollectionTraversalCallback &cb,
-                             PRUint32 aGeneration) {
-    return !cb.WantAllTraces() &&
-           aGeneration &&
-           aGeneration == sGeneration;
+                           PRUint32 aGeneration)
+  {
+    return InGeneration(aGeneration) && !cb.WantAllTraces();
   }
 
   static PRUint32 sGeneration;
 
 private:
   nsCCUncollectableMarker() {}
 
 };
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -201,17 +201,17 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_
 #include "nsContentDLF.h"
 #ifdef MOZ_MEDIA
 #include "nsHTMLMediaElement.h"
 #endif
 #include "nsDOMTouchEvent.h"
 #include "nsIScriptElement.h"
 #include "nsIContentViewer.h"
 #include "nsIObjectLoadingContent.h"
-
+#include "nsCCUncollectableMarker.h"
 #include "mozilla/Base64.h"
 #include "mozilla/Preferences.h"
 
 #include "nsWrapperCacheInlines.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsUnicharUtils.h"
 
 using namespace mozilla::dom;
@@ -255,17 +255,16 @@ PRUint32 nsContentUtils::sJSGCThingRootC
 nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nsnull;
 #endif
 PRUint32 nsContentUtils::sScriptBlockerCount = 0;
 #ifdef DEBUG
 PRUint32 nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
 #endif
 nsTArray< nsCOMPtr<nsIRunnable> >* nsContentUtils::sBlockedScriptRunners = nsnull;
 PRUint32 nsContentUtils::sRunnersCountAtFirstBlocker = 0;
-PRUint32 nsContentUtils::sScriptBlockerCountWhereRunnersPrevented = 0;
 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nsnull;
 
 bool nsContentUtils::sIsHandlingKeyBoardEvent = false;
 bool nsContentUtils::sAllowXULXBL_for_file = false;
 
 nsString* nsContentUtils::sShiftText = nsnull;
 nsString* nsContentUtils::sControlText = nsnull;
 nsString* nsContentUtils::sMetaText = nsnull;
@@ -3386,16 +3385,42 @@ nsContentUtils::MaybeFireNodeRemoved(nsI
     nsMutationEvent mutation(true, NS_MUTATION_NODEREMOVED);
     mutation.mRelatedNode = do_QueryInterface(aParent);
 
     mozAutoSubtreeModified subtree(aOwnerDoc, aParent);
     nsEventDispatcher::Dispatch(aChild, nsnull, &mutation);
   }
 }
 
+PLDHashOperator
+ListenerEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aEntry,
+                   PRUint32 aNumber, void* aArg)
+{
+  PRUint32* gen = static_cast<PRUint32*>(aArg);
+  EventListenerManagerMapEntry* entry =
+    static_cast<EventListenerManagerMapEntry*>(aEntry);
+  if (entry) {
+    nsINode* n = static_cast<nsINode*>(entry->mListenerManager->GetTarget());
+    if (n && n->IsInDoc() &&
+        nsCCUncollectableMarker::InGeneration(n->OwnerDoc()->GetMarkedCCGeneration())) {
+      entry->mListenerManager->UnmarkGrayJSListeners();
+    }
+  }
+  return PL_DHASH_NEXT;
+}
+
+void
+nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments(PRUint32 aGeneration)
+{
+  if (sEventListenerManagersHash.ops) {
+    PL_DHashTableEnumerate(&sEventListenerManagersHash, ListenerEnumerator,
+                           &aGeneration);
+  }
+}
+
 /* static */
 void
 nsContentUtils::TraverseListenerManager(nsINode *aNode,
                                         nsCycleCollectionTraversalCallback &cb)
 {
   if (!sEventListenerManagersHash.ops) {
     // We're already shut down, just return.
     return;
@@ -4414,33 +4439,20 @@ nsContentUtils::AddScriptBlocker()
                  "Should not already have a count");
     sRunnersCountAtFirstBlocker = sBlockedScriptRunners->Length();
   }
   ++sScriptBlockerCount;
 }
 
 /* static */
 void
-nsContentUtils::AddScriptBlockerAndPreventAddingRunners()
-{
-  AddScriptBlocker();
-  if (sScriptBlockerCountWhereRunnersPrevented == 0) {
-    sScriptBlockerCountWhereRunnersPrevented = sScriptBlockerCount;
-  }
-}
-
-/* static */
-void
 nsContentUtils::RemoveScriptBlocker()
 {
   NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
   --sScriptBlockerCount;
-  if (sScriptBlockerCount < sScriptBlockerCountWhereRunnersPrevented) {
-    sScriptBlockerCountWhereRunnersPrevented = 0;
-  }
   if (sScriptBlockerCount) {
     return;
   }
 
   PRUint32 firstBlocker = sRunnersCountAtFirstBlocker;
   PRUint32 lastBlocker = sBlockedScriptRunners->Length();
   PRUint32 originalFirstBlocker = firstBlocker;
   PRUint32 blockersCount = lastBlocker - firstBlocker;
@@ -4464,20 +4476,16 @@ nsContentUtils::RemoveScriptBlocker()
 bool
 nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable)
 {
   if (!aRunnable) {
     return false;
   }
 
   if (sScriptBlockerCount) {
-    if (sScriptBlockerCountWhereRunnersPrevented > 0) {
-      NS_ERROR("Adding a script runner when that is prevented!");
-      return false;
-    }
     return sBlockedScriptRunners->AppendElement(aRunnable) != nsnull;
   }
   
   nsCOMPtr<nsIRunnable> run = aRunnable;
   run->Run();
 
   return true;
 }
@@ -5449,18 +5457,19 @@ public:
   {
   }
   NS_IMETHOD_(void) NoteRoot(PRUint32 langID, void* root,
                              nsCycleCollectionParticipant* helper)
   {
   }
   NS_IMETHOD_(void) NoteScriptChild(PRUint32 langID, void* child)
   {
-    if (langID == nsIProgrammingLanguage::JAVASCRIPT) {
-      mFound = child == mWrapper;
+    if (langID == nsIProgrammingLanguage::JAVASCRIPT &&
+        child == mWrapper) {
+      mFound = true;
     }
   }
   NS_IMETHOD_(void) NoteXPCOMChild(nsISupports *child)
   {
   }
   NS_IMETHOD_(void) NoteNativeChild(void* child,
                                     nsCycleCollectionParticipant* helper)
   {
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -68,16 +68,17 @@
 #include "mozilla/css/Loader.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIScriptRuntime.h"
 #include "nsCOMArray.h"
 
 #include "nsGUIEvent.h"
 #include "nsAsyncDOMEvent.h"
+#include "nsIDOMNodeFilter.h"
 
 #include "nsIDOMStyleSheet.h"
 #include "nsDOMAttribute.h"
 #include "nsIDOMDOMStringList.h"
 #include "nsIDOMDOMImplementation.h"
 #include "nsIDOMDocumentXBL.h"
 #include "mozilla/FunctionTimer.h"
 #include "nsGenericElement.h"
@@ -5019,67 +5020,73 @@ nsDocument::CreateRange(nsIDOMRange** aR
   range.forget(aReturn);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocument::CreateNodeIterator(nsIDOMNode *aRoot,
                                PRUint32 aWhatToShow,
                                nsIDOMNodeFilter *aFilter,
-                               bool aEntityReferenceExpansion,
+                               PRUint8 aOptionalArgc,
                                nsIDOMNodeIterator **_retval)
 {
   *_retval = nsnull;
 
+  if (!aOptionalArgc) {
+    aWhatToShow = nsIDOMNodeFilter::SHOW_ALL;
+  }
+
   if (!aRoot)
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
 
   nsresult rv = nsContentUtils::CheckSameOrigin(this, aRoot);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ENSURE_ARG_POINTER(_retval);
 
   nsCOMPtr<nsINode> root = do_QueryInterface(aRoot);
   NS_ENSURE_TRUE(root, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 
   nsNodeIterator *iterator = new nsNodeIterator(root,
                                                 aWhatToShow,
-                                                aFilter,
-                                                aEntityReferenceExpansion);
+                                                aFilter);
   NS_ENSURE_TRUE(iterator, NS_ERROR_OUT_OF_MEMORY);
 
   NS_ADDREF(*_retval = iterator);
 
-  return NS_OK; 
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocument::CreateTreeWalker(nsIDOMNode *aRoot,
                              PRUint32 aWhatToShow,
                              nsIDOMNodeFilter *aFilter,
-                             bool aEntityReferenceExpansion,
+                             PRUint8 aOptionalArgc,
                              nsIDOMTreeWalker **_retval)
 {
   *_retval = nsnull;
 
+  if (!aOptionalArgc) {
+    aWhatToShow = nsIDOMNodeFilter::SHOW_ALL;
+  }
+
   if (!aRoot)
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
 
   nsresult rv = nsContentUtils::CheckSameOrigin(this, aRoot);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ENSURE_ARG_POINTER(_retval);
 
   nsCOMPtr<nsINode> root = do_QueryInterface(aRoot);
   NS_ENSURE_TRUE(root, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 
   nsTreeWalker* walker = new nsTreeWalker(root,
                                           aWhatToShow,
-                                          aFilter,
-                                          aEntityReferenceExpansion);
+                                          aFilter);
   NS_ENSURE_TRUE(walker, NS_ERROR_OUT_OF_MEMORY);
 
   NS_ADDREF(*_retval = walker);
 
   return NS_OK;
 }
 
 
@@ -7092,18 +7099,26 @@ nsDocument::BlockOnload()
   // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
   // -- it's not ours.
   if (mOnloadBlockCount == 0 && mScriptGlobalObject) {
     if (!nsContentUtils::IsSafeToRunScript()) {
       // Because AddRequest may lead to OnStateChange calls in chrome,
       // block onload only when there are no script blockers.
       ++mAsyncOnloadBlockCount;
       if (mAsyncOnloadBlockCount == 1) {
-        nsContentUtils::AddScriptRunner(
+        bool success = nsContentUtils::AddScriptRunner(
           NS_NewRunnableMethod(this, &nsDocument::AsyncBlockOnload));
+
+        // The script runner shouldn't fail to add. But if somebody broke
+        // something and it does, we'll thrash at 100% cpu forever. The best
+        // response is just to ignore the onload blocking request. See bug 579535.
+        if (!success) {
+          NS_WARNING("Disaster! Onload blocking script runner failed to add - expect bad things!");
+          mAsyncOnloadBlockCount = 0;
+        }
       }
       return;
     }
     nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
     if (loadGroup) {
       loadGroup->AddRequest(mOnloadBlocker, nsnull);
     }
   }
--- a/content/base/src/nsEventSource.cpp
+++ b/content/base/src/nsEventSource.cpp
@@ -58,16 +58,17 @@
 #include "nsIScriptError.h"
 #include "nsICharsetConverterManager.h"
 #include "nsIChannelPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsContentUtils.h"
 #include "mozilla/Preferences.h"
 #include "xpcpublic.h"
 #include "nsCrossSiteListenerProxy.h"
+#include "nsWrapperCacheInlines.h"
 
 using namespace mozilla;
 
 #define REPLACEMENT_CHAR     (PRUnichar)0xFFFD
 #define BOM_CHAR             (PRUnichar)0xFEFF
 #define SPACE_CHAR           (PRUnichar)0x0020
 #define CR_CHAR              (PRUnichar)0x000D
 #define LF_CHAR              (PRUnichar)0x000A
@@ -100,16 +101,40 @@ nsEventSource::~nsEventSource()
 }
 
 //-----------------------------------------------------------------------------
 // nsEventSource::nsISupports
 //-----------------------------------------------------------------------------
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsEventSource)
 
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsEventSource)
+  if (tmp->IsBlack()) {
+    if (tmp->mListenerManager) {
+      tmp->mListenerManager->UnmarkGrayJSListeners();
+      NS_UNMARK_LISTENER_WRAPPER(Open)
+      NS_UNMARK_LISTENER_WRAPPER(Message)
+      NS_UNMARK_LISTENER_WRAPPER(Error)
+    }
+    return true;
+  }
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsEventSource)
+  return tmp->IsBlack();
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsEventSource)
+  return tmp->IsBlack();
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsEventSource,
+                                               nsDOMEventTargetWrapperCache)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsEventSource,
                                                   nsDOMEventTargetWrapperCache)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSrc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mNotificationCallbacks)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLoadGroup)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannelEventSink)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mHttpChannel)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTimer)
--- a/content/base/src/nsEventSource.h
+++ b/content/base/src/nsEventSource.h
@@ -79,18 +79,18 @@ class nsEventSource: public nsDOMEventTa
                      public nsSupportsWeakReference
 {
 friend class AsyncVerifyRedirectCallbackFwr;
 
 public:
   nsEventSource();
   virtual ~nsEventSource();
   NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsEventSource,
-                                           nsDOMEventTargetWrapperCache)
+  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_INHERITED(nsEventSource,
+                                                                   nsDOMEventTargetWrapperCache)
 
   NS_DECL_NSIEVENTSOURCE
 
   // nsIJSNativeInitializer
   NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
                         PRUint32 argc, jsval* argv);
 
   NS_DECL_NSIOBSERVER
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -1105,8 +1105,20 @@ NS_NewChildProcessMessageManager(nsISync
                                                         nsnull,
                                                         nsnull,
                                                         false,
                                                         true);
   NS_ENSURE_TRUE(mm, NS_ERROR_OUT_OF_MEMORY);
   nsFrameMessageManager::sChildProcessManager = mm;
   return CallQueryInterface(mm, aResult);
 }
+
+bool
+nsFrameMessageManager::MarkForCC()
+{
+  PRUint32 len = mListeners.Length();
+  for (PRUint32 i = 0; i < len; ++i) {
+    nsCOMPtr<nsIXPConnectWrappedJS> wjs =
+      do_QueryInterface(mListeners[i].mListener);
+    xpc_UnmarkGrayObject(wjs);
+  }
+  return true;
+}
--- a/content/base/src/nsFrameMessageManager.h
+++ b/content/base/src/nsFrameMessageManager.h
@@ -151,16 +151,17 @@ public:
                        bool aLoadScripts = true);
   void RemoveChildManager(nsFrameMessageManager* aManager)
   {
     mChildManagers.RemoveObject(aManager);
   }
 
   void Disconnect(bool aRemoveFromParent = true);
   void SetCallbackData(void* aData, bool aLoadScripts = true);
+  void* GetCallbackData() { return mCallbackData; }
   void GetParamsForMessage(const jsval& aObject,
                            JSContext* aCx,
                            nsAString& aJSON);
   nsresult SendAsyncMessageInternal(const nsAString& aMessage,
                                     const nsAString& aJSON);
   JSContext* GetJSContext() { return mContext; }
   void SetJSContext(JSContext* aCx) { mContext = aCx; }
   void RemoveFromParent();
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -55,16 +55,17 @@
 #include "nsContentPolicyUtils.h"
 #include "nsIURI.h"
 #include "nsILoadGroup.h"
 #include "imgIContainer.h"
 #include "imgILoader.h"
 #include "nsThreadUtils.h"
 #include "nsNetUtil.h"
 #include "nsAsyncDOMEvent.h"
+#include "nsGenericHTMLElement.h"
 
 #include "nsIPresShell.h"
 #include "nsEventStates.h"
 #include "nsGUIEvent.h"
 
 #include "nsIChannel.h"
 #include "nsIStreamListener.h"
 
@@ -769,19 +770,19 @@ nsImageLoadingContent::LoadImage(nsIURI*
   if (!NS_CP_ACCEPTED(cpDecision)) {
     FireEvent(NS_LITERAL_STRING("error"));
     SetBlockedRequest(aNewURI, cpDecision);
     return NS_OK;
   }
 
   nsLoadFlags loadFlags = aLoadFlags;
   PRInt32 corsmode = GetCORSMode();
-  if (corsmode == nsImageLoadingContent::CORS_ANONYMOUS) {
+  if (corsmode == nsGenericHTMLElement::CORS_ANONYMOUS) {
     loadFlags |= imgILoader::LOAD_CORS_ANONYMOUS;
-  } else if (corsmode == nsImageLoadingContent::CORS_USE_CREDENTIALS) {
+  } else if (corsmode == nsGenericHTMLElement::CORS_USE_CREDENTIALS) {
     loadFlags |= imgILoader::LOAD_CORS_USE_CREDENTIALS;
   }
 
   // Not blocked. Do the load.
   nsCOMPtr<imgIRequest>& req = PrepareNextRequest();
   nsresult rv;
   rv = nsContentUtils::LoadImage(aNewURI, aDocument,
                                  aDocument->NodePrincipal(),
@@ -1181,13 +1182,13 @@ nsImageLoadingContent::CreateStaticImage
   aDest->mStateChangerDepth = mStateChangerDepth;
   aDest->mIsImageStateForced = mIsImageStateForced;
   aDest->mLoading = mLoading;
   aDest->mBroken = mBroken;
   aDest->mUserDisabled = mUserDisabled;
   aDest->mSuppressed = mSuppressed;
 }
 
-nsImageLoadingContent::CORSMode
+nsGenericHTMLElement::CORSMode
 nsImageLoadingContent::GetCORSMode()
 {
-  return CORS_NONE;
+  return nsGenericHTMLElement::CORS_NONE;
 }
--- a/content/base/src/nsImageLoadingContent.h
+++ b/content/base/src/nsImageLoadingContent.h
@@ -48,16 +48,17 @@
 #include "nsIImageLoadingContent.h"
 #include "nsINode.h"
 #include "imgIRequest.h"
 #include "prtypes.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h" // NS_CONTENT_DELETE_LIST_MEMBER
 #include "nsString.h"
 #include "nsEventStates.h"
+#include "nsGenericHTMLElement.h"
 
 class nsIURI;
 class nsIDocument;
 class imgILoader;
 class nsIIOService;
 
 class nsImageLoadingContent : public nsIImageLoadingContent
 {
@@ -65,35 +66,16 @@ class nsImageLoadingContent : public nsI
 public:
   nsImageLoadingContent();
   virtual ~nsImageLoadingContent();
 
   NS_DECL_IMGICONTAINEROBSERVER
   NS_DECL_IMGIDECODEROBSERVER
   NS_DECL_NSIIMAGELOADINGCONTENT
 
-  enum CORSMode {
-    /**
-     * The default of not using CORS to validate cross-origin loads.
-     */
-    CORS_NONE,
-
-    /**
-     * Validate cross-site loads using CORS, but do not send any credentials
-     * (cookies, HTTP auth logins, etc) along with the request.
-     */
-    CORS_ANONYMOUS,
-
-    /**
-     * Validate cross-site loads using CORS, and send credentials such as cookies
-     * and HTTP auth logins along with the request.
-     */
-    CORS_USE_CREDENTIALS
-  };
-
 protected:
   /**
    * LoadImage is called by subclasses when the appropriate
    * attributes (eg 'src' for <img> tags) change.  The string passed
    * in is the new uri string; this consolidates the code for getting
    * the charset, constructing URI objects, and any other incidentals
    * into this superclass.   
    *
@@ -196,17 +178,17 @@ protected:
   // Sets blocking state only if the desired state is different from the
   // current one. See the comment for mBlockingOnload for more information.
   void SetBlockingOnload(bool aBlocking);
 
   /**
    * Returns the CORS mode that will be used for all future image loads. The
    * default implementation returns CORS_NONE unconditionally.
    */
-  virtual CORSMode GetCORSMode();
+  virtual nsGenericHTMLElement::CORSMode GetCORSMode();
 
 private:
   /**
    * Struct used to manage the image observers.
    */
   struct ImageObserver {
     ImageObserver(imgIDecoderObserver* aObserver) :
       mObserver(aObserver),
--- a/content/base/src/nsNodeIterator.cpp
+++ b/content/base/src/nsNodeIterator.cpp
@@ -165,19 +165,18 @@ void nsNodeIterator::NodePointer::MoveBa
 }
 
 /*
  * Factories, constructors and destructors
  */
 
 nsNodeIterator::nsNodeIterator(nsINode *aRoot,
                                PRUint32 aWhatToShow,
-                               nsIDOMNodeFilter *aFilter,
-                               bool aExpandEntityReferences) :
-    nsTraversal(aRoot, aWhatToShow, aFilter, aExpandEntityReferences),
+                               nsIDOMNodeFilter *aFilter) :
+    nsTraversal(aRoot, aWhatToShow, aFilter),
     mDetached(false),
     mPointer(mRoot, true)
 {
     aRoot->AddMutationObserver(this);
 }
 
 nsNodeIterator::~nsNodeIterator()
 {
@@ -242,17 +241,17 @@ NS_IMETHODIMP nsNodeIterator::GetFilter(
     NS_IF_ADDREF(*aFilter = mFilter);
 
     return NS_OK;
 }
 
 /* readonly attribute boolean expandEntityReferences; */
 NS_IMETHODIMP nsNodeIterator::GetExpandEntityReferences(bool *aExpandEntityReferences)
 {
-    *aExpandEntityReferences = mExpandEntityReferences;
+    *aExpandEntityReferences = false;
     return NS_OK;
 }
 
 /* nsIDOMNode nextNode ()  raises (DOMException); */
 NS_IMETHODIMP nsNodeIterator::NextNode(nsIDOMNode **_retval)
 {
     return NextOrPrevNode(&NodePointer::MoveToNext, _retval);
 }
--- a/content/base/src/nsNodeIterator.h
+++ b/content/base/src/nsNodeIterator.h
@@ -58,18 +58,17 @@ class nsNodeIterator : public nsIDOMNode
                        public nsStubMutationObserver2
 {
 public:
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     NS_DECL_NSIDOMNODEITERATOR
 
     nsNodeIterator(nsINode *aRoot,
                    PRUint32 aWhatToShow,
-                   nsIDOMNodeFilter *aFilter,
-                   bool aExpandEntityReferences);
+                   nsIDOMNodeFilter *aFilter);
     virtual ~nsNodeIterator();
 
     NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
     NS_DECL_NSIMUTATIONOBSERVER2_ATTRIBUTECHILDREMOVED
 
     NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsNodeIterator, nsIDOMNodeIterator)
 
 private:
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -92,16 +92,17 @@
 #include "nsFrameLoader.h"
 
 #include "nsObjectLoadingContent.h"
 #include "mozAutoDocUpdate.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIChannelPolicy.h"
 #include "nsChannelPolicy.h"
 #include "mozilla/dom/Element.h"
+#include "sampler.h"
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gObjectLog = PR_NewLogModule("objlc");
 #endif
 
 #define LOG(args) PR_LOG(gObjectLog, PR_LOG_DEBUG, args)
 #define LOG_ENABLED() PR_LOG_TEST(gObjectLog, PR_LOG_DEBUG)
 
@@ -504,16 +505,17 @@ nsObjectLoadingContent::~nsObjectLoading
   }
 }
 
 // nsIRequestObserver
 NS_IMETHODIMP
 nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest,
                                        nsISupports *aContext)
 {
+  SAMPLE_LABEL("nsObjectLoadingContent", "OnStartRequest");
   if (aRequest != mChannel || !aRequest) {
     // This is a bit of an edge case - happens when a new load starts before the
     // previous one got here
     return NS_BINDING_ABORTED;
   }
 
   AutoNotifier notifier(this, true);
 
--- a/content/base/src/nsPlainTextSerializer.h
+++ b/content/base/src/nsPlainTextSerializer.h
@@ -103,28 +103,24 @@ public:
   // nsIContentSink
   NS_IMETHOD WillParse(void) { return NS_OK; }
   NS_IMETHOD WillInterrupt(void) { return NS_OK; }
   NS_IMETHOD WillResume(void) { return NS_OK; }
   NS_IMETHOD SetParser(nsParserBase* aParser) { return NS_OK; }
   NS_IMETHOD OpenContainer(const nsIParserNode& aNode);
   NS_IMETHOD CloseContainer(const nsHTMLTag aTag);
   NS_IMETHOD AddLeaf(const nsIParserNode& aNode);
-  NS_IMETHOD AddComment(const nsIParserNode& aNode) { return NS_OK; }
-  NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode) { return NS_OK; }
-  NS_IMETHOD AddDocTypeDecl(const nsIParserNode& aNode) { return NS_OK; }
   virtual void FlushPendingNotifications(mozFlushType aType) { }
   NS_IMETHOD SetDocumentCharset(nsACString& aCharset) { return NS_OK; }
   virtual nsISupports *GetTarget() { return nsnull; }
 
   // nsIHTMLContentSink
   NS_IMETHOD OpenHead();
   NS_IMETHOD IsEnabled(PRInt32 aTag, bool* aReturn);
   NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode) { return NS_OK; }
-  NS_IMETHOD_(bool) IsFormOnStack() { return false; }
 
   NS_IMETHOD BeginContext(PRInt32 aPosition) { return NS_OK; }
   NS_IMETHOD EndContext(PRInt32 aPosition) { return NS_OK; }
   NS_IMETHOD DidProcessTokens(void) { return NS_OK; }
   NS_IMETHOD WillProcessAToken(void) { return NS_OK; }
   NS_IMETHOD DidProcessAToken(void) { return NS_OK; }
 
   // nsIHTMLToTextSink
--- a/content/base/src/nsTraversal.cpp
+++ b/content/base/src/nsTraversal.cpp
@@ -44,22 +44,20 @@
 #include "nsDOMError.h"
 
 #include "nsIContent.h"
 
 #include "nsGkAtoms.h"
 
 nsTraversal::nsTraversal(nsINode *aRoot,
                          PRUint32 aWhatToShow,
-                         nsIDOMNodeFilter *aFilter,
-                         bool aExpandEntityReferences) :
+                         nsIDOMNodeFilter *aFilter) :
     mRoot(aRoot),
     mWhatToShow(aWhatToShow),
     mFilter(aFilter),
-    mExpandEntityReferences(aExpandEntityReferences),
     mInAcceptNode(false)
 {
     NS_ASSERTION(aRoot, "invalid root in call to nsTraversal constructor");
 }
 
 nsTraversal::~nsTraversal()
 {
     /* destructor code */
--- a/content/base/src/nsTraversal.h
+++ b/content/base/src/nsTraversal.h
@@ -49,25 +49,23 @@
 class nsINode;
 class nsIDOMNodeFilter;
 
 class nsTraversal
 {
 public:
     nsTraversal(nsINode *aRoot,
                 PRUint32 aWhatToShow,
-                nsIDOMNodeFilter *aFilter,
-                bool aExpandEntityReferences);
+                nsIDOMNodeFilter *aFilter);
     virtual ~nsTraversal();
 
 protected:
     nsCOMPtr<nsINode> mRoot;
     PRUint32 mWhatToShow;
     nsCOMPtr<nsIDOMNodeFilter> mFilter;
-    bool mExpandEntityReferences;
     bool mInAcceptNode;
 
     /*
      * Tests if and how a node should be filtered. Uses mWhatToShow and
      * mFilter to test the node.
      * @param aNode     Node to test
      * @param _filtered Returned filtervalue. See nsIDOMNodeFilter.idl
      * @returns         Errorcode
--- a/content/base/src/nsTreeWalker.cpp
+++ b/content/base/src/nsTreeWalker.cpp
@@ -53,19 +53,18 @@
 #include "nsContentUtils.h"
 
 /*
  * Factories, constructors and destructors
  */
 
 nsTreeWalker::nsTreeWalker(nsINode *aRoot,
                            PRUint32 aWhatToShow,
-                           nsIDOMNodeFilter *aFilter,
-                           bool aExpandEntityReferences) :
-    nsTraversal(aRoot, aWhatToShow, aFilter, aExpandEntityReferences),
+                           nsIDOMNodeFilter *aFilter) :
+    nsTraversal(aRoot, aWhatToShow, aFilter),
     mCurrentNode(aRoot)
 {
 }
 
 nsTreeWalker::~nsTreeWalker()
 {
     /* destructor code */
 }
@@ -122,17 +121,17 @@ NS_IMETHODIMP nsTreeWalker::GetFilter(ns
 
     return NS_OK;
 }
 
 /* readonly attribute boolean expandEntityReferences; */
 NS_IMETHODIMP
 nsTreeWalker::GetExpandEntityReferences(bool *aExpandEntityReferences)
 {
-    *aExpandEntityReferences = mExpandEntityReferences;
+    *aExpandEntityReferences = false;
     return NS_OK;
 }
 
 /* attribute nsIDOMNode currentNode; */
 NS_IMETHODIMP nsTreeWalker::GetCurrentNode(nsIDOMNode * *aCurrentNode)
 {
     if (mCurrentNode) {
         return CallQueryInterface(mCurrentNode, aCurrentNode);
--- a/content/base/src/nsTreeWalker.h
+++ b/content/base/src/nsTreeWalker.h
@@ -58,18 +58,17 @@ class nsIDOMNodeFilter;
 class nsTreeWalker : public nsIDOMTreeWalker, public nsTraversal
 {
 public:
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     NS_DECL_NSIDOMTREEWALKER
 
     nsTreeWalker(nsINode *aRoot,
                  PRUint32 aWhatToShow,
-                 nsIDOMNodeFilter *aFilter,
-                 bool aExpandEntityReferences);
+                 nsIDOMNodeFilter *aFilter);
     virtual ~nsTreeWalker();
 
     NS_DECL_CYCLE_COLLECTION_CLASS(nsTreeWalker)
 
 private:
     nsCOMPtr<nsINode> mCurrentNode;
 
     /*
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -77,16 +77,17 @@
 #include "mozilla/Preferences.h"
 #include "nsDOMLists.h"
 #include "xpcpublic.h"
 #include "nsContentPolicyUtils.h"
 #include "nsContentErrors.h"
 #include "jstypedarray.h"
 #include "prmem.h"
 #include "nsDOMFile.h"
+#include "nsWrapperCacheInlines.h"
 
 using namespace mozilla;
 
 #define UTF_8_REPLACEMENT_CHAR    static_cast<PRUnichar>(0xFFFD)
 
 #define TRUE_OR_FAIL_WEBSOCKET(x, ret)                                    \
   PR_BEGIN_MACRO                                                          \
     if (NS_UNLIKELY(!(x))) {                                              \
@@ -424,16 +425,41 @@ nsWebSocket::~nsWebSocket()
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
   Disconnect();
   nsLayoutStatics::Release();
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsWebSocket)
 
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsWebSocket)
+  if (tmp->IsBlack()) {
+    if (tmp->mListenerManager) {
+      tmp->mListenerManager->UnmarkGrayJSListeners();
+      NS_UNMARK_LISTENER_WRAPPER(Open)
+      NS_UNMARK_LISTENER_WRAPPER(Error)
+      NS_UNMARK_LISTENER_WRAPPER(Message)
+      NS_UNMARK_LISTENER_WRAPPER(Close)
+    }
+    return true;
+  }
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsWebSocket)
+  return tmp->IsBlack();
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsWebSocket)
+  return tmp->IsBlack();
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsWebSocket,
+                                               nsDOMEventTargetWrapperCache)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsWebSocket,
                                                   nsDOMEventTargetWrapperCache)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnOpenListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnMessageListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnCloseListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPrincipal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mURI)
--- a/content/base/src/nsWebSocket.h
+++ b/content/base/src/nsWebSocket.h
@@ -78,18 +78,18 @@ class nsWebSocket: public nsDOMEventTarg
 {
 friend class nsWSCloseEvent;
 friend class nsAutoCloseWS;
 
 public:
   nsWebSocket();
   virtual ~nsWebSocket();
   NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsWebSocket,
-                                           nsDOMEventTargetWrapperCache)
+  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_INHERITED(nsWebSocket,
+                                                                   nsDOMEventTargetWrapperCache)
   NS_DECL_NSIWEBSOCKET
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSIWEBSOCKETLISTENER
   NS_DECL_NSIREQUEST
 
   // nsIJSNativeInitializer
   NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* aContext,
                         JSObject* aObject, PRUint32 aArgc, jsval* aArgv);
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -100,22 +100,24 @@
 #include "nsIContentSecurityPolicy.h"
 #include "nsAsyncRedirectVerifyHelper.h"
 #include "jstypedarray.h"
 #include "nsStringBuffer.h"
 #include "nsDOMFile.h"
 #include "nsIFileChannel.h"
 #include "mozilla/Telemetry.h"
 #include "sampler.h"
+#include "nsWrapperCacheInlines.h"
 
 using namespace mozilla;
 
 #define LOAD_STR "load"
 #define ERROR_STR "error"
 #define ABORT_STR "abort"
+#define TIMEOUT_STR "timeout"
 #define LOADSTART_STR "loadstart"
 #define PROGRESS_STR "progress"
 #define UPLOADPROGRESS_STR "uploadprogress"
 #define READYSTATE_STR "readystatechange"
 #define LOADEND_STR "loadend"
 
 // CIDs
 
@@ -137,16 +139,17 @@ using namespace mozilla;
 #define XML_HTTP_REQUEST_GOT_FINAL_STOP (1 << 12) // Internal
 #define XML_HTTP_REQUEST_BACKGROUND     (1 << 13) // Internal
 // This is set when we've got the headers for a multipart XMLHttpRequest,
 // but haven't yet started to process the first part.
 #define XML_HTTP_REQUEST_MPART_HEADERS  (1 << 14) // Internal
 #define XML_HTTP_REQUEST_USE_XSITE_AC   (1 << 15) // Internal
 #define XML_HTTP_REQUEST_NEED_AC_PREFLIGHT (1 << 16) // Internal
 #define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 17) // Internal
+#define XML_HTTP_REQUEST_TIMED_OUT (1 << 18) // Internal
 
 #define XML_HTTP_REQUEST_LOADSTATES         \
   (XML_HTTP_REQUEST_UNSENT |                \
    XML_HTTP_REQUEST_OPENED |                \
    XML_HTTP_REQUEST_HEADERS_RECEIVED |      \
    XML_HTTP_REQUEST_LOADING |               \
    XML_HTTP_REQUEST_DONE |                  \
    XML_HTTP_REQUEST_SENT |                  \
@@ -299,26 +302,28 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(nsXHREven
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXHREventTarget,
                                                   nsDOMEventTargetWrapperCache)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnLoadListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnAbortListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnLoadStartListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnProgressListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnLoadendListener)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnTimeoutListener)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXHREventTarget,
                                                 nsDOMEventTargetWrapperCache)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnLoadListener)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnAbortListener)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnLoadStartListener)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnProgressListener)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnLoadendListener)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnTimeoutListener)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXHREventTarget)
   NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequestEventTarget)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetWrapperCache)
 
 NS_IMPL_ADDREF_INHERITED(nsXHREventTarget, nsDOMEventTargetWrapperCache)
 NS_IMPL_RELEASE_INHERITED(nsXHREventTarget, nsDOMEventTargetWrapperCache)
@@ -383,16 +388,29 @@ nsXHREventTarget::GetOnprogress(nsIDOMEv
 
 NS_IMETHODIMP
 nsXHREventTarget::SetOnprogress(nsIDOMEventListener* aOnprogress)
 {
   return RemoveAddEventListener(NS_LITERAL_STRING(PROGRESS_STR),
                                 mOnProgressListener, aOnprogress);
 }
 
+/* attribute nsIDOMEventListener ontimeout; */
+NS_IMETHODIMP
+nsXHREventTarget::GetOntimeout(nsIDOMEventListener * *aOntimeout)
+{
+  return GetInnerEventListener(mOnTimeoutListener, aOntimeout);
+}
+NS_IMETHODIMP
+nsXHREventTarget::SetOntimeout(nsIDOMEventListener *aOntimeout)
+{
+  return RemoveAddEventListener(NS_LITERAL_STRING(TIMEOUT_STR),
+                                mOnTimeoutListener, aOntimeout);
+}
+
 NS_IMETHODIMP
 nsXHREventTarget::GetOnloadend(nsIDOMEventListener** aOnLoadend)
 {
   return GetInnerEventListener(mOnLoadendListener, aOnLoadend);
 }
 
 NS_IMETHODIMP
 nsXHREventTarget::SetOnloadend(nsIDOMEventListener* aOnLoadend)
@@ -420,23 +438,23 @@ NS_IMPL_RELEASE_INHERITED(nsXMLHttpReque
 
 nsXMLHttpRequest::nsXMLHttpRequest()
   : mResponseBodyDecodedPos(0),
     mResponseType(XML_HTTP_RESPONSE_TYPE_DEFAULT),
     mRequestObserver(nsnull), mState(XML_HTTP_REQUEST_UNSENT),
     mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
     mProgressSinceLastProgressEvent(false),
     mUploadProgress(0), mUploadProgressMax(0),
-    mErrorLoad(false), mTimerIsActive(false),
+    mRequestSentTime(0), mTimeoutMilliseconds(0),
+    mErrorLoad(false), mProgressTimerIsActive(false),
     mProgressEventWasDelayed(false),
-    mLoadLengthComputable(false),
     mIsHtml(false),
     mWarnAboutMultipartHtml(false),
     mWarnAboutSyncHtml(false),
-    mLoadTotal(0),
+    mLoadLengthComputable(false), mLoadTotal(0),
     mFirstStartRequestSeen(false),
     mInLoadProgressEvent(false),
     mResultJSON(JSVAL_VOID),
     mResultArrayBuffer(nsnull)
 {
   nsLayoutStatics::AddRef();
 }
 
@@ -573,16 +591,41 @@ nsXMLHttpRequest::ResetResponse()
 void
 nsXMLHttpRequest::SetRequestObserver(nsIRequestObserver* aObserver)
 {
   mRequestObserver = aObserver;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequest)
 
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXMLHttpRequest)
+  if (tmp->IsBlack()) {
+    if (tmp->mListenerManager) {
+      tmp->mListenerManager->UnmarkGrayJSListeners();
+      NS_UNMARK_LISTENER_WRAPPER(Load)
+      NS_UNMARK_LISTENER_WRAPPER(Error)
+      NS_UNMARK_LISTENER_WRAPPER(Abort)
+      NS_UNMARK_LISTENER_WRAPPER(LoadStart)
+      NS_UNMARK_LISTENER_WRAPPER(Progress)
+      NS_UNMARK_LISTENER_WRAPPER(Loadend)
+      NS_UNMARK_LISTENER_WRAPPER(UploadProgress)
+      NS_UNMARK_LISTENER_WRAPPER(Readystatechange)
+    }
+    return true;
+  }
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXMLHttpRequest)
+  return tmp->IsBlack();
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsXMLHttpRequest)
+  return tmp->IsBlack();
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
+
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLHttpRequest,
                                                   nsXHREventTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannel)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mReadRequest)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mResponseXML)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCORSPreflightChannel)
 
@@ -1162,59 +1205,67 @@ nsXMLHttpRequest::GetStatusText(nsACStri
     }
 
     httpChannel->GetResponseStatusText(aStatusText);
   }
 
   return NS_OK;
 }
 
-/* void abort (); */
-NS_IMETHODIMP
-nsXMLHttpRequest::Abort()
+void
+nsXMLHttpRequest::CloseRequestWithError(const nsAString& aType,
+                                        const PRUint32 aFlag)
 {
   if (mReadRequest) {
     mReadRequest->Cancel(NS_BINDING_ABORTED);
   }
   if (mChannel) {
     mChannel->Cancel(NS_BINDING_ABORTED);
   }
   if (mCORSPreflightChannel) {
     mCORSPreflightChannel->Cancel(NS_BINDING_ABORTED);
   }
+  if (mTimeoutTimer) {
+    mTimeoutTimer->Cancel();
+  }
   PRUint32 responseLength = mResponseBody.Length();
   ResetResponse();
-  mState |= XML_HTTP_REQUEST_ABORTED;
+  mState |= aFlag;
   
   if (!(mState & (XML_HTTP_REQUEST_UNSENT |
                   XML_HTTP_REQUEST_OPENED |
                   XML_HTTP_REQUEST_DONE))) {
     ChangeState(XML_HTTP_REQUEST_DONE, true);
 
     if (!(mState & XML_HTTP_REQUEST_SYNCLOOPING)) {
-      NS_NAMED_LITERAL_STRING(abortStr, ABORT_STR);
-      DispatchProgressEvent(this, abortStr, mLoadLengthComputable, responseLength,
+      DispatchProgressEvent(this, aType, mLoadLengthComputable, responseLength,
                             mLoadTotal);
       if (mUpload && !mUploadComplete) {
         mUploadComplete = true;
-        DispatchProgressEvent(mUpload, abortStr, true, mUploadTransferred,
+        DispatchProgressEvent(mUpload, aType, true, mUploadTransferred,
                               mUploadTotal);
       }
     }
   }
 
   // The ChangeState call above calls onreadystatechange handlers which
   // if they load a new url will cause nsXMLHttpRequest::Open to clear
   // the abort state bit. If this occurs we're not uninitialized (bug 361773).
   if (mState & XML_HTTP_REQUEST_ABORTED) {
     ChangeState(XML_HTTP_REQUEST_UNSENT, false);  // IE seems to do it
   }
 
   mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
-
+}
+
+/* void abort (); */
+NS_IMETHODIMP
+nsXMLHttpRequest::Abort()
+{
+  CloseRequestWithError(NS_LITERAL_STRING(ABORT_STR), XML_HTTP_REQUEST_ABORTED);
   return NS_OK;
 }
 
 /* string getAllResponseHeaders (); */
 NS_IMETHODIMP
 nsXMLHttpRequest::GetAllResponseHeaders(char **_retval)
 {
   NS_ENSURE_ARG_POINTER(_retval);
@@ -1382,16 +1433,17 @@ nsXMLHttpRequest::DispatchProgressEvent(
   if (NS_FAILED(CheckInnerWindowCorrectness()) ||
       (!AllowUploadProgress() &&
        (aTarget == mUpload || aType.EqualsLiteral(UPLOADPROGRESS_STR)))) {
     return;
   }
 
   bool dispatchLoadend = aType.EqualsLiteral(LOAD_STR) ||
                            aType.EqualsLiteral(ERROR_STR) ||
+                           aType.EqualsLiteral(TIMEOUT_STR) ||
                            aType.EqualsLiteral(ABORT_STR);
   
   nsCOMPtr<nsIDOMEvent> event;
   nsresult rv = nsEventDispatcher::CreateEvent(nsnull, nsnull,
                                                NS_LITERAL_STRING("ProgressEvent"),
                                                getter_AddRefs(event));
   if (NS_FAILED(rv)) {
     return;
@@ -1513,53 +1565,50 @@ nsXMLHttpRequest::Open(const nsACString&
       method.LowerCaseEqualsLiteral("track")) {
     return NS_ERROR_INVALID_ARG;
   }
 
   // sync request is not allowed using withCredential or responseType
   // in window context
   if (!async && mOwner &&
       (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS ||
+       mTimeoutMilliseconds ||
        mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT)) {
     if (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS) {
       LogMessage("WithCredentialsSyncXHRWarning", mOwner);
     }
+    if (mTimeoutMilliseconds) {
+      LogMessage("TimeoutSyncXHRWarning", mOwner);
+    }
     if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT) {
       LogMessage("ResponseTypeSyncXHRWarning", mOwner);
     }
     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   }
 
   nsresult rv;
   nsCOMPtr<nsIURI> uri;
-  bool authp = false;
 
   if (mState & (XML_HTTP_REQUEST_OPENED |
                 XML_HTTP_REQUEST_HEADERS_RECEIVED |
                 XML_HTTP_REQUEST_LOADING |
                 XML_HTTP_REQUEST_SENT |
                 XML_HTTP_REQUEST_STOPPED)) {
     // IE aborts as well
     Abort();
 
     // XXX We should probably send a warning to the JS console
     //     that load was aborted and event listeners were cleared
     //     since this looks like a situation that could happen
     //     by accident and you could spend a lot of time wondering
     //     why things didn't work.
   }
 
-  if (mState & XML_HTTP_REQUEST_ABORTED) {
-    // Something caused this request to abort (e.g the current request
-    // was caceled, channels closed etc), most likely the abort()
-    // function was called by script. Unset our aborted state, and
-    // proceed as normal
-
-    mState &= ~XML_HTTP_REQUEST_ABORTED;
-  }
+  // Unset any pre-existing aborted and timed-out states.
+  mState &= ~XML_HTTP_REQUEST_ABORTED & ~XML_HTTP_REQUEST_TIMED_OUT;
 
   if (async) {
     mState |= XML_HTTP_REQUEST_ASYNC;
   } else {
     mState &= ~XML_HTTP_REQUEST_ASYNC;
   }
 
   mState &= ~XML_HTTP_REQUEST_MPART_HEADERS;
@@ -1601,17 +1650,16 @@ nsXMLHttpRequest::Open(const nsACString&
   if (!user.IsEmpty()) {
     nsCAutoString userpass;
     CopyUTF16toUTF8(user, userpass);
     if (!password.IsEmpty()) {
       userpass.Append(':');
       AppendUTF16toUTF8(password, userpass);
     }
     uri->SetUserPass(userpass);
-    authp = true;
   }
 
   // When we are called from JS we can find the load group for the page,
   // and add ourselves to it. This way any pending requests
   // will be automatically aborted if the user leaves the page.
   nsCOMPtr<nsILoadGroup> loadGroup = GetLoadGroup();
 
   // get Content Security Policy from principal to pass into channel
@@ -1815,36 +1863,44 @@ IsSameOrBaseChannel(nsIRequest* aPossibl
 
   return aPossibleBase == aChannel;
 }
 
 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
 NS_IMETHODIMP
 nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
 {
+  SAMPLE_LABEL("nsXMLHttpRequest", "OnStartRequest");
   nsresult rv = NS_OK;
   if (!mFirstStartRequestSeen && mRequestObserver) {
     mFirstStartRequestSeen = true;
     mRequestObserver->OnStartRequest(request, ctxt);
   }
 
   if (!IsSameOrBaseChannel(request, mChannel)) {
     return NS_OK;
   }
 
   // Don't do anything if we have been aborted
   if (mState & XML_HTTP_REQUEST_UNSENT)
     return NS_OK;
 
+  /* Apparently, Abort() should set XML_HTTP_REQUEST_UNSENT.  See bug 361773.
+     XHR2 spec says this is correct. */
   if (mState & XML_HTTP_REQUEST_ABORTED) {
     NS_ERROR("Ugh, still getting data on an aborted XMLHttpRequest!");
 
     return NS_ERROR_UNEXPECTED;
   }
 
+  // Don't do anything if we have timed out.
+  if (mState & XML_HTTP_REQUEST_TIMED_OUT) {
+    return NS_OK;
+  }
+
   nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
   NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
 
   nsCOMPtr<nsIPrincipal> documentPrincipal;
   if (IsSystemXHR()) {
     // Don't give this document the system principal.  We need to keep track of
     // mPrincipal being system because we use it for various security checks
     // that should be passing, but the document data shouldn't get a system
@@ -1859,18 +1915,18 @@ nsXMLHttpRequest::OnStartRequest(nsIRequ
   channel->SetOwner(documentPrincipal);
 
   nsresult status;
   request->GetStatus(&status);
   mErrorLoad = mErrorLoad || NS_FAILED(status);
 
   if (mUpload && !mUploadComplete && !mErrorLoad &&
       (mState & XML_HTTP_REQUEST_ASYNC)) {
-    if (mTimerIsActive) {
-      mTimerIsActive = false;
+    if (mProgressTimerIsActive) {
+      mProgressTimerIsActive = false;
       mProgressNotifier->Cancel();
     }
     MaybeDispatchProgressEvents(true);
     mUploadComplete = true;
     DispatchProgressEvent(mUpload, NS_LITERAL_STRING(LOAD_STR),
                           true, mUploadTotal, mUploadTotal);
   }
 
@@ -2047,17 +2103,19 @@ nsXMLHttpRequest::OnStopRequest(nsIReque
   if (mRequestObserver && mState & XML_HTTP_REQUEST_GOT_FINAL_STOP) {
     NS_ASSERTION(mFirstStartRequestSeen, "Inconsistent state!");
     mFirstStartRequestSeen = false;
     mRequestObserver->OnStopRequest(request, ctxt, status);
   }
 
   // make sure to notify the listener if we were aborted
   // XXX in fact, why don't we do the cleanup below in this case??
-  if (mState & XML_HTTP_REQUEST_UNSENT) {
+  // XML_HTTP_REQUEST_UNSENT is for abort calls.  See OnStartRequest above.
+  if ((mState & XML_HTTP_REQUEST_UNSENT) ||
+      (mState & XML_HTTP_REQUEST_TIMED_OUT)) {
     if (mXMLParserStreamListener)
       (void) mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
     return NS_OK;
   }
 
   // Is this good enough here?
   if (mState & XML_HTTP_REQUEST_PARSEBODY && mXMLParserStreamListener) {
     mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
@@ -2156,17 +2214,21 @@ nsXMLHttpRequest::OnStopRequest(nsIReque
 void
 nsXMLHttpRequest::ChangeStateToDone()
 {
   if (mIsHtml) {
     // In the HTML case, this has to be deferred, because the parser doesn't
     // do it's job synchronously.
     MaybeDispatchProgressEvents(true);
   }
+
   ChangeState(XML_HTTP_REQUEST_DONE, true);
+  if (mTimeoutTimer) {
+    mTimeoutTimer->Cancel();
+  }
 
   NS_NAMED_LITERAL_STRING(errorStr, ERROR_STR);
   NS_NAMED_LITERAL_STRING(loadStr, LOAD_STR);
   DispatchProgressEvent(this,
                         mErrorLoad ? errorStr : loadStr,
                         !mErrorLoad,
                         mLoadTransferred,
                         mErrorLoad ? 0 : mLoadTransferred);
@@ -2622,16 +2684,20 @@ nsXMLHttpRequest::Send(nsIVariant *aBody
         nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
   }
 
   // Since we expect XML data, set the type hint accordingly
   // This means that we always try to parse local files as XML
   // ignoring return value, as this is not critical
   mChannel->SetContentType(NS_LITERAL_CSTRING("application/xml"));
 
+  // We're about to send the request.  Start our timeout.
+  mRequestSentTime = PR_Now();
+  StartTimeoutTimer();
+
   // Set up the preflight if needed
   if (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT) {
     // Check to see if this initial OPTIONS request has already been cached
     // in our special Access Control Cache.
 
     rv = NS_StartCORSPreflight(mChannel, listener,
                                mPrincipal, withCredentials,
                                mCORSUnsafeHeaders,
@@ -2813,16 +2879,70 @@ nsXMLHttpRequest::SetRequestHeader(const
       nsCString(header), nsCString(value)
     };
     mModifiedRequestHeaders.AppendElement(reqHeader);
   }
 
   return rv;
 }
 
+/* attribute unsigned long timeout; */
+NS_IMETHODIMP
+nsXMLHttpRequest::GetTimeout(PRUint32 *aTimeout)
+{
+  *aTimeout = mTimeoutMilliseconds;
+  return NS_OK;
+}
+NS_IMETHODIMP
+nsXMLHttpRequest::SetTimeout(PRUint32 aTimeout)
+{
+  if ((mState & (XML_HTTP_REQUEST_ASYNC | XML_HTTP_REQUEST_UNSENT)) || !mOwner) {
+    mTimeoutMilliseconds = aTimeout;
+    if (mRequestSentTime) {
+      StartTimeoutTimer();
+    }
+    return NS_OK;
+  }
+
+  /* Timeout is not supported for synchronous requests with an owning window,
+     per XHR2 spec. */
+  LogMessage("TimeoutSyncXHRWarning", mOwner);
+  return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+}
+
+void
+nsXMLHttpRequest::StartTimeoutTimer()
+{
+  NS_ABORT_IF_FALSE(mRequestSentTime,
+                    "StartTimeoutTimer mustn't be called before the request was sent!");
+  if (mState & XML_HTTP_REQUEST_DONE) {
+    // do nothing!
+    return;
+  }
+
+  if (mTimeoutTimer) {
+    mTimeoutTimer->Cancel();
+  }
+
+  if (!mTimeoutMilliseconds) {
+    return;
+  }
+
+  if (!mTimeoutTimer) {
+    mTimeoutTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+  }
+  PRUint32 elapsed =
+    (PRUint32)((PR_Now() - mRequestSentTime) / PR_USEC_PER_MSEC);
+  mTimeoutTimer->InitWithCallback(
+    this,
+    mTimeoutMilliseconds > elapsed ? mTimeoutMilliseconds - elapsed : 0,
+    nsITimer::TYPE_ONE_SHOT
+  );
+}
+
 /* readonly attribute long readyState; */
 NS_IMETHODIMP
 nsXMLHttpRequest::GetReadyState(PRUint16 *aState)
 {
   NS_ENSURE_ARG_POINTER(aState);
   // Translate some of our internal states for external consumers
   if (mState & XML_HTTP_REQUEST_UNSENT) {
     *aState = UNSENT;
@@ -2955,17 +3075,17 @@ nsXMLHttpRequest::ChangeState(PRUint32 a
   if (aState & XML_HTTP_REQUEST_LOADSTATES) {
     mState &= ~XML_HTTP_REQUEST_LOADSTATES;
   }
   mState |= aState;
   nsresult rv = NS_OK;
 
   if (mProgressNotifier &&
       !(aState & (XML_HTTP_REQUEST_HEADERS_RECEIVED | XML_HTTP_REQUEST_LOADING))) {
-    mTimerIsActive = false;
+    mProgressTimerIsActive = false;
     mProgressNotifier->Cancel();
   }
 
   if ((aState & XML_HTTP_REQUEST_LOADSTATES) && // Broadcast load states only
       aBroadcast &&
       (mState & XML_HTTP_REQUEST_ASYNC ||
        aState & XML_HTTP_REQUEST_OPENED ||
        aState & XML_HTTP_REQUEST_DONE)) {
@@ -3106,22 +3226,22 @@ nsXMLHttpRequest::OnRedirectVerifyCallba
 
 /////////////////////////////////////////////////////
 // nsIProgressEventSink methods:
 //
 
 void
 nsXMLHttpRequest::MaybeDispatchProgressEvents(bool aFinalProgress)
 {
-  if (aFinalProgress && mTimerIsActive) {