Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Wed, 16 May 2012 15:39:10 -0700
changeset 112498 3b9f67f4ab7140c9447cd5d1598ac4005223df73
parent 112497 e14efc51f56268a5f5981d0c6544cf0af2ddda2d (current diff)
parent 98157 e493ce7639a3117baf534b50990660d8589c5464 (diff)
child 112499 7223b430719826265fae65749380ae740f2beac0
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone15.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge from mozilla-central.
accessible/src/html/nsHTMLSelectAccessible.cpp
accessible/src/html/nsHTMLSelectAccessible.h
browser/base/content/browser.js
browser/base/content/browser.xul
browser/base/content/highlighter.css
browser/base/content/tabbrowser.xml
browser/components/nsBrowserContentHandler.js
content/base/src/nsDOMFile.cpp
content/base/src/nsFrameLoader.cpp
content/base/src/nsXMLHttpRequest.cpp
content/canvas/src/CanvasUtils.h
content/canvas/src/CustomQS_Canvas2D.h
content/canvas/src/WebGLContext.cpp
content/canvas/src/WebGLContext.h
content/canvas/src/WebGLContextGL.cpp
content/canvas/src/WebGLContextValidate.cpp
content/canvas/src/nsCanvasRenderingContext2D.cpp
content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
content/html/content/src/nsHTMLCanvasElement.cpp
content/html/content/src/nsHTMLMediaElement.cpp
content/html/content/src/nsHTMLSourceElement.cpp
content/mathml/content/src/nsMathMLElement.cpp
content/mathml/content/src/nsMathMLElement.h
content/media/VideoUtils.h
content/media/nsBuiltinDecoderReader.cpp
content/media/nsBuiltinDecoderStateMachine.cpp
content/media/ogg/nsOggCodecState.cpp
content/media/raw/nsRawReader.cpp
content/media/webm/nsWebMReader.cpp
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBFactory.cpp
dom/indexedDB/IndexedDatabaseManager.cpp
dom/locales/en-US/chrome/dom/dom.properties
dom/wifi/WifiWorker.js
gfx/gl/GLContextProviderEGL.cpp
gfx/layers/basic/BasicLayers.cpp
gfx/layers/opengl/ImageLayerOGL.cpp
gfx/layers/opengl/ImageLayerOGL.h
gfx/thebes/gfxASurface.cpp
gfx/thebes/gfxAndroidPlatform.cpp
gfx/thebes/gfxAndroidPlatform.h
gfx/thebes/gfxContext.h
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxPlatform.h
js/jsd/jsd_xpc.cpp
js/jsd/jsd_xpc.h
js/src/gc/Marking.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsdbgapi.cpp
js/src/jsdbgapi.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsscript.cpp
js/src/jsscript.h
js/xpconnect/src/XPCVariant.cpp
js/xpconnect/src/XPCWrappedJS.cpp
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
layout/base/FrameLayerBuilder.cpp
layout/mathml/nsMathMLFrame.cpp
layout/mathml/nsMathMLFrame.h
layout/mathml/nsMathMLmfracFrame.cpp
layout/mathml/nsMathMLmmultiscriptsFrame.cpp
layout/mathml/nsMathMLmoFrame.cpp
layout/mathml/nsMathMLmpaddedFrame.cpp
layout/mathml/nsMathMLmspaceFrame.cpp
layout/mathml/nsMathMLmsubFrame.cpp
layout/mathml/nsMathMLmsubsupFrame.cpp
layout/mathml/nsMathMLmsupFrame.cpp
mfbt/CheckedInt.h
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/nsHttpConnection.h
netwerk/protocol/http/nsHttpConnectionMgr.cpp
netwerk/protocol/res/nsResProtocolHandler.cpp
toolkit/content/license.html
toolkit/mozapps/installer/packager.mk
widget/cocoa/nsChildView.h
widget/cocoa/nsCocoaUtils.h
widget/cocoa/nsCocoaUtils.mm
widget/gtk2/nsWindow.cpp
widget/qt/nsWindow.cpp
widget/windows/nsWindow.cpp
widget/xpwidgets/nsBaseWidget.cpp
xpcom/ds/CheckedInt.h
xpcom/tests/TestCheckedInt.cpp
--- a/Makefile.in
+++ b/Makefile.in
@@ -172,17 +172,17 @@ SYM_STORE_SOURCE_DIRS := $(topsrcdir)
 
 include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
 
 ifdef MOZ_SYMBOLS_EXTRA_BUILDID
 EXTRA_BUILDID := -$(MOZ_SYMBOLS_EXTRA_BUILDID)
 endif
 
 SYMBOL_INDEX_NAME = \
-  $(MOZ_APP_NAME)-$(MOZ_APP_VERSION)-$(OS_TARGET)-$(BUILDID)$(EXTRA_BUILDID)-symbols.txt
+  $(MOZ_APP_NAME)-$(MOZ_APP_VERSION)-$(OS_TARGET)-$(BUILDID)-$(CPU_ARCH)$(EXTRA_BUILDID)-symbols.txt
 
 buildsymbols:
 ifdef MOZ_CRASHREPORTER
 ifdef USE_ELF_HACK
     ifeq (mobile,$(MOZ_BUILD_APP))
 		$(MAKE) -C mobile/xul/installer elfhack
     else
 		$(MAKE) -C $(MOZ_BUILD_APP)/installer elfhack
--- a/accessible/src/html/nsHTMLSelectAccessible.cpp
+++ b/accessible/src/html/nsHTMLSelectAccessible.cpp
@@ -83,19 +83,16 @@ nsHTMLSelectListAccessible::NativeState(
     state |= states::MULTISELECTABLE | states::EXTSELECTABLE;
 
   return state;
 }
 
 role
 nsHTMLSelectListAccessible::NativeRole()
 {
-  if (mParent && mParent->Role() == roles::COMBOBOX)
-    return roles::COMBOBOX_LIST;
-
   return roles::LISTBOX;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLSelectListAccessible: SelectAccessible
 
 bool
 nsHTMLSelectListAccessible::IsSelect()
@@ -705,16 +702,22 @@ bool
 nsHTMLComboboxListAccessible::IsPrimaryForNode() const
 {
   return false;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLComboboxAccessible: nsAccessible
 
+role
+nsHTMLComboboxListAccessible::NativeRole()
+{
+  return roles::COMBOBOX_LIST;
+}
+
 PRUint64
 nsHTMLComboboxListAccessible::NativeState()
 {
   // As a nsHTMLComboboxListAccessible we can have the following states:
   // FOCUSED, FOCUSABLE, FLOATING, INVISIBLE
   // Get focus status from base class
   PRUint64 state = nsAccessible::NativeState();
 
--- a/accessible/src/html/nsHTMLSelectAccessible.h
+++ b/accessible/src/html/nsHTMLSelectAccessible.h
@@ -253,16 +253,17 @@ public:
                                nsDocAccessible* aDoc);
   virtual ~nsHTMLComboboxListAccessible() {}
 
   // nsAccessNode
   virtual nsIFrame* GetFrame() const;
   virtual bool IsPrimaryForNode() const;
 
   // nsAccessible
+  virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
   virtual void GetBoundsRect(nsRect& aBounds, nsIFrame** aBoundingFrame);
 
   // Widgets
   virtual bool IsActiveWidget() const;
   virtual bool AreItemsOperable() const;
 };
 
--- a/accessible/src/jsat/AccessFu.jsm
+++ b/accessible/src/jsat/AccessFu.jsm
@@ -194,16 +194,31 @@ var AccessFu = {
   _handleAccEvent: function _handleAccEvent(aEvent) {
     switch (aEvent.eventType) {
       case Ci.nsIAccessibleEvent.EVENT_VIRTUALCURSOR_CHANGED:
         {
           let pivot = aEvent.accessible.
             QueryInterface(Ci.nsIAccessibleCursorable).virtualCursor;
           let event = aEvent.
             QueryInterface(Ci.nsIAccessibleVirtualCursorChangeEvent);
+          let position = pivot.position;
+          let doc = aEvent.DOMNode;
+
+          if (doc instanceof Ci.nsIDOMDocument && position.DOMNode) {
+            // Set the caret to the start of the pivot position, and move
+            // the focus in the same manner as browse with caret mode.
+            // This blurs the focus on the previous pivot position (if it
+            // was activated), and keeps us in a predictable spot for tab
+            // focus.
+            let sel = doc.getSelection();
+            sel.collapse(position.DOMNode, 0);
+            Cc["@mozilla.org/focus-manager;1"]
+              .getService(Ci.nsIFocusManager).moveFocus(
+                doc.defaultView, null, Ci.nsIFocusManager.MOVEFOCUS_CARET, 0);
+          }
 
           let newContext = this.getNewContext(event.oldAccessible,
                                               pivot.position);
           this.presenters.forEach(
             function(p) {
               p.pivotChanged(pivot.position, newContext);
             });
           break;
--- a/accessible/src/jsat/VirtualCursorController.jsm
+++ b/accessible/src/jsat/VirtualCursorController.jsm
@@ -13,77 +13,102 @@ var EXPORTED_SYMBOLS = ['VirtualCursorCo
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 
 var gAccRetrieval = Cc['@mozilla.org/accessibleRetrieval;1'].
   getService(Ci.nsIAccessibleRetrieval);
 
 var VirtualCursorController = {
+  NOT_EDITABLE: 0,
+  SINGLE_LINE_EDITABLE: 1,
+  MULTI_LINE_EDITABLE: 2,
+
   attach: function attach(aWindow) {
     this.chromeWin = aWindow;
-    this.chromeWin.document.addEventListener('keypress', this._onkeypress, true);
+    this.chromeWin.document.addEventListener('keypress', this, true);
   },
 
   detach: function detach() {
-    this.chromeWin.document.removeEventListener('keypress', this._onkeypress,
-                                                true);
+    this.chromeWin.document.removeEventListener('keypress', this, true);
   },
 
   _getBrowserApp: function _getBrowserApp() {
     switch (Services.appinfo.OS) {
       case 'Android':
         return this.chromeWin.BrowserApp;
       default:
         return this.chromeWin.gBrowser;
     }
   },
 
-  _onkeypress: function _onkeypress(aEvent) {
-    let document = VirtualCursorController._getBrowserApp().
-      selectedBrowser.contentDocument;
-
-    dump('keypress ' + aEvent.keyCode + '\n');
+  handleEvent: function handleEvent(aEvent) {
+    let document = this._getBrowserApp().selectedBrowser.contentDocument;
+    let target = aEvent.target;
 
     switch (aEvent.keyCode) {
       case aEvent.DOM_VK_END:
-        VirtualCursorController.moveForward(document, true);
+        this.moveForward(document, true);
         break;
       case aEvent.DOM_VK_HOME:
-        VirtualCursorController.moveBackward(document, true);
+        this.moveBackward(document, true);
         break;
       case aEvent.DOM_VK_RIGHT:
-        VirtualCursorController.moveForward(document, aEvent.shiftKey);
+        if (this._isEditableText(target) &&
+            target.selectionEnd != target.textLength)
+          // Don't move forward if caret is not at end of entry.
+          // XXX: Fix for rtl
+          return;
+        this.moveForward(document, aEvent.shiftKey);
         break;
       case aEvent.DOM_VK_LEFT:
-        VirtualCursorController.moveBackward(document, aEvent.shiftKey);
+        if (this._isEditableText(target) &&
+            target.selectionEnd != 0)
+          // Don't move backward if caret is not at start of entry.
+          // XXX: Fix for rtl
+          return;
+        this.moveBackward(document, aEvent.shiftKey);
         break;
       case aEvent.DOM_VK_UP:
+        if (this._isEditableText(target) == this.MULTI_LINE_EDITABLE &&
+            target.selectionEnd != 0)
+          // Don't blur content if caret is not at start of text area.
+          return;
         if (Services.appinfo.OS == 'Android')
           // Return focus to native Android browser chrome.
           Cc['@mozilla.org/android/bridge;1'].
             getService(Ci.nsIAndroidBridge).handleGeckoMessage(
               JSON.stringify({ gecko: { type: 'ToggleChrome:Focus' } }));
         break;
       case aEvent.DOM_VK_RETURN:
-        // XXX: It is true that desktop does not map the keypad enter key to
-        // DOM_VK_ENTER. So for desktop we require a ctrl+return instead.
-        if (Services.appinfo.OS == 'Android' || !aEvent.ctrlKey)
+      case aEvent.DOM_VK_ENTER:
+        if (this._isEditableText(target))
           return;
-      case aEvent.DOM_VK_ENTER:
-        VirtualCursorController.activateCurrent(document);
+        this.activateCurrent(document);
         break;
       default:
         return;
     }
 
     aEvent.preventDefault();
     aEvent.stopPropagation();
   },
 
+  _isEditableText: function _isEditableText(aElement) {
+    // XXX: Support contentEditable and design mode
+    if (aElement instanceof Ci.nsIDOMHTMLInputElement &&
+        aElement.mozIsTextField(false))
+      return this.SINGLE_LINE_EDITABLE;
+
+    if (aElement instanceof Ci.nsIDOMHTMLTextAreaElement)
+      return this.MULTI_LINE_EDITABLE;
+
+    return this.NOT_EDITABLE;
+  },
+
   moveForward: function moveForward(document, last) {
     let virtualCursor = this.getVirtualCursor(document);
     if (last) {
       virtualCursor.moveLast(this.SimpleTraversalRule);
     } else {
       virtualCursor.moveNext(this.SimpleTraversalRule);
     }
   },
--- a/browser/base/content/browser-appmenu.inc
+++ b/browser/base/content/browser-appmenu.inc
@@ -193,17 +193,17 @@
                     hidden="true"
                     label="&inspectMenu.label;"
                     type="checkbox"
                     command="Tools:Inspect"
                     key="key_inspect"/>
           <menuitem id="appmenu_debugger"
                     hidden="true"
                     type="checkbox"
-                    label="&debuggerMenu.label;"
+                    label="&debuggerMenu.label2;"
                     key="key_debugger"
                     command="Tools:Debugger"/>
           <menuitem id="appmenu_remoteDebugger"
                     hidden="true"
                     label="&remoteDebuggerMenu.label;"
                     command="Tools:RemoteDebugger"/>
           <menuitem id="appmenu_chromeDebugger"
                     hidden="true"
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -569,17 +569,17 @@
                             hidden="true"
                             label="&inspectMenu.label;"
                             accesskey="&inspectMenu.accesskey;"
                             key="key_inspect"
                             command="Tools:Inspect"/>
                   <menuitem id="menu_debugger"
                             hidden="true"
                             type="checkbox"
-                            label="&debuggerMenu.label;"
+                            label="&debuggerMenu.label2;"
                             key="key_debugger"
                             command="Tools:Debugger"/>
                   <menuitem id="menu_remoteDebugger"
                             hidden="true"
                             label="&remoteDebuggerMenu.label;"
                             command="Tools:RemoteDebugger"/>
                   <menuitem id="menu_chromeDebugger"
                             hidden="true"
--- a/browser/base/content/browser-thumbnails.js
+++ b/browser/base/content/browser-thumbnails.js
@@ -3,19 +3,29 @@
  * 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 = {
+  /**
+   * Pref that controls whether we can store SSL content on disk
+   */
+  PREF_DISK_CACHE_SSL: "browser.cache.disk_cache_ssl",
+
   _captureDelayMS: 1000,
 
   /**
+   * Used to keep track of disk_cache_ssl preference
+   */
+  _sslDiskCacheEnabled: null,
+
+  /**
    * Map of capture() timeouts assigned to their browsers.
    */
   _timeouts: null,
 
   /**
    * Cache for the PageThumbs module.
    */
   _pageThumbs: null,
@@ -27,29 +37,34 @@ let gBrowserThumbnails = {
 
   init: function Thumbnails_init() {
     try {
       if (Services.prefs.getBoolPref("browser.pagethumbnails.capturing_disabled"))
         return;
     } catch (e) {}
 
     gBrowser.addTabsProgressListener(this);
+    Services.prefs.addObserver(this.PREF_DISK_CACHE_SSL, this, false);
+
+    this._sslDiskCacheEnabled =
+      Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
 
     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);
+    Services.prefs.removeObserver(this.PREF_DISK_CACHE_SSL, this);
 
     this._tabEvents.forEach(function (aEvent) {
       gBrowser.tabContainer.removeEventListener(aEvent, this, false);
     }, this);
   },
 
   handleEvent: function Thumbnails_handleEvent(aEvent) {
     switch (aEvent.type) {
@@ -63,16 +78,21 @@ let gBrowserThumbnails = {
         break;
       case "TabClose": {
         this._clearTimeout(aEvent.target.linkedBrowser);
         break;
       }
     }
   },
 
+  observe: function Thumbnails_observe() {
+    this._sslDiskCacheEnabled =
+      Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
+  },
+
   /**
    * 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);
@@ -124,29 +144,40 @@ let gBrowserThumbnails = {
     let channel = aBrowser.docShell.currentDocumentChannel;
 
     // No valid document channel. We shouldn't take a screenshot.
     if (!channel)
       return false;
 
     // Don't take screenshots of internally redirecting about: pages.
     // This includes error pages.
-    if (channel.originalURI.schemeIs("about"))
+    let uri = channel.originalURI;
+    if (uri.schemeIs("about"))
       return false;
 
+    let httpChannel;
     try {
-      // If the channel is a nsIHttpChannel get its http status code.
-      let httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
+      httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
+    } catch (e) { /* Not an HTTP channel. */ }
 
+    if (httpChannel) {
       // 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;
+      if (Math.floor(httpChannel.responseStatus / 100) != 2)
+        return false;
+
+      // Cache-Control: no-store.
+      if (httpChannel.isNoStoreResponse())
+        return false;
+
+      // Don't capture HTTPS pages unless the user explicitly enabled it.
+      if (uri.schemeIs("https") && !this._sslDiskCacheEnabled)
+        return false;
     }
+
+    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.xul
+++ b/browser/base/content/browser.xul
@@ -1073,16 +1073,17 @@
 #endif
     </toolbar>
 
     <toolbar id="developer-toolbar"
              class="devtools-toolbar"
              hidden="true">
 #ifdef XP_MACOSX
           <toolbarbutton id="developer-toolbar-closebutton"
+                         class="devtools-closebutton"
                          oncommand="DeveloperToolbar.hide();"
                          tooltiptext="&devToolbarCloseButton.tooltiptext;"/>
 #endif
           <stack class="gclitoolbar-stack-node" flex="1">
             <description class="gclitoolbar-prompt">&#187;</description>
             <description class="gclitoolbar-complete-node"/>
             <textbox class="gclitoolbar-input-node" rows="1"/>
           </stack>
@@ -1097,16 +1098,17 @@
                          command="Tools:Inspect"/>
           <toolbarbutton id="developer-toolbar-debugger"
                          label="&scriptsButton.label;"
                          class="devtools-toolbarbutton"
                          hidden="true"
                          command="Tools:Debugger"/>
 #ifndef XP_MACOSX
           <toolbarbutton id="developer-toolbar-closebutton"
+                         class="devtools-closebutton"
                          oncommand="DeveloperToolbar.hide();"
                          tooltiptext="&devToolbarCloseButton.tooltiptext;"/>
 #endif
    </toolbar>
 
     <toolbar id="addon-bar"
              toolbarname="&addonBarCmd.label;" accesskey="&addonBarCmd.accesskey;"
              collapsed="true"
--- a/browser/base/content/highlighter.css
+++ b/browser/base/content/highlighter.css
@@ -106,18 +106,21 @@ html|*#highlighter-nodeinfobar-tagname {
   display: none;
 }
 
 #inspector-option-toolbarbutton > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
 #inspector-layoutview-container > iframe {
-  -moz-transition-property: height;
-  -moz-transition-duration: 0.1s;
   /* header size */
   height: 22px;
 }
 
+#inspector-layoutview-container:not([disable-transitions]) > iframe {
+  -moz-transition-property: height;
+  -moz-transition-duration: 0.2s;
+}
+
 #inspector-layoutview-container > iframe[open] {
   /* header size + layout view size: 22px + 155px */
   height: 177px;
 }
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -3801,17 +3801,16 @@
                      class="tab-icon-image"
                      role="presentation"/>
           <xul:label flex="1"
                      xbl:inherits="value=label,crop,accesskey,fadein,pinned,selected"
                      class="tab-text tab-label"
                      role="presentation"/>
           <xul:toolbarbutton anonid="close-button"
                              xbl:inherits="fadein,pinned,selected"
-                             clickthrough="never"
                              class="tab-close-button"/>
         </xul:hbox>
       </xul:stack>
     </content>
 
     <implementation>
       <property name="pinned" readonly="true">
         <getter>
--- a/browser/components/thumbnails/test/Makefile.in
+++ b/browser/components/thumbnails/test/Makefile.in
@@ -8,19 +8,21 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = browser/components/thumbnails/test
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
 	browser_thumbnails_capture.js \
+	browser_thumbnails_privacy.js \
 	browser_thumbnails_redirect.js \
 	browser_thumbnails_storage.js \
 	browser_thumbnails_bug726727.js \
 	browser_thumbnails_bug753755.js \
 	head.js \
 	background_red.html \
 	background_red_redirect.sjs \
+	privacy_cache_control.sjs \
 	$(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_privacy.js
@@ -0,0 +1,66 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const PREF_DISK_CACHE_SSL = "browser.cache.disk_cache_ssl";
+const URL = "://example.com/browser/browser/components/thumbnails/" +
+            "test/privacy_cache_control.sjs";
+
+function runTests() {
+  registerCleanupFunction(function () {
+    Services.prefs.clearUserPref(PREF_DISK_CACHE_SSL);
+  });
+
+  let positive = [
+    // A normal HTTP page without any Cache-Control header.
+    {scheme: "http", cacheControl: null, diskCacheSSL: false},
+
+    // A normal HTTP page with 'Cache-Control: private'.
+    {scheme: "http", cacheControl: "private", diskCacheSSL: false},
+
+    // Capture HTTPS pages if browser.cache.disk_cache_ssl == true.
+    {scheme: "https", cacheControl: null, diskCacheSSL: true},
+    {scheme: "https", cacheControl: "public", diskCacheSSL: true},
+    {scheme: "https", cacheControl: "private", diskCacheSSL: true}
+  ];
+
+  let negative = [
+    // Never capture pages with 'Cache-Control: no-store'.
+    {scheme: "http", cacheControl: "no-store", diskCacheSSL: false},
+    {scheme: "http", cacheControl: "no-store", diskCacheSSL: true},
+    {scheme: "https", cacheControl: "no-store", diskCacheSSL: false},
+    {scheme: "https", cacheControl: "no-store", diskCacheSSL: true},
+
+    // Don't capture HTTPS pages by default.
+    {scheme: "https", cacheControl: null, diskCacheSSL: false},
+    {scheme: "https", cacheControl: "public", diskCacheSSL: false},
+    {scheme: "https", cacheControl: "private", diskCacheSSL: false}
+  ];
+
+  yield checkCombinations(positive, true);
+  yield checkCombinations(negative, false);
+}
+
+function checkCombinations(aCombinations, aResult) {
+  let combi = aCombinations.shift();
+  if (!combi) {
+    next();
+    return;
+  }
+
+  let url = combi.scheme + URL;
+  if (combi.cacheControl)
+    url += "?" + combi.cacheControl;
+  Services.prefs.setBoolPref(PREF_DISK_CACHE_SSL, combi.diskCacheSSL);
+
+  let tab = gBrowser.selectedTab = gBrowser.addTab(url);
+  let browser = gBrowser.selectedBrowser;
+
+  whenLoaded(browser, function () {
+    let msg = JSON.stringify(combi) + " == " + aResult;
+    is(gBrowserThumbnails._shouldCapture(browser), aResult, msg);
+    gBrowser.removeTab(tab);
+
+    // Continue with the next combination.
+    checkCombinations(aCombinations, aResult);
+  });
+}
--- a/browser/components/thumbnails/test/head.js
+++ b/browser/components/thumbnails/test/head.js
@@ -112,60 +112,32 @@ function captureAndCheckColor(aRed, aGre
  * @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 checkThumbnailColor(aURL, aRed, aGreen, aBlue, aMessage) {
   let width = 100, height = 100;
   let thumb = PageThumbs.getThumbnailURL(aURL, width, height);
 
-  getXULDocument(function (aDocument) {
-    let htmlns = "http://www.w3.org/1999/xhtml";
-    let img = aDocument.createElementNS(htmlns, "img");
-    img.setAttribute("src", thumb);
-
-    whenLoaded(img, function () {
-      let canvas = aDocument.createElementNS(htmlns, "canvas");
-      canvas.setAttribute("width", width);
-      canvas.setAttribute("height", height);
-
-      // 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, aRed, aGreen, aBlue, aMessage);
-
-      next();
-    });
-  });
-}
+  let htmlns = "http://www.w3.org/1999/xhtml";
+  let img = document.createElementNS(htmlns, "img");
+  img.setAttribute("src", thumb);
 
-/**
- * Passes a XUL document (created if necessary) to the given callback.
- * @param aCallback The function to be called when the XUL document has been
- *                  created. The first argument will be the document.
- */
-function getXULDocument(aCallback) {
-  let hiddenWindow = Services.appShell.hiddenDOMWindow;
-  let doc = cachedXULDocument || hiddenWindow.document;
+  whenLoaded(img, function () {
+    let canvas = document.createElementNS(htmlns, "canvas");
+    canvas.setAttribute("width", width);
+    canvas.setAttribute("height", height);
 
-  if (doc instanceof XULDocument) {
-    aCallback(cachedXULDocument = doc);
-    return;
-  }
-
-  let iframe = doc.createElement("iframe");
-  iframe.setAttribute("src", "chrome://global/content/mozilla.xhtml");
+    // 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, aRed, aGreen, aBlue, aMessage);
 
-  iframe.addEventListener("DOMContentLoaded", function onLoad() {
-    iframe.removeEventListener("DOMContentLoaded", onLoad, false);
-    aCallback(cachedXULDocument = iframe.contentDocument);
-  }, false);
-
-  doc.body.appendChild(iframe);
-  registerCleanupFunction(function () { doc.body.removeChild(iframe); });
+    next();
+  });
 }
 
 /**
  * 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.
new file mode 100644
--- /dev/null
+++ b/browser/components/thumbnails/test/privacy_cache_control.sjs
@@ -0,0 +1,16 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function handleRequest(aRequest, aResponse) {
+  // Set HTTP Status
+  aResponse.setStatusLine(aRequest.httpVersion, 200, "OK");
+
+  // Set Cache-Control header.
+  let value = aRequest.queryString;
+  if (value)
+    aResponse.setHeader("Cache-Control", value);
+
+  // Set the response body.
+  aResponse.write("<!DOCTYPE html><html><body></body></html>");
+}
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/linux32/debug-asan
@@ -0,0 +1,18 @@
+# Use at least -O1 for optimization to avoid stack space
+# exhaustions caused by Clang function inlining.
+ac_add_options --enable-debug
+ac_add_options --enable-optimize="-O1"
+
+# ASan specific options on Linux
+ac_add_options --enable-valgrind
+
+. $topsrcdir/build/unix/mozconfig.asan
+
+# Avoid dependency on libstdc++ 4.5
+ac_add_options --enable-stdcxx-compat
+
+# Enable parallel compiling
+mk_add_options MOZ_MAKE_FLAGS="-j4"
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/linux32/nightly-asan
@@ -0,0 +1,20 @@
+# We still need to build with debug symbols
+ac_add_options --disable-debug
+ac_add_options --enable-optimize="-O2 -g"
+
+# ASan specific options on Linux
+ac_add_options --enable-valgrind
+
+# Other options
+ac_add_options --enable-codesighs
+
+. $topsrcdir/build/unix/mozconfig.asan
+
+# Avoid dependency on libstdc++ 4.5
+ac_add_options --enable-stdcxx-compat
+
+# Enable parallel compiling
+mk_add_options MOZ_MAKE_FLAGS="-j4"
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/linux64/debug-asan
@@ -0,0 +1,18 @@
+# Use at least -O1 for optimization to avoid stack space
+# exhaustions caused by Clang function inlining.
+ac_add_options --enable-debug
+ac_add_options --enable-optimize="-O1"
+
+# ASan specific options on Linux
+ac_add_options --enable-valgrind
+
+. $topsrcdir/build/unix/mozconfig.asan
+
+# Avoid dependency on libstdc++ 4.5
+ac_add_options --enable-stdcxx-compat
+
+# Enable parallel compiling
+mk_add_options MOZ_MAKE_FLAGS="-j4"
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/linux64/nightly-asan
@@ -0,0 +1,20 @@
+# We still need to build with debug symbols
+ac_add_options --disable-debug
+ac_add_options --enable-optimize="-O2 -g"
+
+# ASan specific options on Linux
+ac_add_options --enable-valgrind
+
+# Other options
+ac_add_options --enable-codesighs
+
+. $topsrcdir/build/unix/mozconfig.asan
+
+# Avoid dependency on libstdc++ 4.5
+ac_add_options --enable-stdcxx-compat
+
+# Enable parallel compiling
+mk_add_options MOZ_MAKE_FLAGS="-j4"
+
+# Package js shell.
+export MOZ_PACKAGE_JSSHELL=1
--- a/browser/devtools/highlighter/inspector.jsm
+++ b/browser/devtools/highlighter/inspector.jsm
@@ -1500,16 +1500,20 @@ InspectorStyleSidebar.prototype = {
     btn.setAttribute("type", "radio");
     btn.setAttribute("group", "sidebar-tools");
     this._toolbar.appendChild(btn);
 
     // create tool iframe
     let frame = this._chromeDoc.createElement("iframe");
     frame.setAttribute("flex", "1");
     frame._toolID = aRegObj.id;
+
+    // This is needed to enable tooltips inside the iframe document.
+    frame.setAttribute("tooltip", "aHTMLTooltip");
+
     this._deck.appendChild(frame);
 
     // wire up button to show the iframe
     let onClick = function() {
       this.activatePanel(aRegObj.id);
     }.bind(this);
     btn.addEventListener("click", onClick, true);
 
--- a/browser/devtools/layoutview/LayoutView.jsm
+++ b/browser/devtools/layoutview/LayoutView.jsm
@@ -92,16 +92,19 @@ LayoutView.prototype = {
     this.onLock = onLock.bind(this);
     this.onUnlock = onUnlock.bind(this);
     this.inspector.on("locked", this.onLock);
     this.inspector.on("unlocked", this.onUnlock);
 
     // Build the layout view in the sidebar.
     this.buildView();
 
+    this.bound_handleKeypress = this.handleKeypress.bind(this);
+    this.iframe.addEventListener("keypress", this.bound_handleKeypress, true);
+
     // Get messages from the iframe.
     this.inspector.chromeWindow.addEventListener("message", this.onMessage, true);
 
     // Store for the different dimensions of the node.
     // 'selector' refers to the element that holds the value in view.xhtml;
     // 'property' is what we are measuring;
     // 'value' is the computed dimension, computed in update().
     this.map = {
@@ -146,16 +149,17 @@ LayoutView.prototype = {
 
   /**
    * Destroy the nodes. Remove listeners.
    */
   destroy: function LV_destroy() {
     this.inspector.removeListener("locked", this.onLock);
     this.inspector.removeListener("unlocked", this.onUnlock);
     this.browser.removeEventListener("MozAfterPaint", this.update, true);
+    this.iframe.removeEventListener("keypress", this.bound_handleKeypress, true);
     this.inspector.chromeWindow.removeEventListener("message", this.onMessage, true);
     this.close();
     this.iframe = null;
     this.view.parentNode.removeChild(this.view);
   },
 
   /**
    * Build the Layout container:
@@ -211,28 +215,53 @@ LayoutView.prototype = {
         this.onDocumentReady();
         break;
       default:
         break;
     }
   },
 
   /**
+   * Handle keypress.
+   */
+   handleKeypress: function LV_handleKeypress(event) {
+     let win = this.inspector.chromeWindow;
+
+     // avoid scroll
+     if (event.keyCode == win.KeyEvent.DOM_VK_LEFT ||
+         event.keyCode == win.KeyEvent.DOM_VK_RIGHT ||
+         event.keyCode == win.KeyEvent.DOM_VK_UP ||
+         event.keyCode == win.KeyEvent.DOM_VK_DOWN ||
+         event.keyCode == win.KeyEvent.DOM_VK_PAGE_UP ||
+         event.keyCode == win.KeyEvent.DOM_VK_PAGE_DOWN) {
+
+        event.preventDefault();
+     }
+
+     if (event.charCode == win.KeyEvent.DOM_VK_SPACE) {
+       this.toggle(true);
+     }
+   },
+
+  /**
    * Open the view container.
    *
    * @param aUserAction Is the action triggered by the user (click on the
    * open/close button in the view)
    */
   open: function LV_open(aUserAction) {
     this.isOpen = true;
     if (this.documentReady)
       this.doc.body.classList.add("open");
     if (aUserAction) {
       this.inspector._layoutViewIsOpen = true;
       Services.prefs.setBoolPref("devtools.layoutview.open", true);
+      this.view.removeAttribute("disable-transitions");
+    } else {
+      this.view.setAttribute("disable-transitions", "true");
     }
     this.iframe.setAttribute("open", "true");
     this.update();
   },
 
   /**
    * Close the view container.
    *
@@ -241,16 +270,19 @@ LayoutView.prototype = {
    */
   close: function LV_close(aUserAction) {
     this.isOpen = false;
     if (this.documentReady)
       this.doc.body.classList.remove("open");
     if (aUserAction) {
       this.inspector._layoutViewIsOpen = false;
       Services.prefs.setBoolPref("devtools.layoutview.open", false);
+      this.view.removeAttribute("disable-transitions");
+    } else {
+      this.view.setAttribute("disable-transitions", "true");
     }
     this.iframe.removeAttribute("open");
   },
 
   /**
    * Toggle view container state (open/close).
    *
    * @param aUserAction Is the action triggered by the user (click on the
--- a/browser/devtools/shared/test/browser_gcli_web.js
+++ b/browser/devtools/shared/test/browser_gcli_web.js
@@ -93,66 +93,71 @@ define('gclitest/index', ['require', 'ex
 
 });
 /*
  * Copyright 2009-2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE.txt or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
 
-define('gclitest/suite', ['require', 'exports', 'module' , 'gcli/index', 'test/examiner', 'gclitest/testCli', 'gclitest/testCompletion', 'gclitest/testExec', 'gclitest/testHistory', 'gclitest/testJs', 'gclitest/testKeyboard', 'gclitest/testRequire', 'gclitest/testResource', 'gclitest/testScratchpad', 'gclitest/testSpell', 'gclitest/testSplit', 'gclitest/testTokenize', 'gclitest/testTooltip', 'gclitest/testTypes', 'gclitest/testUtil'], function(require, exports, module) {
+define('gclitest/suite', ['require', 'exports', 'module' , 'gcli/index', 'test/examiner', 'gclitest/testCanon', 'gclitest/testCli', 'gclitest/testCompletion', 'gclitest/testExec', 'gclitest/testHelp', 'gclitest/testHistory', 'gclitest/testInputter', 'gclitest/testIntro', 'gclitest/testJs', 'gclitest/testKeyboard', 'gclitest/testPref', 'gclitest/testRequire', 'gclitest/testResource', 'gclitest/testScratchpad', 'gclitest/testSettings', 'gclitest/testSpell', 'gclitest/testSplit', 'gclitest/testTokenize', 'gclitest/testTooltip', 'gclitest/testTypes', 'gclitest/testUtil'], function(require, exports, module) {
 
   // We need to make sure GCLI is initialized before we begin testing it
   require('gcli/index');
 
   var examiner = require('test/examiner');
 
   // It's tempting to want to unify these strings and make addSuite() do the
   // call to require(), however that breaks the build system which looks for
   // the strings passed to require
+  examiner.addSuite('gclitest/testCanon', require('gclitest/testCanon'));
   examiner.addSuite('gclitest/testCli', require('gclitest/testCli'));
   examiner.addSuite('gclitest/testCompletion', require('gclitest/testCompletion'));
   examiner.addSuite('gclitest/testExec', require('gclitest/testExec'));
+  examiner.addSuite('gclitest/testHelp', require('gclitest/testHelp'));
   examiner.addSuite('gclitest/testHistory', require('gclitest/testHistory'));
+  examiner.addSuite('gclitest/testInputter', require('gclitest/testInputter'));
+  examiner.addSuite('gclitest/testIntro', require('gclitest/testIntro'));
   examiner.addSuite('gclitest/testJs', require('gclitest/testJs'));
   examiner.addSuite('gclitest/testKeyboard', require('gclitest/testKeyboard'));
+  examiner.addSuite('gclitest/testPref', require('gclitest/testPref'));
   examiner.addSuite('gclitest/testRequire', require('gclitest/testRequire'));
   examiner.addSuite('gclitest/testResource', require('gclitest/testResource'));
   examiner.addSuite('gclitest/testScratchpad', require('gclitest/testScratchpad'));
+  examiner.addSuite('gclitest/testSettings', require('gclitest/testSettings'));
   examiner.addSuite('gclitest/testSpell', require('gclitest/testSpell'));
   examiner.addSuite('gclitest/testSplit', require('gclitest/testSplit'));
   examiner.addSuite('gclitest/testTokenize', require('gclitest/testTokenize'));
   examiner.addSuite('gclitest/testTooltip', require('gclitest/testTooltip'));
   examiner.addSuite('gclitest/testTypes', require('gclitest/testTypes'));
   examiner.addSuite('gclitest/testUtil', require('gclitest/testUtil'));
 
   exports.examiner = examiner;
 });
 /*
  * Copyright 2009-2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE.txt or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
 
-define('test/examiner', ['require', 'exports', 'module' ], function(require, exports, module) {
+define('test/examiner', ['require', 'exports', 'module' , 'test/assert'], function(require, exports, module) {
 var examiner = exports;
 
+var assert = require('test/assert');
 
 /**
  * Test harness data
  */
 examiner.suites = {};
 
 /**
  * The gap between tests when running async
  */
 var delay = 10;
 
-var currentTest = null;
-
 var stati = {
   notrun: { index: 0, name: 'Skipped' },
   executing: { index: 1, name: 'Executing' },
   asynchronous: { index: 2, name: 'Waiting' },
   pass: { index: 3, name: 'Pass' },
   fail: { index: 4, name: 'Fail' }
 };
 
@@ -285,66 +290,30 @@ examiner.detailedResultLog = function() 
             ', checks=' + suite.checks + ')');
 
     Object.keys(suite.tests).forEach(function(testName) {
       var test = suite.tests[testName];
       if (test.status !== stati.pass || test.failures.length !== 0) {
         console.log('- ' + test.name + ': ' + test.status.name);
         test.failures.forEach(function(failure) {
           console.log('  - ' + failure.message);
-          if (failure.expected) {
-            console.log('    - Expected: ' + failure.expected);
-            console.log('    -   Actual: ' + failure.actual);
+          if (failure.params) {
+            console.log('    - P1: ' + failure.p1);
+            console.log('    - P2: ' + failure.p2);
           }
         }.bind(this));
       }
     }.bind(this));
   }.bind(this));
 
   console.log();
   console.log('Summary: ' + this.status.name + ' (' + this.checks + ' checks)');
 };
 
 /**
- * Used by assert to record a failure against the current test
- * @param failure A set of properties describing the failure. Properties include:
- * - message (string, required) A message describing the test
- * - expected (optional) The expected data
- * - actual (optional) The actual data
- */
-examiner.recordFailure = function(failure) {
-  if (!currentTest) {
-    console.error('No currentTest for ' + failure.message);
-    return;
-  }
-
-  currentTest.status = stati.fail;
-  currentTest.failures.push(failure);
-};
-
-/**
- * Used by assert to record a check pass
- */
-examiner.recordPass = function() {
-  if (!currentTest) {
-    console.error('No currentTest');
-    return;
-  }
-
-  currentTest.checks++;
-};
-
-/**
- * When we want to note something alongside a test
- */
-examiner.log = function(message) {
-  currentTest.failures.push({ message: message });
-};
-
-/**
  * A suite is a group of tests
  */
 function Suite(suiteName, suite) {
   this.name = suiteName.replace(/gclitest\//, '');
   this.suite = suite;
 
   this.tests = {};
   Object.keys(suite).forEach(function(testName) {
@@ -457,17 +426,17 @@ Suite.prototype._setup = function(option
     return true;
   }
 
   try {
     this.suite.setup(options);
     return true;
   }
   catch (ex) {
-    this._logToAllTests(stati.notrun, '' + ex);
+    this._logToAllTests('' + ex);
     console.error(ex);
     if (ex.stack) {
       console.error(ex.stack);
     }
     return false;
   }
 };
 
@@ -479,34 +448,35 @@ Suite.prototype._shutdown = function(opt
     return true;
   }
 
   try {
     this.suite.shutdown(options);
     return true;
   }
   catch (ex) {
-    this._logToAllTests(stati.fail, '' + ex);
+    this._logToAllTests('' + ex);
     console.error(ex);
     if (ex.stack) {
       console.error(ex.stack);
     }
     return false;
   }
 };
 
 /**
  * Something has gone wrong that affects all tests in this Suite
  */
-Suite.prototype._logToAllTests = function(status, message) {
+Suite.prototype._logToAllTests = function(message) {
+  var priorCurrentTest = assert.currentTest;
   Object.keys(this.tests).forEach(function(testName) {
-    var test = this.tests[testName];
-    test.status = status;
-    test.failures.push({ message: message });
+    assert.currentTest = this.tests[testName];
+    assert.ok(false, message);
   }.bind(this));
+  assert.currentTest = priorCurrentTest;
 };
 
 
 /**
  * A test represents data about a single test function
  */
 function Test(suite, name, func) {
   this.suite = suite;
@@ -518,38 +488,37 @@ function Test(suite, name, func) {
   this.status = stati.notrun;
   this.checks = 0;
 }
 
 /**
  * Run just a single test
  */
 Test.prototype.run = function(options) {
-  currentTest = this;
+  assert.currentTest = this;
   this.status = stati.executing;
   this.failures = [];
   this.checks = 0;
 
   try {
     this.func.apply(this.suite, [ options ]);
   }
   catch (ex) {
-    this.status = stati.fail;
-    this.failures.push({ message: '' + ex });
+    assert.ok(false, '' + ex);
     console.error(ex);
-    if (ex.stack) {
+    if ((options.isNode || options.isFirefox) && ex.stack) {
       console.error(ex.stack);
     }
   }
 
   if (this.status === stati.executing) {
     this.status = stati.pass;
   }
 
-  currentTest = null;
+  assert.currentTest = null;
 };
 
 /**
  * Run all the tests in this suite asynchronously
  */
 Test.prototype.runAsync = function(options, callback) {
   setTimeout(function() {
     this.run(options);
@@ -575,31 +544,316 @@ Test.prototype.toRemote = function() {
 
 });
 /*
  * Copyright 2009-2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE.txt or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
 
-define('gclitest/testCli', ['require', 'exports', 'module' , 'gcli/cli', 'gcli/types', 'gclitest/commands', 'test/assert'], function(require, exports, module) {
+define('test/assert', ['require', 'exports', 'module' ], function(require, exports, module) {
+
+  exports.ok = ok;
+  exports.is = is;
+  exports.log = info;
+
+});
+/*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+define('gclitest/testCanon', ['require', 'exports', 'module' , 'gclitest/helpers', 'gcli/canon', 'test/assert'], function(require, exports, module) {
+
+  var helpers = require('gclitest/helpers');
+  var canon = require('gcli/canon');
+  var test = require('test/assert');
+
+  exports.testAddRemove = function(options) {
+    var startCount = canon.getCommands().length;
+    var events = 0;
+
+    var canonChange = function(ev) {
+      events++;
+    };
+    canon.onCanonChange.add(canonChange);
+
+    canon.addCommand({
+      name: 'testadd',
+      exec: function() {
+        return 1;
+      }
+    });
+
+    test.is(canon.getCommands().length, startCount + 1, 'add command success');
+    test.is(events, 1, 'add event');
+    helpers.exec(options, {
+      typed: 'testadd',
+      outputMatch: /^1$/
+    });
+
+    canon.addCommand({
+      name: 'testadd',
+      exec: function() {
+        return 2;
+      }
+    });
+
+    test.is(canon.getCommands().length, startCount + 1, 'readd command success');
+    test.is(events, 2, 'readd event');
+    helpers.exec(options, {
+      typed: 'testadd',
+      outputMatch: /^2$/
+    });
+
+    canon.removeCommand('testadd');
+
+    test.is(canon.getCommands().length, startCount, 'remove command success');
+    test.is(events, 3, 'remove event');
+    helpers.status(options, {
+      typed: 'testadd',
+      status: 'ERROR'
+    });
+
+    canon.addCommand({
+      name: 'testadd',
+      exec: function() {
+        return 3;
+      }
+    });
+
+    test.is(canon.getCommands().length, startCount + 1, 'rereadd command success');
+    test.is(events, 4, 'rereadd event');
+    helpers.exec(options, {
+      typed: 'testadd',
+      outputMatch: /^3$/
+    });
+
+    canon.removeCommand({
+      name: 'testadd'
+    });
+
+    test.is(canon.getCommands().length, startCount, 'reremove command success');
+    test.is(events, 5, 'reremove event');
+    helpers.status(options, {
+      typed: 'testadd',
+      status: 'ERROR'
+    });
+
+    canon.onCanonChange.remove(canonChange);
+  };
+
+});
+/*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+define('gclitest/helpers', ['require', 'exports', 'module' , 'test/assert'], function(require, exports, module) {
+
+
+var test = require('test/assert');
+
+/**
+ * Check that we can parse command input.
+ * Doesn't execute the command, just checks that we grok the input properly:
+ *
+ * helpers.status({
+ *   // Test inputs
+ *   typed: "ech",           // Required
+ *   cursor: 3,              // Optional cursor position
+ *
+ *   // Thing to check
+ *   status: "INCOMPLETE",   // One of "VALID", "ERROR", "INCOMPLETE"
+ *   emptyParameters: [ "<message>" ], // Still to type
+ *   directTabText: "o",     // Simple completion text
+ *   arrowTabText: "",       // When the completion is not an extension
+ *   markup: "VVVIIIEEE",    // What state should the error markup be in
+ * });
+ */
+exports.status = function(options, tests) {
+  var requisition = options.display.requisition;
+  var inputter = options.display.inputter;
+  var completer = options.display.completer;
+
+  if (tests.typed) {
+    inputter.setInput(tests.typed);
+  }
+  else {
+    test.ok(false, "Missing typed for " + JSON.stringify(tests));
+    return;
+  }
+
+  if (tests.cursor) {
+    inputter.setCursor(tests.cursor);
+  }
+
+  if (tests.status) {
+    test.is(requisition.getStatus().toString(), tests.status,
+            "status for " + tests.typed);
+  }
+
+  if (tests.emptyParameters != null) {
+    var realParams = completer.emptyParameters;
+    test.is(realParams.length, tests.emptyParameters.length,
+            'emptyParameters.length for \'' + tests.typed + '\'');
+
+    if (realParams.length === tests.emptyParameters.length) {
+      for (var i = 0; i < realParams.length; i++) {
+        test.is(realParams[i].replace(/\u00a0/g, ' '), tests.emptyParameters[i],
+                'emptyParameters[' + i + '] for \'' + tests.typed + '\'');
+      }
+    }
+  }
+
+  if (tests.markup) {
+    var cursor = tests.cursor ? tests.cursor.start : tests.typed.length;
+    var statusMarkup = requisition.getInputStatusMarkup(cursor);
+    var actualMarkup = statusMarkup.map(function(s) {
+      return Array(s.string.length + 1).join(s.status.toString()[0]);
+    }).join('');
+    test.is(tests.markup, actualMarkup, 'markup for ' + tests.typed);
+  }
+
+  if (tests.directTabText) {
+    test.is(completer.directTabText, tests.directTabText,
+            'directTabText for \'' + tests.typed + '\'');
+  }
+
+  if (tests.arrowTabText) {
+    test.is(completer.arrowTabText, ' \u00a0\u21E5 ' + tests.arrowTabText,
+            'arrowTabText for \'' + tests.typed + '\'');
+  }
+};
+
+/**
+ * Execute a command:
+ *
+ * helpers.exec({
+ *   // Test inputs
+ *   typed: "echo hi",        // Optional, uses existing if undefined
+ *
+ *   // Thing to check
+ *   args: { message: "hi" }, // Check that the args were understood properly
+ *   outputMatch: /^hi$/,     // Regex to test against textContent of output
+ *   blankOutput: true,       // Special checks when there is no output
+ * });
+ */
+exports.exec = function(options, tests) {
+  var requisition = options.display.requisition;
+  var inputter = options.display.inputter;
+
+  tests = tests || {};
+
+  if (tests.typed) {
+    inputter.setInput(tests.typed);
+  }
+
+  var typed = inputter.getInputState().typed;
+  var output = requisition.exec({ hidden: true });
+
+  test.is(typed, output.typed, 'output.command for: ' + typed);
+
+  if (tests.completed !== false) {
+    test.ok(output.completed, 'output.completed false for: ' + typed);
+  }
+  else {
+    // It is actually an error if we say something is async and it turns
+    // out not to be? For now we're saying 'no'
+    // test.ok(!output.completed, 'output.completed true for: ' + typed);
+  }
+
+  if (tests.args != null) {
+    test.is(Object.keys(tests.args).length, Object.keys(output.args).length,
+            'arg count for ' + typed);
+
+    Object.keys(output.args).forEach(function(arg) {
+      var expectedArg = tests.args[arg];
+      var actualArg = output.args[arg];
+
+      if (Array.isArray(expectedArg)) {
+        if (!Array.isArray(actualArg)) {
+          test.ok(false, 'actual is not an array. ' + typed + '/' + arg);
+          return;
+        }
+
+        test.is(expectedArg.length, actualArg.length,
+                'array length: ' + typed + '/' + arg);
+        for (var i = 0; i < expectedArg.length; i++) {
+          test.is(expectedArg[i], actualArg[i],
+                  'member: "' + typed + '/' + arg + '/' + i);
+        }
+      }
+      else {
+        test.is(expectedArg, actualArg, 'typed: "' + typed + '" arg: ' + arg);
+      }
+    });
+  }
+
+  if (!options.window.document.createElement) {
+    test.log('skipping output tests (missing doc.createElement) for ' + typed);
+    return;
+  }
+
+  var div = options.window.document.createElement('div');
+  output.toDom(div);
+  var displayed = div.textContent.trim();
+
+  if (tests.outputMatch) {
+    function doTest(match, against) {
+      if (!match.test(against)) {
+        test.ok(false, "html output for " + typed + " against " + match.source);
+        console.log("Actual textContent");
+        console.log(against);
+      }
+    }
+    if (Array.isArray(tests.outputMatch)) {
+      tests.outputMatch.forEach(function(match) {
+        doTest(match, displayed);
+      });
+    }
+    else {
+      doTest(tests.outputMatch, displayed);
+    }
+  }
+
+  if (tests.blankOutput != null) {
+    if (!/^$/.test(displayed)) {
+      test.ok(false, "html for " + typed + " (textContent sent to info)");
+      console.log("Actual textContent");
+      console.log(displayed);
+    }
+  }
+};
+
+
+});
+/*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+define('gclitest/testCli', ['require', 'exports', 'module' , 'gcli/cli', 'gcli/types', 'gclitest/mockCommands', 'test/assert'], function(require, exports, module) {
 
 
 var Requisition = require('gcli/cli').Requisition;
 var Status = require('gcli/types').Status;
-var commands = require('gclitest/commands');
+var mockCommands = require('gclitest/mockCommands');
 
 var test = require('test/assert');
 
 exports.setup = function() {
-  commands.setup();
+  mockCommands.setup();
 };
 
 exports.shutdown = function() {
-  commands.shutdown();
+  mockCommands.shutdown();
 };
 
 
 var assign1;
 var assign2;
 var assignC;
 var requ;
 var debug = false;
@@ -701,29 +955,29 @@ exports.testTsv = function() {
   test.is(-1, assignC.paramIndex);
   test.is('tsv', requ.commandAssignment.value.name);
 
   update({ typed: 'tsv o', cursor: { start: 5, end: 5 } });
   test.is(        'VVVVI', statuses);
   test.is(Status.ERROR, status);
   test.is(0, assignC.paramIndex);
   test.ok(assignC.getPredictions().length >= 2);
-  test.is(commands.option1, assignC.getPredictions()[0].value);
-  test.is(commands.option2, assignC.getPredictions()[1].value);
+  test.is(mockCommands.option1, assignC.getPredictions()[0].value);
+  test.is(mockCommands.option2, assignC.getPredictions()[1].value);
   test.is('tsv', requ.commandAssignment.value.name);
   test.is('o', assign1.arg.text);
   test.is(undefined, assign1.value);
 
   update({ typed: 'tsv option', cursor: { start: 10, end: 10 } });
   test.is(        'VVVVIIIIII', statuses);
   test.is(Status.ERROR, status);
   test.is(0, assignC.paramIndex);
   test.ok(assignC.getPredictions().length >= 2);
-  test.is(commands.option1, assignC.getPredictions()[0].value);
-  test.is(commands.option2, assignC.getPredictions()[1].value);
+  test.is(mockCommands.option1, assignC.getPredictions()[0].value);
+  test.is(mockCommands.option2, assignC.getPredictions()[1].value);
   test.is('tsv', requ.commandAssignment.value.name);
   test.is('option', assign1.arg.text);
   test.is(undefined, assign1.value);
 
   update({ typed: 'tsv option', cursor: { start: 1, end: 1 } });
   test.is(        'VVVVEEEEEE', statuses);
   test.is(Status.ERROR, status);
   test.is(-1, assignC.paramIndex);
@@ -740,44 +994,44 @@ exports.testTsv = function() {
   test.is('option', assign1.arg.text);
   test.is(undefined, assign1.value);
 
   update({ typed: 'tsv option1', cursor: { start: 11, end: 11 } });
   test.is(        'VVVVVVVVVVV', statuses);
   test.is(Status.ERROR, status);
   test.is('tsv', requ.commandAssignment.value.name);
   test.is('option1', assign1.arg.text);
-  test.is(commands.option1, assign1.value);
+  test.is(mockCommands.option1, assign1.value);
   test.is(0, assignC.paramIndex);
 
   update({ typed: 'tsv option1 ', cursor: { start: 12, end: 12 } });
   test.is(        'VVVVVVVVVVVV', statuses);
   test.is(Status.ERROR, status);
   test.is('tsv', requ.commandAssignment.value.name);
   test.is('option1', assign1.arg.text);
-  test.is(commands.option1, assign1.value);
+  test.is(mockCommands.option1, assign1.value);
   test.is(1, assignC.paramIndex);
 
   update({ typed: 'tsv option1 6', cursor: { start: 13, end: 13 } });
   test.is(        'VVVVVVVVVVVVV', statuses);
   test.is(Status.VALID, status);
   test.is('tsv', requ.commandAssignment.value.name);
   test.is('option1', assign1.arg.text);
-  test.is(commands.option1, assign1.value);
+  test.is(mockCommands.option1, assign1.value);
   test.is('6', assign2.arg.text);
   test.is('6', assign2.value);
   test.is('string', typeof assign2.value);
   test.is(1, assignC.paramIndex);
 
   update({ typed: 'tsv option2 6', cursor: { start: 13, end: 13 } });
   test.is(        'VVVVVVVVVVVVV', statuses);
   test.is(Status.VALID, status);
   test.is('tsv', requ.commandAssignment.value.name);
   test.is('option2', assign1.arg.text);
-  test.is(commands.option2, assign1.value);
+  test.is(mockCommands.option2, assign1.value);
   test.is('6', assign2.arg.text);
   test.is(6, assign2.value);
   test.is('number', typeof assign2.value);
   test.is(1, assignC.paramIndex);
 };
 
 exports.testInvalid = function() {
   update({ typed: 'zxjq', cursor: { start: 4, end: 4 } });
@@ -870,50 +1124,55 @@ exports.testSingleNumber = function() {
 exports.testElement = function(options) {
   update({ typed: 'tse', cursor: { start: 3, end: 3 } });
   test.is(        'VVV', statuses);
   test.is(Status.ERROR, status);
   test.is('tse', requ.commandAssignment.value.name);
   test.ok(assign1.arg.isBlank());
   test.is(undefined, assign1.value);
 
-  update({ typed: 'tse :root', cursor: { start: 9, end: 9 } });
-  test.is(        'VVVVVVVVV', statuses);
-  test.is(Status.VALID, status);
-  test.is('tse', requ.commandAssignment.value.name);
-  test.is(':root', assign1.arg.text);
-  if (!options.window.isFake) {
-    test.is(options.window.document.documentElement, assign1.value);
+  if (!options.isNode) {
+    update({ typed: 'tse :root', cursor: { start: 9, end: 9 } });
+    test.is(        'VVVVVVVVV', statuses);
+    test.is(Status.VALID, status);
+    test.is('tse', requ.commandAssignment.value.name);
+    test.is(':root', assign1.arg.text);
+    if (!options.window.isFake) {
+      test.is(options.window.document.documentElement, assign1.value);
+    }
+
+    if (!options.window.isFake) {
+      var inputElement = options.window.document.getElementById('gcli-input');
+      if (inputElement) {
+        update({ typed: 'tse #gcli-input', cursor: { start: 15, end: 15 } });
+        test.is(        'VVVVVVVVVVVVVVV', statuses);
+        test.is(Status.VALID, status);
+        test.is('tse', requ.commandAssignment.value.name);
+        test.is('#gcli-input', assign1.arg.text);
+        test.is(inputElement, assign1.value);
+      }
+      else {
+        test.log('Skipping test that assumes gcli on the web');
+      }
+    }
+
+    update({ typed: 'tse #gcli-nomatch', cursor: { start: 17, end: 17 } });
+    // This is somewhat debatable because this input can't be corrected simply
+    // by typing so it's and error rather than incomplete, however without
+    // digging into the CSS engine we can't tell that so we default to incomplete
+    test.is(        'VVVVIIIIIIIIIIIII', statuses);
+    test.is(Status.ERROR, status);
+    test.is('tse', requ.commandAssignment.value.name);
+    test.is('#gcli-nomatch', assign1.arg.text);
+    test.is(undefined, assign1.value);
   }
-
-  if (!options.window.isFake) {
-    var inputElement = options.window.document.getElementById('gcli-input');
-    if (inputElement) {
-      update({ typed: 'tse #gcli-input', cursor: { start: 15, end: 15 } });
-      test.is(        'VVVVVVVVVVVVVVV', statuses);
-      test.is(Status.VALID, status);
-      test.is('tse', requ.commandAssignment.value.name);
-      test.is('#gcli-input', assign1.arg.text);
-      test.is(inputElement, assign1.value);
-    }
-    else {
-      test.log('Skipping test that assumes gcli on the web');
-    }
+  else {
+    test.log('Skipping :root test due to jsdom (from isNode)');
   }
 
-  update({ typed: 'tse #gcli-nomatch', cursor: { start: 17, end: 17 } });
-  // This is somewhat debatable because this input can't be corrected simply
-  // by typing so it's and error rather than incomplete, however without
-  // digging into the CSS engine we can't tell that so we default to incomplete
-  test.is(        'VVVVIIIIIIIIIIIII', statuses);
-  test.is(Status.ERROR, status);
-  test.is('tse', requ.commandAssignment.value.name);
-  test.is('#gcli-nomatch', assign1.arg.text);
-  test.is(undefined, assign1.value);
-
   update({ typed: 'tse #', cursor: { start: 5, end: 5 } });
   test.is(        'VVVVE', statuses);
   test.is(Status.ERROR, status);
   test.is('tse', requ.commandAssignment.value.name);
   test.is('#', assign1.arg.text);
   test.is(undefined, assign1.value);
 
   update({ typed: 'tse .', cursor: { start: 5, end: 5 } });
@@ -1025,256 +1284,255 @@ exports.testDeeplyNested = function() {
 
 });
 /*
  * Copyright 2009-2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE.txt or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
 
-define('gclitest/commands', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/util', 'gcli/types/selection', 'gcli/types/basic', 'gcli/types'], function(require, exports, module) {
-var commands = exports;
+define('gclitest/mockCommands', ['require', 'exports', 'module' , 'gcli/canon', 'gcli/util', 'gcli/types/selection', 'gcli/types/basic', 'gcli/types'], function(require, exports, module) {
 
 
 var canon = require('gcli/canon');
 var util = require('gcli/util');
 
 var SelectionType = require('gcli/types/selection').SelectionType;
 var DeferredType = require('gcli/types/basic').DeferredType;
 var types = require('gcli/types');
 
 /**
  * Registration and de-registration.
  */
-commands.setup = function() {
+exports.setup = function() {
   // setup/shutdown need to register/unregister types, however that means we
-  // need to re-initialize commands.option1 and commands.option2 with the
+  // need to re-initialize exports.option1 and exports.option2 with the
   // actual types
-  commands.option1.type = types.getType('string');
-  commands.option2.type = types.getType('number');
-
-  types.registerType(commands.optionType);
-  types.registerType(commands.optionValue);
-
-  canon.addCommand(commands.tsv);
-  canon.addCommand(commands.tsr);
-  canon.addCommand(commands.tse);
-  canon.addCommand(commands.tsj);
-  canon.addCommand(commands.tsb);
-  canon.addCommand(commands.tss);
-  canon.addCommand(commands.tsu);
-  canon.addCommand(commands.tsn);
-  canon.addCommand(commands.tsnDif);
-  canon.addCommand(commands.tsnExt);
-  canon.addCommand(commands.tsnExte);
-  canon.addCommand(commands.tsnExten);
-  canon.addCommand(commands.tsnExtend);
-  canon.addCommand(commands.tsnDeep);
-  canon.addCommand(commands.tsnDeepDown);
-  canon.addCommand(commands.tsnDeepDownNested);
-  canon.addCommand(commands.tsnDeepDownNestedCmd);
-  canon.addCommand(commands.tselarr);
-  canon.addCommand(commands.tsm);
-  canon.addCommand(commands.tsg);
+  exports.option1.type = types.getType('string');
+  exports.option2.type = types.getType('number');
+
+  types.registerType(exports.optionType);
+  types.registerType(exports.optionValue);
+
+  canon.addCommand(exports.tsv);
+  canon.addCommand(exports.tsr);
+  canon.addCommand(exports.tse);
+  canon.addCommand(exports.tsj);
+  canon.addCommand(exports.tsb);
+  canon.addCommand(exports.tss);
+  canon.addCommand(exports.tsu);
+  canon.addCommand(exports.tsn);
+  canon.addCommand(exports.tsnDif);
+  canon.addCommand(exports.tsnExt);
+  canon.addCommand(exports.tsnExte);
+  canon.addCommand(exports.tsnExten);
+  canon.addCommand(exports.tsnExtend);
+  canon.addCommand(exports.tsnDeep);
+  canon.addCommand(exports.tsnDeepDown);
+  canon.addCommand(exports.tsnDeepDownNested);
+  canon.addCommand(exports.tsnDeepDownNestedCmd);
+  canon.addCommand(exports.tselarr);
+  canon.addCommand(exports.tsm);
+  canon.addCommand(exports.tsg);
 };
 
-commands.shutdown = function() {
-  canon.removeCommand(commands.tsv);
-  canon.removeCommand(commands.tsr);
-  canon.removeCommand(commands.tse);
-  canon.removeCommand(commands.tsj);
-  canon.removeCommand(commands.tsb);
-  canon.removeCommand(commands.tss);
-  canon.removeCommand(commands.tsu);
-  canon.removeCommand(commands.tsn);
-  canon.removeCommand(commands.tsnDif);
-  canon.removeCommand(commands.tsnExt);
-  canon.removeCommand(commands.tsnExte);
-  canon.removeCommand(commands.tsnExten);
-  canon.removeCommand(commands.tsnExtend);
-  canon.removeCommand(commands.tsnDeep);
-  canon.removeCommand(commands.tsnDeepDown);
-  canon.removeCommand(commands.tsnDeepDownNested);
-  canon.removeCommand(commands.tsnDeepDownNestedCmd);
-  canon.removeCommand(commands.tselarr);
-  canon.removeCommand(commands.tsm);
-  canon.removeCommand(commands.tsg);
-
-  types.deregisterType(commands.optionType);
-  types.deregisterType(commands.optionValue);
+exports.shutdown = function() {
+  canon.removeCommand(exports.tsv);
+  canon.removeCommand(exports.tsr);
+  canon.removeCommand(exports.tse);
+  canon.removeCommand(exports.tsj);
+  canon.removeCommand(exports.tsb);
+  canon.removeCommand(exports.tss);
+  canon.removeCommand(exports.tsu);
+  canon.removeCommand(exports.tsn);
+  canon.removeCommand(exports.tsnDif);
+  canon.removeCommand(exports.tsnExt);
+  canon.removeCommand(exports.tsnExte);
+  canon.removeCommand(exports.tsnExten);
+  canon.removeCommand(exports.tsnExtend);
+  canon.removeCommand(exports.tsnDeep);
+  canon.removeCommand(exports.tsnDeepDown);
+  canon.removeCommand(exports.tsnDeepDownNested);
+  canon.removeCommand(exports.tsnDeepDownNestedCmd);
+  canon.removeCommand(exports.tselarr);
+  canon.removeCommand(exports.tsm);
+  canon.removeCommand(exports.tsg);
+
+  types.deregisterType(exports.optionType);
+  types.deregisterType(exports.optionValue);
 };
 
 
-commands.option1 = { type: types.getType('string') };
-commands.option2 = { type: types.getType('number') };
+exports.option1 = { type: types.getType('string') };
+exports.option2 = { type: types.getType('number') };
 
 var lastOption = undefined;
 
-commands.optionType = new SelectionType({
+exports.optionType = new SelectionType({
   name: 'optionType',
   lookup: [
-    { name: 'option1', value: commands.option1 },
-    { name: 'option2', value: commands.option2 }
+    { name: 'option1', value: exports.option1 },
+    { name: 'option2', value: exports.option2 }
   ],
   noMatch: function() {
     lastOption = undefined;
   },
   stringify: function(option) {
     lastOption = option;
     return SelectionType.prototype.stringify.call(this, option);
   },
   parse: function(arg) {
     var conversion = SelectionType.prototype.parse.call(this, arg);
     lastOption = conversion.value;
     return conversion;
   }
 });
 
-commands.optionValue = new DeferredType({
+exports.optionValue = new DeferredType({
   name: 'optionValue',
   defer: function() {
     if (lastOption && lastOption.type) {
       return lastOption.type;
     }
     else {
       return types.getType('blank');
     }
   }
 });
 
-commands.onCommandExec = util.createEvent('commands.onCommandExec');
+exports.onCommandExec = util.createEvent('commands.onCommandExec');
 
 function createExec(name) {
   return function(args, context) {
     var data = {
-      command: commands[name],
+      command: exports[name],
       args: args,
       context: context
     };
-    commands.onCommandExec(data);
+    exports.onCommandExec(data);
     return data;
   };
 }
 
-commands.tsv = {
+exports.tsv = {
   name: 'tsv',
   params: [
     { name: 'optionType', type: 'optionType' },
     { name: 'optionValue', type: 'optionValue' }
   ],
   exec: createExec('tsv')
 };
 
-commands.tsr = {
+exports.tsr = {
   name: 'tsr',
   params: [ { name: 'text', type: 'string' } ],
   exec: createExec('tsr')
 };
 
-commands.tse = {
+exports.tse = {
   name: 'tse',
   params: [ { name: 'node', type: 'node' } ],
   exec: createExec('tse')
 };
 
-commands.tsj = {
+exports.tsj = {
   name: 'tsj',
   params: [ { name: 'javascript', type: 'javascript' } ],
   exec: createExec('tsj')
 };
 
-commands.tsb = {
+exports.tsb = {
   name: 'tsb',
   params: [ { name: 'toggle', type: 'boolean' } ],
   exec: createExec('tsb')
 };
 
-commands.tss = {
+exports.tss = {
   name: 'tss',
   exec: createExec('tss')
 };
 
-commands.tsu = {
+exports.tsu = {
   name: 'tsu',
   params: [ { name: 'num', type: { name: 'number', max: 10, min: -5, step: 3 } } ],
   exec: createExec('tsu')
 };
 
-commands.tsn = {
+exports.tsn = {
   name: 'tsn'
 };
 
-commands.tsnDif = {
+exports.tsnDif = {
   name: 'tsn dif',
   params: [ { name: 'text', type: 'string' } ],
   exec: createExec('tsnDif')
 };
 
-commands.tsnExt = {
+exports.tsnExt = {
   name: 'tsn ext',
   params: [ { name: 'text', type: 'string' } ],
   exec: createExec('tsnExt')
 };
 
-commands.tsnExte = {
+exports.tsnExte = {
   name: 'tsn exte',
   params: [ { name: 'text', type: 'string' } ],
   exec: createExec('')
 };
 
-commands.tsnExten = {
+exports.tsnExten = {
   name: 'tsn exten',
   params: [ { name: 'text', type: 'string' } ],
   exec: createExec('tsnExte')
 };
 
-commands.tsnExtend = {
+exports.tsnExtend = {
   name: 'tsn extend',
   params: [ { name: 'text', type: 'string' } ],
   exec: createExec('tsnExtend')
 };
 
-commands.tsnDeep = {
+exports.tsnDeep = {
   name: 'tsn deep',
 };
 
-commands.tsnDeepDown = {
+exports.tsnDeepDown = {
   name: 'tsn deep down',
 };
 
-commands.tsnDeepDownNested = {
+exports.tsnDeepDownNested = {
   name: 'tsn deep down nested',
 };
 
-commands.tsnDeepDownNestedCmd = {
+exports.tsnDeepDownNestedCmd = {
   name: 'tsn deep down nested cmd',
   exec: createExec('tsnDeepDownNestedCmd')
 };
 
-commands.tselarr = {
+exports.tselarr = {
   name: 'tselarr',
   params: [
     { name: 'num', type: { name: 'selection', data: [ '1', '2', '3' ] } },
     { name: 'arr', type: { name: 'array', subtype: 'string' } },
   ],
   exec: createExec('tselarr')
 };
 
-commands.tsm = {
+exports.tsm = {
   name: 'tsm',
   description: 'a 3-param test selection|string|number',
   params: [
     { name: 'abc', type: { name: 'selection', data: [ 'a', 'b', 'c' ] } },
     { name: 'txt', type: 'string' },
     { name: 'num', type: { name: 'number', max: 42, min: 0 } },
   ],
   exec: createExec('tsm')
 };
 
-commands.tsg = {
+exports.tsg = {
   name: 'tsg',
   description: 'a param group test',
   params: [
     { name: 'solo', type: { name: 'selection', data: [ 'aaa', 'bbb', 'ccc' ] } },
     {
       group: 'First',
       params: [
         { name: 'txt1', type: 'string', defaultValue: null },
@@ -1295,42 +1553,29 @@ commands.tsg = {
 
 });
 /*
  * Copyright 2009-2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE.txt or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
 
-define('test/assert', ['require', 'exports', 'module' ], function(require, exports, module) {
-
-  exports.ok = ok;
-  exports.is = is;
-  exports.log = info;
-
-});
-/*
- * Copyright 2009-2011 Mozilla Foundation and contributors
- * Licensed under the New BSD license. See LICENSE.txt or:
- * http://opensource.org/licenses/BSD-3-Clause
- */
-
-define('gclitest/testCompletion', ['require', 'exports', 'module' , 'test/assert', 'gclitest/commands'], function(require, exports, module) {
+define('gclitest/testCompletion', ['require', 'exports', 'module' , 'test/assert', 'gclitest/mockCommands'], function(require, exports, module) {
 
 
 var test = require('test/assert');
-var commands = require('gclitest/commands');
+var mockCommands = require('gclitest/mockCommands');
 
 
 exports.setup = function() {
-  commands.setup();
+  mockCommands.setup();
 };
 
 exports.shutdown = function() {
-  commands.shutdown();
+  mockCommands.shutdown();
 };
 
 
 function type(typed, tests, options) {
   var inputter = options.display.inputter;
   var completer = options.display.completer;
 
   inputter.setInput(typed);
@@ -1513,39 +1758,39 @@ exports.testActivate = function(options)
 
 });
 /*
  * Copyright 2009-2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE.txt or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
 
-define('gclitest/testExec', ['require', 'exports', 'module' , 'gcli/cli', 'gcli/canon', 'gclitest/commands', 'gcli/types/node', 'test/assert'], function(require, exports, module) {
+define('gclitest/testExec', ['require', 'exports', 'module' , 'gcli/cli', 'gcli/canon', 'gclitest/mockCommands', 'gcli/types/node', 'test/assert'], function(require, exports, module) {
 
 
 var Requisition = require('gcli/cli').Requisition;
 var canon = require('gcli/canon');
-var commands = require('gclitest/commands');
+var mockCommands = require('gclitest/mockCommands');
 var nodetype = require('gcli/types/node');
 
 var test = require('test/assert');
 
 var actualExec;
 var actualOutput;
 var hideExec = false;
 
 exports.setup = function() {
-  commands.setup();
-  commands.onCommandExec.add(commandExeced);
+  mockCommands.setup();
+  mockCommands.onCommandExec.add(commandExeced);
   canon.commandOutputManager.onOutput.add(commandOutputed);
 };
 
 exports.shutdown = function() {
-  commands.shutdown();
-  commands.onCommandExec.remove(commandExeced);
+  mockCommands.shutdown();
+  mockCommands.onCommandExec.remove(commandExeced);
   canon.commandOutputManager.onOutput.remove(commandOutputed);
 };
 
 function commandExeced(ev) {
   actualExec = ev;
 }
 
 function commandOutputed(ev) {
@@ -1609,18 +1854,18 @@ function exec(command, expectedArgs) {
 
 
 exports.testExec = function(options) {
   hideExec = options.hideExec;
 
   exec('tss', {});
 
   // Bug 707008 - GCLI deferred types don't work properly
-  exec('tsv option1 10', { optionType: commands.option1, optionValue: '10' });
-  exec('tsv option2 10', { optionType: commands.option2, optionValue: 10 });
+  exec('tsv option1 10', { optionType: mockCommands.option1, optionValue: '10' });
+  exec('tsv option2 10', { optionType: mockCommands.option2, optionValue: 10 });
 
   exec('tsr fred', { text: 'fred' });
   exec('tsr fred bloggs', { text: 'fred bloggs' });
   exec('tsr "fred bloggs"', { text: 'fred bloggs' });
 
   exec('tsb', { toggle: false });
   exec('tsb --toggle', { toggle: true });
 
@@ -1671,16 +1916,105 @@ var mockDoc = {
 
 });
 /*
  * Copyright 2009-2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE.txt or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
 
+define('gclitest/testHelp', ['require', 'exports', 'module' , 'gclitest/helpers'], function(require, exports, module) {
+
+  var helpers = require('gclitest/helpers');
+
+  exports.testHelpStatus = function(options) {
+    helpers.status(options, {
+      typed:  'help',
+      markup: 'VVVV',
+      status: 'VALID',
+      emptyParameters: [ " [search]" ]
+    });
+
+    helpers.status(options, {
+      typed:  'help foo',
+      markup: 'VVVVVVVV',
+      status: 'VALID',
+      emptyParameters: [ ]
+    });
+
+    helpers.status(options, {
+      typed:  'help foo bar',
+      markup: 'VVVVVVVVVVVV',
+      status: 'VALID',
+      emptyParameters: [ ]
+    });
+  };
+
+  exports.testHelpExec = function(options) {
+    if (options.isFirefox) {
+      helpers.exec(options, {
+        typed: 'help',
+        args: { search: null },
+        outputMatch: [
+          /Available Commands/,
+          /Get help/
+        ]
+      });
+    }
+    else {
+      helpers.exec(options, {
+        typed: 'help',
+        args: { search: null },
+        outputMatch: [
+          /Welcome to GCLI/,
+          /Source \(BSD\)/,
+          /Get help/
+        ]
+      });
+    }
+
+    helpers.exec(options, {
+      typed: 'help nomatch',
+      args: { search: 'nomatch' },
+      outputMatch: /Commands starting with 'nomatch':$/
+    });
+
+    helpers.exec(options, {
+      typed: 'help help',
+      args: { search: 'help' },
+      outputMatch: [
+        /Synopsis:/,
+        /Provide help either/,
+        /\(string, optional\)/
+      ]
+    });
+
+    helpers.exec(options, {
+      typed: 'help a b',
+      args: { search: 'a b' },
+      outputMatch: /Commands starting with 'a b':$/
+    });
+
+    helpers.exec(options, {
+      typed: 'help hel',
+      args: { search: 'hel' },
+      outputMatch: [
+        /Commands starting with 'hel':/,
+        /Get help on the available commands/
+      ]
+    });
+  };
+
+});
+/*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
 define('gclitest/testHistory', ['require', 'exports', 'module' , 'test/assert', 'gcli/history'], function(require, exports, module) {
 
 var test = require('test/assert');
 var History = require('gcli/history').History;
 
 exports.setup = function() {
 };
 
@@ -1732,16 +2066,158 @@ exports.testForwardsPastIndex = function
 
 });
 /*
  * Copyright 2009-2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE.txt or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
 
+define('gclitest/testInputter', ['require', 'exports', 'module' , 'gclitest/mockCommands', 'gcli/util', 'test/assert'], function(require, exports, module) {
+
+
+var mockCommands = require('gclitest/mockCommands');
+var KeyEvent = require('gcli/util').KeyEvent;
+
+var test = require('test/assert');
+
+var latestEvent = undefined;
+var latestOutput = undefined;
+var latestData = undefined;
+
+var outputted = function(ev) {
+  function updateData() {
+    latestData = latestOutput.data;
+  }
+
+  if (latestOutput != null) {
+    ev.output.onChange.remove(updateData);
+  }
+
+  latestEvent = ev;
+  latestOutput = ev.output;
+
+  ev.output.onChange.add(updateData);
+};
+
+exports.setup = function(options) {
+  options.display.requisition.commandOutputManager.onOutput.add(outputted);
+  mockCommands.setup();
+};
+
+exports.shutdown = function(options) {
+  mockCommands.shutdown();
+  options.display.requisition.commandOutputManager.onOutput.remove(outputted);
+};
+
+exports.testOutput = function(options) {
+  latestEvent = undefined;
+  latestOutput = undefined;
+  latestData = undefined;
+
+  var inputter = options.display.inputter;
+  var focusManager = options.display.focusManager;
+
+  inputter.setInput('tss');
+
+  inputter.onKeyDown({
+    keyCode: KeyEvent.DOM_VK_RETURN
+  });
+
+  test.is(inputter.element.value, 'tss', 'inputter should do nothing on RETURN keyDown');
+  test.is(latestEvent, undefined, 'no events this test');
+  test.is(latestData, undefined, 'no data this test');
+
+  inputter.onKeyUp({
+    keyCode: KeyEvent.DOM_VK_RETURN
+  });
+
+  test.ok(latestEvent != null, 'events this test');
+  test.is(latestData.command.name, 'tss', 'last command is tss');
+
+  test.is(inputter.element.value, '', 'inputter should exec on RETURN keyUp');
+
+  test.ok(focusManager._recentOutput, 'recent output happened');
+
+  inputter.onKeyUp({
+    keyCode: KeyEvent.DOM_VK_F1
+  });
+
+  test.ok(!focusManager._recentOutput, 'no recent output happened post F1');
+  test.ok(focusManager._helpRequested, 'F1 = help');
+
+  inputter.onKeyUp({
+    keyCode: KeyEvent.DOM_VK_ESCAPE
+  });
+
+  test.ok(!focusManager._helpRequested, 'ESCAPE = anti help');
+
+  latestOutput.onClose();
+};
+
+
+});
+/*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+define('gclitest/testIntro', ['require', 'exports', 'module' , 'gclitest/helpers', 'test/assert'], function(require, exports, module) {
+
+  var helpers = require('gclitest/helpers');
+  var test = require('test/assert');
+
+  exports.testIntroStatus = function(options) {
+    if (options.isFirefox) {
+      test.log('Skipping testIntroStatus in Firefox.');
+      return;
+    }
+
+    helpers.status(options, {
+      typed:  'intro',
+      markup: 'VVVVV',
+      status: 'VALID',
+      emptyParameters: [ ]
+    });
+
+    helpers.status(options, {
+      typed:  'intro foo',
+      markup: 'VVVVVVEEE',
+      status: 'ERROR',
+      emptyParameters: [ ]
+    });
+  };
+
+  exports.testIntroExec = function(options) {
+    if (options.isFirefox) {
+      test.log('Skipping testIntroExec in Firefox.');
+      return;
+    }
+
+    helpers.exec(options, {
+      typed: 'intro',
+      args: { },
+      outputMatch: [
+        /graphical\s*command\s*line/,
+        /GCLI/,
+        /help/,
+        /F1/,
+        /Escape/
+      ]
+    });
+  };
+
+});
+/*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
 define('gclitest/testJs', ['require', 'exports', 'module' , 'gcli/cli', 'gcli/types', 'gcli/types/javascript', 'gcli/canon', 'test/assert'], function(require, exports, module) {
 
 
 var Requisition = require('gcli/cli').Requisition;
 var Status = require('gcli/types').Status;
 var javascript = require('gcli/types/javascript');
 var canon = require('gcli/canon');
 
@@ -1906,42 +2382,42 @@ exports.testBasic = function(options) {
 
 });
 /*
  * Copyright 2009-2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE.txt or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
 
-define('gclitest/testKeyboard', ['require', 'exports', 'module' , 'gcli/cli', 'gcli/canon', 'gclitest/commands', 'gcli/types/javascript', 'test/assert'], function(require, exports, module) {
+define('gclitest/testKeyboard', ['require', 'exports', 'module' , 'gcli/cli', 'gcli/canon', 'gclitest/mockCommands', 'gcli/types/javascript', 'test/assert'], function(require, exports, module) {
 
 
 var Requisition = require('gcli/cli').Requisition;
 var canon = require('gcli/canon');
-var commands = require('gclitest/commands');
+var mockCommands = require('gclitest/mockCommands');
 var javascript = require('gcli/types/javascript');
 
 var test = require('test/assert');
 
 var tempWindow;
 var inputter;
 
 exports.setup = function(options) {
   tempWindow = javascript.getGlobalObject();
   javascript.setGlobalObject(options.window);
 
   if (options.display) {
     inputter = options.display.inputter;
   }
 
-  commands.setup();
+  mockCommands.setup();
 };
 
 exports.shutdown = function(options) {
-  commands.shutdown();
+  mockCommands.shutdown();
 
   inputter = undefined;
   javascript.setGlobalObject(tempWindow);
   tempWindow = undefined;
 };
 
 var COMPLETES_TO = 'complete';
 var KEY_UPS_TO = 'keyup';
@@ -2087,41 +2563,605 @@ exports.testIncrDecr = function() {
 
 });
 /*
  * Copyright 2009-2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE.txt or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
 
+define('gclitest/testPref', ['require', 'exports', 'module' , 'gcli/commands/pref', 'gclitest/helpers', 'gclitest/mockSettings', 'test/assert'], function(require, exports, module) {
+
+
+var pref = require('gcli/commands/pref');
+var helpers = require('gclitest/helpers');
+var mockSettings = require('gclitest/mockSettings');
+var test = require('test/assert');
+
+
+exports.setup = function(options) {
+  if (!options.isFirefox) {
+    mockSettings.setup();
+  }
+  else {
+    test.log('Skipping testPref in Firefox.');
+  }
+};
+
+exports.shutdown = function(options) {
+  if (!options.isFirefox) {
+    mockSettings.shutdown();
+  }
+};
+
+exports.testPrefSetStatus = function(options) {
+  if (options.isFirefox) {
+    test.log('Skipping testPref in Firefox.');
+    return;
+  }
+
+  helpers.status(options, {
+    typed:  'pref s',
+    markup: 'IIIIVI',
+    status: 'ERROR',
+    directTabText: 'et'
+  });
+
+  helpers.status(options, {
+    typed:  'pref set',
+    markup: 'VVVVVVVV',
+    status: 'ERROR',
+    emptyParameters: [ ' <setting>', ' <value>' ]
+  });
+
+  helpers.status(options, {
+    typed:  'pref xxx',
+    markup: 'EEEEVEEE',
+    status: 'ERROR'
+  });
+
+  helpers.status(options, {
+    typed:  'pref set ',
+    markup: 'VVVVVVVVV',
+    status: 'ERROR',
+    emptyParameters: [ ' <value>' ]
+  });
+
+  helpers.status(options, {
+    typed:  'pref set ',
+    markup: 'VVVVVVVVV',
+    status: 'ERROR',
+    emptyParameters: [ ' <value>' ]
+  });
+
+  helpers.status(options, {
+    typed:  'pref set tempTBo',
+    markup: 'VVVVVVVVVIIIIIII',
+    directTabText: 'ol',
+    status: 'ERROR',
+    emptyParameters: [ ' <value>' ]
+  });
+
+  helpers.status(options, {
+    typed:  'pref set tempTBool 4',
+    markup: 'VVVVVVVVVVVVVVVVVVVE',
+    directTabText: '',
+    status: 'ERROR',
+    emptyParameters: [ ]
+  });
+
+  helpers.status(options, {
+    typed:  'pref set tempNumber 4',
+    markup: 'VVVVVVVVVVVVVVVVVVVVV',
+    directTabText: '',
+    status: 'VALID',
+    emptyParameters: [ ]
+  });
+};
+
+exports.testPrefExec = function(options) {
+  if (options.isFirefox) {
+    test.log('Skipping testPref in Firefox.');
+    return;
+  }
+
+  var initialAllowSet = pref.allowSet.value;
+  pref.allowSet.value = false;
+
+  test.is(mockSettings.tempNumber.value, 42, 'set to 42');
+
+  helpers.exec(options, {
+    typed: 'pref set tempNumber 4',
+    args: {
+      setting: mockSettings.tempNumber,
+      value: 4
+    },
+    outputMatch: [ /void your warranty/, /I promise/ ]
+  });
+
+  test.is(mockSettings.tempNumber.value, 42, 'still set to 42');
+  pref.allowSet.value = true;
+
+  helpers.exec(options, {
+    typed: 'pref set tempNumber 4',
+    args: {
+      setting: mockSettings.tempNumber,
+      value: 4
+    },
+    blankOutput: true
+  });
+
+  test.is(mockSettings.tempNumber.value, 4, 'set to 4');
+
+  helpers.exec(options, {
+    typed: 'pref reset tempNumber',
+    args: {
+      setting: mockSettings.tempNumber
+    },
+    blankOutput: true
+  });
+
+  test.is(mockSettings.tempNumber.value, 42, 'reset to 42');
+
+  pref.allowSet.value = initialAllowSet;
+
+  helpers.exec(options, {
+    typed: 'pref list tempNum',
+    args: {
+      search: 'tempNum'
+    },
+    outputMatch: /Filter/
+  });
+};
+
+
+});
+/*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+define('gcli/commands/pref', ['require', 'exports', 'module' , 'gcli/index', 'gcli/l10n', 'gcli/util', 'gcli/settings', 'gcli/promise', 'text!gcli/commands/pref_list_outer.html', 'text!gcli/commands/pref_list.css', 'text!gcli/commands/pref_set_check.html', 'text!gcli/commands/pref_list_inner.html'], function(require, exports, module) {
+
+
+var gcli = require('gcli/index');
+var l10n = require('gcli/l10n');
+var util = require('gcli/util');
+var settings = require('gcli/settings');
+var Promise = require('gcli/promise').Promise;
+
+/**
+ * Record if the user has clicked on 'Got It!'
+ */
+var allowSetSettingSpec = {
+  name: 'allowSet',
+  type: 'boolean',
+  description: l10n.lookup('allowSetDesc'),
+  defaultValue: false
+};
+exports.allowSet = undefined;
+
+/**
+ * 'pref' command
+ */
+var prefCmdSpec = {
+  name: 'pref',
+  description: l10n.lookup('prefDesc'),
+  manual: l10n.lookup('prefManual')
+};
+
+/**
+ * 'pref list' command
+ */
+var prefListCmdSpec = {
+  name: 'pref list',
+  description: l10n.lookup('prefListDesc'),
+  manual: l10n.lookup('prefListManual'),
+  params: [
+    {
+      name: 'search',
+      type: 'string',
+      defaultValue: null,
+      description: l10n.lookup('prefListSearchDesc'),
+      manual: l10n.lookup('prefListSearchManual')
+    }
+  ],
+  exec: function Command_prefList(args, context) {
+    return context.createView({
+      html: require('text!gcli/commands/pref_list_outer.html'),
+      data: new PrefList(args, context),
+      options: {
+        blankNullUndefined: true,
+        allowEval: true,
+        stack: 'pref_list_outer.html'
+      },
+      css: require('text!gcli/commands/pref_list.css'),
+      cssId: 'gcli-pref-list'
+    });
+  }
+};
+
+/**
+ * 'pref set' command
+ */
+var prefSetCmdSpec = {
+  name: 'pref set',
+  description: l10n.lookup('prefSetDesc'),
+  manual: l10n.lookup('prefSetManual'),
+  params: [
+    {
+      name: 'setting',
+      type: 'setting',
+      description: l10n.lookup('prefSetSettingDesc'),
+      manual: l10n.lookup('prefSetSettingManual')
+    },
+    {
+      name: 'value',
+      type: 'settingValue',
+      description: l10n.lookup('prefSetValueDesc'),
+      manual: l10n.lookup('prefSetValueManual')
+    }
+  ],
+  exec: function Command_prefSet(args, context) {
+    if (!exports.allowSet.value &&
+            args.setting.name !== exports.allowSet.name) {
+      return context.createView({
+        html: require('text!gcli/commands/pref_set_check.html'),
+        options: { allowEval: true, stack: 'pref_set_check.html' },
+        data: {
+          l10n: l10n.propertyLookup,
+          activate: function() {
+            context.exec('pref set allowSet true');
+          }
+        },
+      });
+    }
+    args.setting.value = args.value;
+    return null;
+  }
+};
+
+/**
+ * 'pref reset' command
+ */
+var prefResetCmdSpec = {
+  name: 'pref reset',
+  description: l10n.lookup('prefResetDesc'),
+  manual: l10n.lookup('prefResetManual'),
+  params: [
+    {
+      name: 'setting',
+      type: 'setting',
+      description: l10n.lookup('prefResetSettingDesc'),
+      manual: l10n.lookup('prefResetSettingManual')
+    }
+  ],
+  exec: function Command_prefReset(args, context) {
+    args.setting.setDefault();
+    return null;
+  }
+};
+
+/**
+ * Registration and de-registration.
+ */
+exports.startup = function() {
+  exports.allowSet = settings.addSetting(allowSetSettingSpec);
+
+  gcli.addCommand(prefCmdSpec);
+  gcli.addCommand(prefListCmdSpec);
+  gcli.addCommand(prefSetCmdSpec);
+  gcli.addCommand(prefResetCmdSpec);
+};
+
+exports.shutdown = function() {
+  gcli.removeCommand(prefCmdSpec);
+  gcli.removeCommand(prefListCmdSpec);
+  gcli.removeCommand(prefSetCmdSpec);
+  gcli.removeCommand(prefResetCmdSpec);
+
+  settings.removeSetting(allowSetSettingSpec);
+  exports.allowSet = undefined;
+};
+
+
+/**
+ * A manager for our version of about:config
+ */
+function PrefList(args, context) {
+  this.search = args.search;
+  this.context = context;
+  this.url = util.createUrlLookup(module);
+  this.edit = this.url('pref_list_edit.png');
+}
+
+/**
+ *
+ */
+PrefList.prototype.onLoad = function(element) {
+  var table = element.querySelector('.gcli-pref-list-table');
+  this.updateTable(table);
+  return '';
+};
+
+/**
+ * Forward localization lookups
+ */
+PrefList.prototype.l10n = l10n.propertyLookup;
+
+/**
+ * Called from the template onkeyup for the filter element
+ */
+PrefList.prototype.updateTable = function(table) {
+  util.clearElement(table);
+  var view = this.context.createView({
+    html: require('text!gcli/commands/pref_list_inner.html'),
+    options: { blankNullUndefined: true, stack: 'pref_list_inner.html' },
+    data: this
+  });
+  var child = view.toDom(table.ownerDocument);
+  util.setContents(table, child);
+};
+
+/**
+ * Which preferences match the filter?
+ */
+Object.defineProperty(PrefList.prototype, 'preferences', {
+  get: function() {
+    return settings.getAll(this.search);
+  },
+  enumerable: true
+});
+
+/**
+ * Which preferences match the filter?
+ */
+Object.defineProperty(PrefList.prototype, 'promisePreferences', {
+  get: function() {
+    var promise = new Promise();
+    setTimeout(function() {
+      promise.resolve(settings.getAll(this.search));
+    }.bind(this), 10);
+    return promise;
+  },
+  enumerable: true
+});
+
+PrefList.prototype.onFilterChange = function(ev) {
+  if (ev.target.value !== this.search) {
+    this.search = ev.target.value;
+
+    var root = ev.target.parentNode.parentNode;
+    var table = root.querySelector('.gcli-pref-list-table');
+    this.updateTable(table);
+  }
+};
+
+PrefList.prototype.onSetClick = function(ev) {
+  var typed = ev.currentTarget.getAttribute('data-command');
+  this.context.update(typed);
+};
+
+});
+define("text!gcli/commands/pref_list_outer.html", [], "<div ignore=\"${onLoad(__element)}\">\n" +
+  "  <div class=\"gcli-pref-list-filter\">\n" +
+  "    ${l10n.prefOutputFilter}:\n" +
+  "    <input onKeyUp=\"${onFilterChange}\" value=\"${search}\"/>\n" +
+  "  </div>\n" +
+  "  <table class=\"gcli-pref-list-table\">\n" +
+  "    <colgroup>\n" +
+  "      <col class=\"gcli-pref-list-name\"/>\n" +
+  "      <col class=\"gcli-pref-list-value\"/>\n" +
+  "    </colgroup>\n" +
+  "    <tr>\n" +
+  "      <th>${l10n.prefOutputName}</th>\n" +
+  "      <th>${l10n.prefOutputValue}</th>\n" +
+  "    </tr>\n" +
+  "  </table>\n" +
+  "  <div class=\"gcli-pref-list-scroller\">\n" +
+  "    <table class=\"gcli-pref-list-table\" save=\"${table}\">\n" +
+  "    </table>\n" +
+  "  </div>\n" +
+  "</div>\n" +
+  "");
+
+define("text!gcli/commands/pref_list.css", [], "\n" +
+  ".gcli-pref-list-scroller {\n" +
+  "  max-height: 200px;\n" +
+  "  overflow-y: auto;\n" +
+  "  overflow-x: hidden;\n" +
+  "  display: inline-block;\n" +
+  "}\n" +
+  "\n" +
+  ".gcli-pref-list-table {\n" +
+  "  width: 500px;\n" +
+  "  table-layout: fixed;\n" +
+  "}\n" +
+  "\n" +
+  ".gcli-pref-list-table tr > th {\n" +
+  "  text-align: left;\n" +
+  "}\n" +
+  "\n" +
+  ".gcli-pref-list-table tr > td {\n" +
+  "  text-overflow: elipsis;\n" +
+  "  word-wrap: break-word;\n" +
+  "}\n" +
+  "\n" +
+  ".gcli-pref-list-name {\n" +
+  "  width: 70%;\n" +
+  "}\n" +
+  "\n" +
+  ".gcli-pref-list-command {\n" +
+  "  display: none;\n" +
+  "}\n" +
+  "\n" +
+  ".gcli-pref-list-row:hover .gcli-pref-list-command {\n" +
+  "  display: inline-block;\n" +
+  "}\n" +
+  "");
+
+define("text!gcli/commands/pref_set_check.html", [], "<div>\n" +
+  "  <p><strong>${l10n.prefSetCheckHeading}</strong></p>\n" +
+  "  <p>${l10n.prefSetCheckBody}</p>\n" +
+  "  <button onclick=\"${activate}\">${l10n.prefSetCheckGo}</button>\n" +
+  "</div>\n" +
+  "");
+
+define("text!gcli/commands/pref_list_inner.html", [], "<table>\n" +
+  "  <colgroup>\n" +
+  "    <col class=\"gcli-pref-list-name\"/>\n" +
+  "    <col class=\"gcli-pref-list-value\"/>\n" +
+  "  </colgroup>\n" +
+  "  <tr class=\"gcli-pref-list-row\" foreach=\"preference in ${promisePreferences}\">\n" +
+  "    <td>${preference.name}</td>\n" +
+  "    <td onclick=\"${onSetClick}\" data-command=\"pref set ${preference.name} \">\n" +
+  "      ${preference.value}\n" +
+  "      <img class=\"gcli-pref-list-command\" _src=\"${edit}\"/>\n" +
+  "    </td>\n" +
+  "  </tr>\n" +
+  "</table>\n" +
+  "");
+
+/*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+define('gclitest/mockSettings', ['require', 'exports', 'module' , 'gcli/settings'], function(require, exports, module) {
+
+
+var settings = require('gcli/settings');
+
+
+var tempTBoolSpec = {
+  name: 'tempTBool',
+  type: 'boolean',
+  description: 'temporary default true boolean',
+  defaultValue: true
+};
+exports.tempTBool = undefined;
+
+var tempFBoolSpec = {
+  name: 'tempFBool',
+  type: 'boolean',
+  description: 'temporary default false boolean',
+  defaultValue: false
+};
+exports.tempFBool = undefined;
+
+var tempUStringSpec = {
+  name: 'tempUString',
+  type: 'string',
+  description: 'temporary default undefined string'
+};
+exports.tempUString = undefined;
+
+var tempNStringSpec = {
+  name: 'tempNString',
+  type: 'string',
+  description: 'temporary default undefined string',
+  defaultValue: null
+};
+exports.tempNString = undefined;
+
+var tempQStringSpec = {
+  name: 'tempQString',
+  type: 'string',
+  description: 'temporary default "q" string',
+  defaultValue: 'q'
+};
+exports.tempQString = undefined;
+
+var tempNumberSpec = {
+  name: 'tempNumber',
+  type: 'number',
+  description: 'temporary number',
+  defaultValue: 42
+};
+exports.tempNumber = undefined;
+
+var tempSelectionSpec = {
+  name: 'tempSelection',
+  type: { name: 'selection', data: [ 'a', 'b', 'c' ] },
+  description: 'temporary selection',
+  defaultValue: 'a'
+};
+exports.tempSelection = undefined;
+
+/**
+ * Registration and de-registration.
+ */
+exports.setup = function() {
+  exports.tempTBool = settings.addSetting(tempTBoolSpec);
+  exports.tempFBool = settings.addSetting(tempFBoolSpec);
+  exports.tempUString = settings.addSetting(tempUStringSpec);
+  exports.tempNString = settings.addSetting(tempNStringSpec);
+  exports.tempQString = settings.addSetting(tempQStringSpec);
+  exports.tempNumber = settings.addSetting(tempNumberSpec);
+  exports.tempSelection = settings.addSetting(tempSelectionSpec);
+};
+
+exports.shutdown = function() {
+  settings.removeSetting(tempTBoolSpec);
+  settings.removeSetting(tempFBoolSpec);
+  settings.removeSetting(tempUStringSpec);
+  settings.removeSetting(tempNStringSpec);
+  settings.removeSetting(tempQStringSpec);
+  settings.removeSetting(tempNumberSpec);
+  settings.removeSetting(tempSelectionSpec);
+
+  exports.tempTBool = undefined;
+  exports.tempFBool = undefined;
+  exports.tempUString = undefined;
+  exports.tempNString = undefined;
+  exports.tempQString = undefined;
+  exports.tempNumber = undefined;
+  exports.tempSelection = undefined;
+};
+
+
+});
+/*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
 define('gclitest/testRequire', ['require', 'exports', 'module' , 'test/assert', 'gclitest/requirable'], function(require, exports, module) {
 
 var test = require('test/assert');
 
 
 exports.testWorking = function() {
   // There are lots of requirement tests that we could be doing here
   // The fact that we can get anything at all working is a testament to
   // require doing what it should - we don't need to test the
   var requireable = require('gclitest/requirable');
   test.is('thing1', requireable.thing1);
   test.is(2, requireable.thing2);
   test.ok(requireable.thing3 === undefined);
 };
 
-exports.testDomains = function() {
+exports.testDomains = function(options) {
   var requireable = require('gclitest/requirable');
   test.ok(requireable.status === undefined);
   requireable.setStatus(null);
   test.is(null, requireable.getStatus());
   test.ok(requireable.status === undefined);
   requireable.setStatus('42');
   test.is('42', requireable.getStatus());
   test.ok(requireable.status === undefined);
 
+  if (options.isUnamdized) {
+    test.log('Running unamdized, Reduced tests');
+    return;
+  }
+
   if (define.Domain) {
     var domain = new define.Domain();
     var requireable2 = domain.require('gclitest/requirable');
     test.is(undefined, requireable2.status);
     test.is('initial', requireable2.getStatus());
     requireable2.setStatus(999);
     test.is(999, requireable2.getStatus());
     test.is(undefined, requireable2.status);
@@ -2212,18 +3252,18 @@ exports.setup = function(options) {
 };
 
 exports.shutdown = function(options) {
   resource.setDocument(tempDocument);
   tempDocument = undefined;
 };
 
 exports.testPredictions = function(options) {
-  if (options.window.isFake) {
-    test.log('Skipping resource tests: options.window.isFake = true');
+  if (options.window.isFake || options.isFirefox) {
+    test.log('Skipping resource tests: window.isFake || isFirefox');
     return;
   }
 
   var resource1 = types.getType('resource');
   var predictions1 = resource1.parseString('').getPredictions();
   test.ok(predictions1.length > 1, 'have resources');
   predictions1.forEach(function(prediction) {
     checkPrediction(resource1, prediction);
@@ -2323,16 +3363,165 @@ exports.testActivate = function(options)
   stubScratchpad.activatedCount = 0;
   options.display.inputter.onKeyUp(ev);
   test.is(1, stubScratchpad.activatedCount, 'scratchpad is activated');
 };
 
 
 });
 /*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+define('gclitest/testSettings', ['require', 'exports', 'module' , 'gclitest/mockSettings', 'test/assert'], function(require, exports, module) {
+
+
+var mockSettings = require('gclitest/mockSettings');
+var test = require('test/assert');
+
+
+exports.setup = function(options) {
+  if (!options.isFirefox) {
+    mockSettings.setup();
+  }
+  else {
+    test.log('Skipping testSettings in Firefox.');
+  }
+};
+
+exports.shutdown = function(options) {
+  if (!options.isFirefox) {
+    mockSettings.shutdown();
+  }
+};
+
+exports.testChange = function(options) {
+  if (options.isFirefox) {
+    test.log('Skipping testPref in Firefox.');
+    return;
+  }
+
+  mockSettings.tempTBool.setDefault();
+  mockSettings.tempFBool.setDefault();
+  mockSettings.tempUString.setDefault();
+  mockSettings.tempNString.setDefault();
+  mockSettings.tempQString.setDefault();
+  mockSettings.tempNumber.setDefault();
+  mockSettings.tempSelection.setDefault();
+
+  test.is(mockSettings.tempTBool.value, true, 'tempTBool default');
+  test.is(mockSettings.tempFBool.value, false, 'tempFBool default');
+  test.is(mockSettings.tempUString.value, undefined, 'tempUString default');
+  test.is(mockSettings.tempNString.value, null, 'tempNString default');
+  test.is(mockSettings.tempQString.value, 'q', 'tempQString default');
+  test.is(mockSettings.tempNumber.value, 42, 'tempNumber default');
+  test.is(mockSettings.tempSelection.value, 'a', 'tempSelection default');
+
+  function tempTBoolCheck(ev) {
+    test.is(ev.setting, mockSettings.tempTBool, 'tempTBool event setting');
+    test.is(ev.value, false, 'tempTBool event value');
+    test.is(ev.setting.value, false, 'tempTBool event setting value');
+  }
+  mockSettings.tempTBool.onChange.add(tempTBoolCheck);
+  mockSettings.tempTBool.value = false;
+  test.is(mockSettings.tempTBool.value, false, 'tempTBool change');
+
+  function tempFBoolCheck(ev) {
+    test.is(ev.setting, mockSettings.tempFBool, 'tempFBool event setting');
+    test.is(ev.value, true, 'tempFBool event value');
+    test.is(ev.setting.value, true, 'tempFBool event setting value');
+  }
+  mockSettings.tempFBool.onChange.add(tempFBoolCheck);
+  mockSettings.tempFBool.value = true;
+  test.is(mockSettings.tempFBool.value, true, 'tempFBool change');
+
+  function tempUStringCheck(ev) {
+    test.is(ev.setting, mockSettings.tempUString, 'tempUString event setting');
+    test.is(ev.value, 'x', 'tempUString event value');
+    test.is(ev.setting.value, 'x', 'tempUString event setting value');
+  }
+  mockSettings.tempUString.onChange.add(tempUStringCheck);
+  mockSettings.tempUString.value = 'x';
+  test.is(mockSettings.tempUString.value, 'x', 'tempUString change');
+
+  function tempNStringCheck(ev) {
+    test.is(ev.setting, mockSettings.tempNString, 'tempNString event setting');
+    test.is(ev.value, 'y', 'tempNString event value');
+    test.is(ev.setting.value, 'y', 'tempNString event setting value');
+  }
+  mockSettings.tempNString.onChange.add(tempNStringCheck);
+  mockSettings.tempNString.value = 'y';
+  test.is(mockSettings.tempNString.value, 'y', 'tempNString change');
+
+  function tempQStringCheck(ev) {
+    test.is(ev.setting, mockSettings.tempQString, 'tempQString event setting');
+    test.is(ev.value, 'qq', 'tempQString event value');
+    test.is(ev.setting.value, 'qq', 'tempQString event setting value');
+  }
+  mockSettings.tempQString.onChange.add(tempQStringCheck);
+  mockSettings.tempQString.value = 'qq';
+  test.is(mockSettings.tempQString.value, 'qq', 'tempQString change');
+
+  function tempNumberCheck(ev) {
+    test.is(ev.setting, mockSettings.tempNumber, 'tempNumber event setting');
+    test.is(ev.value, -1, 'tempNumber event value');
+    test.is(ev.setting.value, -1, 'tempNumber event setting value');
+  }
+  mockSettings.tempNumber.onChange.add(tempNumberCheck);
+  mockSettings.tempNumber.value = -1;
+  test.is(mockSettings.tempNumber.value, -1, 'tempNumber change');
+
+  function tempSelectionCheck(ev) {
+    test.is(ev.setting, mockSettings.tempSelection, 'tempSelection event setting');
+    test.is(ev.value, 'b', 'tempSelection event value');
+    test.is(ev.setting.value, 'b', 'tempSelection event setting value');
+  }
+  mockSettings.tempSelection.onChange.add(tempSelectionCheck);
+  mockSettings.tempSelection.value = 'b';
+  test.is(mockSettings.tempSelection.value, 'b', 'tempSelection change');
+
+  mockSettings.tempTBool.onChange.remove(tempTBoolCheck);
+  mockSettings.tempFBool.onChange.remove(tempFBoolCheck);
+  mockSettings.tempUString.onChange.remove(tempUStringCheck);
+  mockSettings.tempNString.onChange.remove(tempNStringCheck);
+  mockSettings.tempQString.onChange.remove(tempQStringCheck);
+  mockSettings.tempNumber.onChange.remove(tempNumberCheck);
+  mockSettings.tempSelection.onChange.remove(tempSelectionCheck);
+
+  function tempNStringReCheck(ev) {
+    test.is(ev.setting, mockSettings.tempNString, 'tempNString event reset');
+    test.is(ev.value, null, 'tempNString event revalue');
+    test.is(ev.setting.value, null, 'tempNString event setting revalue');
+  }
+  mockSettings.tempNString.onChange.add(tempNStringReCheck);
+
+  mockSettings.tempTBool.setDefault();
+  mockSettings.tempFBool.setDefault();
+  mockSettings.tempUString.setDefault();
+  mockSettings.tempNString.setDefault();
+  mockSettings.tempQString.setDefault();
+  mockSettings.tempNumber.setDefault();
+  mockSettings.tempSelection.setDefault();
+
+  mockSettings.tempNString.onChange.remove(tempNStringReCheck);
+
+  test.is(mockSettings.tempTBool.value, true, 'tempTBool reset');
+  test.is(mockSettings.tempFBool.value, false, 'tempFBool reset');
+  test.is(mockSettings.tempUString.value, undefined, 'tempUString reset');
+  test.is(mockSettings.tempNString.value, null, 'tempNString reset');
+  test.is(mockSettings.tempQString.value, 'q', 'tempQString reset');
+  test.is(mockSettings.tempNumber.value, 42, 'tempNumber reset');
+  test.is(mockSettings.tempSelection.value, 'a', 'tempSelection reset');
+};
+
+
+});
+/*
  * Copyright (c) 2009 Panagiotis Astithas
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
@@ -2348,17 +3537,22 @@ var test = require('test/assert');
 var Speller = require('gcli/types/spell').Speller;
 
 exports.setup = function() {
 };
 
 exports.shutdown = function() {
 };
 
-exports.testSimple = function(options) {
+exports.testSpellerSimple = function(options) {
+  if (options.isFirefox) {
+    test.log('Skipping testPref in Firefox.');
+    return;
+  }
+
   var speller = new Speller();
   speller.train(Object.keys(options.window));
 
   test.is(speller.correct('document'), 'document');
   test.is(speller.correct('documen'), 'document');
   test.is(speller.correct('ocument'), 'document');
   test.is(speller.correct('odcument'), 'document');
 
@@ -2368,32 +3562,33 @@ exports.testSimple = function(options) {
 
 });
 /*
  * Copyright 2009-2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE.txt or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
 
-define('gclitest/testSplit', ['require', 'exports', 'module' , 'test/assert', 'gclitest/commands', 'gcli/cli'], function(require, exports, module) {
+define('gclitest/testSplit', ['require', 'exports', 'module' , 'test/assert', 'gclitest/mockCommands', 'gcli/cli', 'gcli/canon'], function(require, exports, module) {
 
 var test = require('test/assert');
 
-var commands = require('gclitest/commands');
+var mockCommands = require('gclitest/mockCommands');
 var Requisition = require('gcli/cli').Requisition;
+var canon = require('gcli/canon');
 
 exports.setup = function() {
-  commands.setup();
+  mockCommands.setup();
 };
 
 exports.shutdown = function() {
-  commands.shutdown();
+  mockCommands.shutdown();
 };
 
-exports.testSimple = function() {
+exports.testSplitSimple = function() {
   var args;
   var requ = new Requisition();
 
   args = requ._tokenize('s');
   requ._split(args);
   test.is(0, args.length);
   test.is('s', requ.commandAssignment.arg.text);
 };
@@ -2411,16 +3606,21 @@ exports.testFlatCommand = function() {
   requ._split(args);
   test.is('tsv', requ.commandAssignment.value.name);
   test.is(2, args.length);
   test.is('a', args[0].text);
   test.is('b', args[1].text);
 };
 
 exports.testJavascript = function() {
+  if (!canon.getCommand('{')) {
+    test.log('Skipping testJavascript because { is not registered');
+    return;
+  }
+
   var args;
   var requ = new Requisition();
 
   args = requ._tokenize('{');
   requ._split(args);
   test.is(1, args.length);
   test.is('', args[0].text);
   test.is('', requ.commandAssignment.arg.text);
@@ -2456,17 +3656,17 @@ exports.testBlanks = function() {
 
   args = requ._tokenize(' ');
   test.is(1, args.length);
   test.is('', args[0].text);
   test.is(' ', args[0].prefix);
   test.is('', args[0].suffix);
 };
 
-exports.testSimple = function() {
+exports.testTokSimple = function() {
   var args;
   var requ = new Requisition();
 
   args = requ._tokenize('s');
   test.is(1, args.length);
   test.is('s', args[0].text);
   test.is('', args[0].prefix);
   test.is('', args[0].suffix);
@@ -2716,29 +3916,29 @@ exports.testPathological = function() {
 
 });
 /*
  * Copyright 2009-2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE.txt or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
 
-define('gclitest/testTooltip', ['require', 'exports', 'module' , 'test/assert', 'gclitest/commands'], function(require, exports, module) {
+define('gclitest/testTooltip', ['require', 'exports', 'module' , 'test/assert', 'gclitest/mockCommands'], function(require, exports, module) {
 
 
 var test = require('test/assert');
-var commands = require('gclitest/commands');
+var mockCommands = require('gclitest/mockCommands');
 
 
 exports.setup = function() {
-  commands.setup();
+  mockCommands.setup();
 };
 
 exports.shutdown = function() {
-  commands.shutdown();
+  mockCommands.shutdown();
 };
 
 
 function type(typed, tests, options) {
   var inputter = options.display.inputter;
   var tooltip = options.display.tooltip;
 
   inputter.setInput(typed);
@@ -2885,28 +4085,41 @@ exports.testFindCssSelector = function(o
 
 
 });
 
 let testModuleNames = [
   'gclitest/index',
   'gclitest/suite',
   'test/examiner',
+  'test/assert',
+  'gclitest/testCanon',
+  'gclitest/helpers',
   'gclitest/testCli',
-  'gclitest/commands',
-  'test/assert',
+  'gclitest/mockCommands',
   'gclitest/testCompletion',
   'gclitest/testExec',
+  'gclitest/testHelp',
   'gclitest/testHistory',
+  'gclitest/testInputter',
+  'gclitest/testIntro',
   'gclitest/testJs',
   'gclitest/testKeyboard',
+  'gclitest/testPref',
+  'gcli/commands/pref',
+  'text!gcli/commands/pref_list_outer.html',
+  'text!gcli/commands/pref_list.css',
+  'text!gcli/commands/pref_set_check.html',
+  'text!gcli/commands/pref_list_inner.html',
+  'gclitest/mockSettings',
   'gclitest/testRequire',
   'gclitest/requirable',
   'gclitest/testResource',
   'gclitest/testScratchpad',
+  'gclitest/testSettings',
   'gclitest/testSpell',
   'gclitest/testSplit',
   'gclitest/testTokenize',
   'gclitest/testTooltip',
   'gclitest/testTypes',
   'gclitest/testUtil',
 ];
 
@@ -2917,19 +4130,19 @@ const TEST_URI = "data:text/html;charset
 
 function test() {
   localDefine = define;
 
   DeveloperToolbarTest.test(TEST_URI, function(browser, tab) {
     var gclitest = define.globalDomain.require("gclitest/index");
     gclitest.run({
       display: DeveloperToolbar.display,
-      // window: browser.getBrowser().contentWindow
+      isFirefox: true,
+      window: browser.contentDocument.defaultView
     });
-
     finish();
   });
 }
 
 registerCleanupFunction(function() {
   testModuleNames.forEach(function(moduleName) {
     delete localDefine.modules[moduleName];
     delete localDefine.globalDomain.modules[moduleName];
--- a/browser/devtools/shared/test/browser_toolbar_basic.js
+++ b/browser/devtools/shared/test/browser_toolbar_basic.js
@@ -1,36 +1,124 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that the developer toolbar works properly
 
+let imported = {};
+Components.utils.import("resource:///modules/HUDService.jsm", imported);
+registerCleanupFunction(function() {
+  imported = {};
+});
+
 const URL = "http://example.com/browser/browser/devtools/shared/test/browser_toolbar_basic.html";
 
 function test() {
   addTab(URL, function(browser, tab) {
     info("Starting browser_toolbar_basic.js");
     runTest();
   });
 }
 
 function runTest() {
-  Services.obs.addObserver(checkOpen, DeveloperToolbar.NOTIFICATIONS.SHOW, false);
-  // TODO: reopen the window so the pref has a chance to take effect
-  // EventUtils.synthesizeKey("v", { ctrlKey: true, shiftKey: true });
-  DeveloperToolbarTest.show();
+  ok(!DeveloperToolbar.visible, "DeveloperToolbar is not visible in runTest");
+
+  oneTimeObserve(DeveloperToolbar.NOTIFICATIONS.SHOW, catchFail(checkOpen));
+  document.getElementById("Tools:DevToolbar").doCommand();
 }
 
 function checkOpen() {
-  Services.obs.removeObserver(checkOpen, DeveloperToolbar.NOTIFICATIONS.SHOW, false);
-  ok(DeveloperToolbar.visible, "DeveloperToolbar is visible");
+  ok(DeveloperToolbar.visible, "DeveloperToolbar is visible in checkOpen");
+
+  let close = document.getElementById("developer-toolbar-closebutton");
+  let webconsole = document.getElementById("developer-toolbar-webconsole");
+  let inspector = document.getElementById("developer-toolbar-inspector");
+  let debuggr = document.getElementById("developer-toolbar-debugger");
+
+  ok(close, "Close button exists");
+
+  ok(!webconsole.checked, "web console button state 1");
+  ok(!inspector.checked, "inspector button state 1");
+  ok(!debuggr.checked, "debugger button state 1");
+
+  document.getElementById("Tools:WebConsole").doCommand();
+
+  ok(webconsole.checked, "web console button state 2");
+  ok(!inspector.checked, "inspector button state 2");
+  ok(!debuggr.checked, "debugger button state 2");
+
+  document.getElementById("Tools:Inspect").doCommand();
+
+  ok(webconsole.checked, "web console button state 3");
+  ok(inspector.checked, "inspector button state 3");
+  ok(!debuggr.checked, "debugger button state 3");
+
+  // Christmas tree!
 
-  Services.obs.addObserver(checkClosed, DeveloperToolbar.NOTIFICATIONS.HIDE, false);
-  // EventUtils.synthesizeKey("v", { ctrlKey: true, shiftKey: true });
-  DeveloperToolbarTest.hide();
+  // The web console opens synchronously, but closes asynchronously.
+  let hud = imported.HUDService.getHudByWindow(content);
+  imported.HUDService.disableAnimation(hud.hudId);
+
+  document.getElementById("Tools:WebConsole").doCommand();
+
+  ok(!webconsole.checked, "web console button state 6");
+  ok(inspector.checked, "inspector button state 6");
+  ok(!debuggr.checked, "debugger button state 6");
+
+  document.getElementById("Tools:Inspect").doCommand();
+
+  ok(!webconsole.checked, "web console button state 7");
+  ok(!inspector.checked, "inspector button state 7");
+  ok(!debuggr.checked, "debugger button state 7");
+
+  // All closed
+
+  // Check we can open and close and retain button state
+  document.getElementById("Tools:Inspect").doCommand();
+
+  ok(!webconsole.checked, "web console button state 8");
+  ok(inspector.checked, "inspector button state 8");
+  ok(!debuggr.checked, "debugger button state 8");
+
+  oneTimeObserve(DeveloperToolbar.NOTIFICATIONS.HIDE, catchFail(checkClosed));
+  document.getElementById("Tools:DevToolbar").doCommand();
 }
 
 function checkClosed() {
-  Services.obs.removeObserver(checkClosed, DeveloperToolbar.NOTIFICATIONS.HIDE, false);
-  ok(!DeveloperToolbar.visible, "DeveloperToolbar is not visible");
+  ok(!DeveloperToolbar.visible, "DeveloperToolbar is not visible in checkClosed");
+
+  // Check we grok state even when closed
+  document.getElementById("Tools:WebConsole").doCommand();
+
+  oneTimeObserve(DeveloperToolbar.NOTIFICATIONS.SHOW, catchFail(checkReOpen));
+  document.getElementById("Tools:DevToolbar").doCommand();
+}
+
+function checkReOpen() {
+  ok(DeveloperToolbar.visible, "DeveloperToolbar is visible in checkReOpen");
+
+  let webconsole = document.getElementById("developer-toolbar-webconsole");
+  let inspector = document.getElementById("developer-toolbar-inspector");
+  let debuggr = document.getElementById("developer-toolbar-debugger");
+
+  ok(webconsole.checked, "web console button state 9");
+  ok(inspector.checked, "inspector button state 9");
+  ok(!debuggr.checked, "debugger button state 9");
+
+  oneTimeObserve(DeveloperToolbar.NOTIFICATIONS.HIDE, catchFail(checkReClosed));
+  document.getElementById("developer-toolbar-closebutton").doCommand();
+}
+
+function checkReClosed() {
+  ok(!DeveloperToolbar.visible, "DeveloperToolbar is not visible in checkReClosed");
 
   finish();
 }
+
+//------------------------------------------------------------------------------
+
+function oneTimeObserve(name, callback) {
+  var func = function() {
+    Services.obs.removeObserver(func, name);
+    callback();
+  };
+  Services.obs.addObserver(func, name, false);
+}
--- a/browser/devtools/shared/test/head.js
+++ b/browser/devtools/shared/test/head.js
@@ -305,17 +305,29 @@ let DeveloperToolbarTest = {
 
     addTab(uri, function(browser, tab) {
       DeveloperToolbarTest.show(function() {
 
         try {
           testFunc(browser, tab);
         }
         catch (ex) {
-          ok(false, "" + ex);
+          ok(false, ex);
           console.error(ex);
           finish();
-          throw ex;
         }
       });
     });
   },
 };
+
+function catchFail(func) {
+  return function() {
+    try {
+      return func.apply(null, arguments);
+    }
+    catch (ex) {
+      ok(false, ex);
+      console.error(ex);
+      finish();
+    }
+  };
+}
--- a/browser/devtools/webconsole/gcli.jsm
+++ b/browser/devtools/webconsole/gcli.jsm
@@ -763,27 +763,16 @@ var canon = exports;
 
 var util = require('gcli/util');
 var l10n = require('gcli/l10n');
 
 var types = require('gcli/types');
 var Status = require('gcli/types').Status;
 var BooleanType = require('gcli/types/basic').BooleanType;
 
-
-/**
- * A lookup hash of our registered commands
- */
-var commands = {};
-
-/**
- * A sorted list of command names, we regularly want them in order, so pre-sort
- */
-var commandNames = [];
-
 /**
  * Implement the localization algorithm for any documentation objects (i.e.
  * description and manual) in a command.
  * @param data The data assigned to a description or manual property
  * @param onUndefined If data == null, should we return the data untouched or
  * lookup a 'we don't know' key in it's place.
  */
 function lookup(data, onUndefined) {
@@ -1026,41 +1015,69 @@ Object.defineProperty(Parameter.prototyp
   },
   enumerable: true
 });
 
 canon.Parameter = Parameter;
 
 
 /**
+ * A lookup hash of our registered commands
+ */
+var commands = {};
+
+/**
+ * A sorted list of command names, we regularly want them in order, so pre-sort
+ */
+var commandNames = [];
+
+/**
+ * A lookup of the original commandSpecs by command name
+ */
+var commandSpecs = {};
+
+/**
  * Add a command to the canon of known commands.
  * This function is exposed to the outside world (via gcli/index). It is
  * documented in docs/index.md for all the world to see.
  * @param commandSpec The command and its metadata.
  * @return The new command
  */
 canon.addCommand = function addCommand(commandSpec) {
+  if (commands[commandSpec.name] != null) {
+    // Roughly canon.removeCommand() without the event call, which we do later
+    delete commands[commandSpec.name];
+    commandNames = commandNames.filter(function(test) {
+      return test !== commandSpec.name;
+    });
+  }
+
   var command = new Command(commandSpec);
   commands[commandSpec.name] = command;
   commandNames.push(commandSpec.name);
   commandNames.sort();
 
+  commandSpecs[commandSpec.name] = commandSpec;
+
   canon.onCanonChange();
   return command;
 };
 
 /**
  * Remove an individual command. The opposite of #addCommand().
  * @param commandOrName Either a command name or the command itself.
  */
 canon.removeCommand = function removeCommand(commandOrName) {
   var name = typeof commandOrName === 'string' ?
           commandOrName :
           commandOrName.name;
+
+  // See start of canon.addCommand if changing this code
   delete commands[name];
+  delete commandSpecs[name];
   commandNames = commandNames.filter(function(test) {
     return test !== name;
   });
 
   canon.onCanonChange();
 };
 
 /**
@@ -1085,16 +1102,24 @@ canon.getCommands = function getCommands
 /**
  * Get an array containing the names of the registered commands.
  */
 canon.getCommandNames = function getCommandNames() {
   return commandNames.slice(0);
 };
 
 /**
+ * Get access to the stored commandMetaDatas (i.e. before they were made into
+ * instances of Command/Parameters) so we can remote them.
+ */
+canon.getCommandSpecs = function getCommandSpecs() {
+  return commandSpecs;
+};
+
+/**
  * Enable people to be notified of changes to the list of commands
  */
 canon.onCanonChange = util.createEvent('canon.onCanonChange');
 
 /**
  * CommandOutputManager stores the output objects generated by executed
  * commands.
  *
@@ -1221,19 +1246,27 @@ exports.createEvent = function(name) {
 
   /**
    * Remove a handler function added through add(). Both func and scope must
    * be strict equals (===) the values used in the call to add()
    * @param func The function to call when this event is triggered
    * @param scope Optional 'this' object for the function call
    */
   event.remove = function(func, scope) {
+    var found = false;
     handlers = handlers.filter(function(test) {
-      return test.func !== func && test.scope !== scope;
+      var noMatch = (test.func !== func && test.scope !== scope);
+      if (!noMatch) {
+        found = true;
+      }
+      return noMatch;
     });
+    if (!found) {
+      console.warn('Failed to remove handler from ' + name);
+    }
   };
 
   /**
    * Remove all handlers.
    * Reset the state of this event back to it's post create state
    */
   event.removeAll = function() {
     handlers = [];
@@ -1585,16 +1618,23 @@ exports.findCssSelector = function(ele) 
  * Work out the path for images.
  */
 exports.createUrlLookup = function(callingModule) {
   return function imageUrl(path) {
     try {
       return require('text!gcli/ui/' + path);
     }
     catch (ex) {
+      // Under node/unamd callingModule is provided by node. This code isn't
+      // the right answer but it's enough to pass all the unit tests and get
+      // test coverage information, which is all we actually care about here.
+      if (callingModule.filename) {
+        return callingModule.filename + path;
+      }
+
       var filename = callingModule.id.split('/').pop() + '.js';
 
       if (callingModule.uri.substr(-filename.length) !== filename) {
         console.error('Can\'t work out path from module.uri/module.id');
         return path;
       }
 
       if (callingModule.uri) {
@@ -4995,17 +5035,18 @@ define('gcli/ui/intro', ['require', 'exp
   var Output = require('gcli/cli').Output;
 
   /**
    * Record if the user has clicked on 'Got It!'
    */
   var hideIntroSettingSpec = {
     name: 'hideIntro',
     type: 'boolean',
-    description: l10n.lookup('hideIntroDesc')
+    description: l10n.lookup('hideIntroDesc'),
+    defaultValue: false
   };
   var hideIntro;
 
   /**
    * Register (and unregister) the hide-intro setting
    */
   exports.startup = function() {
     hideIntro = settings.addSetting(hideIntroSettingSpec);
@@ -5708,16 +5749,19 @@ Requisition.prototype.cloneAssignments =
  * The overall status is the most severe status.
  * There is no such thing as an INCOMPLETE overall status because the
  * definition of INCOMPLETE takes into account the cursor position to say 'this
  * isn't quite ERROR because the user can fix it by typing', however overall,
  * this is still an error status.
  */
 Requisition.prototype.getStatus = function() {
   var status = Status.VALID;
+  if (!this._unassigned.arg.isBlank()) {
+    return Status.ERROR;
+  }
   this.getAssignments(true).forEach(function(assignment) {
     var assignStatus = assignment.getStatus();
     if (assignStatus > status) {
       status = assignStatus;
     }
   }, this);
   if (status === Status.INCOMPLETE) {
     status = Status.ERROR;
@@ -6664,19 +6708,31 @@ Output.prototype.toDom = function(elemen
   if (typeof HTMLElement !== 'undefined' && output instanceof HTMLElement) {
     node = output;
   }
   else if (output.isView) {
     node = output.toDom(document);
   }
   else {
     if (this.command.returnType === 'terminal') {
-      node = util.createElement(document, 'textarea');
-      node.classList.add('gcli-row-terminal');
-      node.readOnly = true;
+      if (Array.isArray(output)) {
+        node = util.createElement(document, 'div');
+        output.forEach(function() {
+          var child = util.createElement(document, 'textarea');
+          child.classList.add('gcli-row-subterminal');
+          child.readOnly = true;
+
+          node.appendChild(child);
+        });
+      }
+      else {
+        node = util.createElement(document, 'textarea');
+        node.classList.add('gcli-row-terminal');
+        node.readOnly = true;
+      }
     }
     else {
       node = util.createElement(document, 'p');
     }
 
     util.setContents(node, output.toString());
   }
 
@@ -6807,17 +6863,16 @@ exports.shutdown = function() {
  * The specific problem we are solving is when the hint element must be visible
  * if either the command line or any of the inputs in the hint element has the
  * focus, and invisible at other times, without hiding and showing the hint
  * element even briefly as the focus changes between them.
  * It does this simply by postponing the hide events by 250ms to see if
  * something else takes focus.
  * @param options Object containing user customization properties, including:
  * - blurDelay (default=150ms)
- * - slowTypingDelay (default=3000ms)
  * - debug (default=false)
  * - commandOutputManager (default=canon.commandOutputManager)
  * @param components Object that links to other UI components. GCLI provided:
  * - document
  */
 function FocusManager(options, components) {
   options = options || {};
 
@@ -6833,22 +6888,16 @@ function FocusManager(options, component
   this._blurDelayTimeout = null; // Result of setTimeout in delaying a blur
   this._monitoredElements = [];  // See addMonitoredElement()
 
   this._isError = false;
   this._hasFocus = false;
   this._helpRequested = false;
   this._recentOutput = false;
 
-  // Be more helpful if the user pauses
-  // this._slowTyping = false;
-  // this._keyPressTimeout = null;
-  // this._onSlowTyping = this._onSlowTyping.bind(this);
-  // this._slowTypingDelay = options.slowTypingDelay || 3000;
-
   this.onVisibilityChange = util.createEvent('FocusManager.onVisibilityChange');
 
   this._focused = this._focused.bind(this);
   this._document.addEventListener('focus', this._focused, true);
 
   eagerHelper.onChange.add(this._eagerHelperChanged, this);
 
   this.isTooltipVisible = undefined;
@@ -6873,17 +6922,16 @@ FocusManager.prototype.destroy = functio
     monitor.element.removeEventListener('blur', monitor.onBlur, true);
   }
 
   if (this._blurDelayTimeout) {
     this._window.clearTimeout(this._blurDelayTimeout);
     this._blurDelayTimeout = null;
   }
 
-  // delete this._onSlowTyping;
   delete this._focused;
   delete this._document;
   delete this._window;
   delete this._commandOutputManager;
 };
 
 /**
  * The easy way to include an element in the set of things that are part of the
@@ -6964,18 +7012,16 @@ FocusManager.prototype._focused = functi
  * straight away and informs the listeners
  * @param where Optional source string for debugging only
  */
 FocusManager.prototype._reportFocus = function(where) {
   if (this._debug) {
     console.log('FocusManager._reportFocus(' + (where || 'unknown') + ')');
   }
 
-  // this._resetSlowTypingAlarm();
-
   if (this._blurDelayTimeout) {
     if (this._debug) {
       console.log('FocusManager.cancelBlur');
     }
     this._window.clearTimeout(this._blurDelayTimeout);
     this._blurDelayTimeout = null;
   }
 
@@ -6991,18 +7037,16 @@ FocusManager.prototype._reportFocus = fu
  * status and informs the listeners
  * @param where Optional source string for debugging only
  */
 FocusManager.prototype._reportBlur = function(where) {
   if (this._debug) {
     console.log('FocusManager._reportBlur(' + where + ')');
   }
 
-  // this._cancelSlowTypingAlarm();
-
   if (this._hasFocus) {
     if (this._blurDelayTimeout) {
       if (this._debug) {
         console.log('FocusManager.blurPending');
       }
       return;
     }
 
@@ -7013,89 +7057,54 @@ FocusManager.prototype._reportBlur = fun
       this._hasFocus = false;
       this._checkShow();
       this._blurDelayTimeout = null;
     }.bind(this), this._blurDelay);
   }
 };
 
 /**
- * Called on keypress or new focus. Sets off a timer to explode if the user
- * stops typing.
- */
-FocusManager.prototype._resetSlowTypingAlarm = function() {
-  // this._cancelSlowTypingAlarm();
-  // this._keyPressTimeout = this._window.setTimeout(this._onSlowTyping,
-  //                                                 this._slowTypingDelay);
-};
-
-/**
- * Don't kick off a slow typing alarm
- */
-FocusManager.prototype._cancelSlowTypingAlarm = function() {
-  // if (this._keyPressTimeout) {
-  //   this._window.clearTimeout(this._keyPressTimeout);
-  //   this._keyPressTimeout = null;
-  // }
-  // this._slowTyping = false;
-};
-
-/**
- * Called from the key-press timeout
- */
-FocusManager.prototype._onSlowTyping = function() {
-  // this._slowTyping = true;
-  // this._checkShow();
-};
-
-/**
  * The setting has changed
  */
 FocusManager.prototype._eagerHelperChanged = function() {
   this._checkShow();
 };
 
 /**
  * The inputter tells us about keyboard events so we can decide to delay
  * showing the tooltip element, (or if the keypress is F1, show it now)
  */
 FocusManager.prototype.onInputChange = function(ev) {
-  // this._resetSlowTypingAlarm();
-  // this._slowTyping = false;
   this._recentOutput = false;
   this._checkShow();
 };
 
 /**
  * Generally called for something like a F1 key press, when the user explicitly
  * wants help
  */
 FocusManager.prototype.helpRequest = function() {
   if (this._debug) {
     console.log('FocusManager.helpRequest');
   }
 
-  // this._cancelSlowTypingAlarm();
-  // this._slowTyping = true;
   this._helpRequested = true;
   this._recentOutput = false;
   this._checkShow();
 };
 
 /**
  * Generally called for something like a ESC key press, when the user explicitly
  * wants to get rid of the help
  */
 FocusManager.prototype.removeHelp = function() {
   if (this._debug) {
     console.log('FocusManager.removeHelp');
   }
 
-  // this._cancelSlowTypingAlarm();
-  // this._slowTyping = false;
   this._importantFieldFlag = false;
   this._isError = false;
   this._helpRequested = false;
   this._recentOutput = false;
   this._checkShow();
 };
 
 /**
@@ -7171,20 +7180,16 @@ FocusManager.prototype._shouldShowToolti
   if (this._helpRequested) {
     return { visible: true, reason: 'helpRequested' };
   }
 
   if (this._importantFieldFlag) {
     return { visible: true, reason: 'importantFieldFlag' };
   }
 
-  // if (this._slowTyping) {
-  //   return { visible: true, reason: 'slowTyping' };
-  // }
-
   return { visible: false, reason: 'default' };
 };
 
 /**
  * Calculate if we should be showing or hidden taking into account all the
  * available inputs
  */
 FocusManager.prototype._shouldShowOutput = function() {
@@ -8002,18 +8007,18 @@ function Menu(options) {
   this.onItemClick = util.createEvent('Menu.onItemClick');
 }
 
 /**
  * Avoid memory leaks
  */
 Menu.prototype.destroy = function() {
   delete this.element;
-  delete this.items;
   delete this.template;
+  delete this.document;
 };
 
 /**
  * The default is to do nothing when someone clicks on the menu.
  * This is called from template.html
  * @param ev The click event from the browser
  */
 Menu.prototype.onItemClickInternal = function(ev) {
@@ -8383,17 +8388,17 @@ var helpCommandSpec = {
   name: 'help',
   description: l10n.lookup('helpDesc'),
   manual: l10n.lookup('helpManual'),
   params: [
     {
       name: 'search',
       type: 'string',
       description: l10n.lookup('helpSearchDesc'),
-      manual: l10n.lookup('helpSearchManual'),
+      manual: l10n.lookup('helpSearchManual2'),
       defaultValue: null
     }
   ],
   returnType: 'html',
 
   exec: function(args, context) {
     var match = canon.getCommand(args.search || undefined);
     if (match) {
@@ -9458,17 +9463,18 @@ History.prototype.backward = function() 
   if (this._current < this._buffer.length - 1) {
     this._current++;
   }
   return this._buffer[this._current];
 };
 
 exports.History = History;
 
-});define("text!gcli/ui/inputter.css", [], "");
+});
+define("text!gcli/ui/inputter.css", [], "");
 
 /*
  * Copyright 2009-2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE.txt or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
 
 define('gcli/ui/completer', ['require', 'exports', 'module' , 'gcli/util', 'gcli/ui/domtemplate', 'text!gcli/ui/completer.html'], function(require, exports, module) {
@@ -9824,18 +9830,16 @@ Tooltip.prototype.destroy = function() {
 
   this.field.onFieldChange.remove(this.fieldChanged, this);
   this.field.destroy();
 
   delete this.errorEle;
   delete this.descriptionEle;
   delete this.highlightEle;
 
-  delete this.field;
-  delete this.focusManager;
   delete this.document;
   delete this.element;
   delete this.panelElement;
   delete this.template;
 };
 
 /**
  * The inputter acts on UP/DOWN if there is a menu showing
--- a/browser/locales/en-US/chrome/browser/devtools/debugger.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/debugger.dtd
@@ -1,20 +1,20 @@
-<!-- LOCALIZATION NOTE : FILE This file contains the Script Debugger strings -->
+<!-- LOCALIZATION NOTE : FILE This file contains the Debugger strings -->
 <!-- LOCALIZATION NOTE : FILE Do not translate commandkey -->
 
 <!-- LOCALIZATION NOTE : FILE The correct localization of this file might be to
   - keep it in English, or another language commonly spoken among web developers.
   - You want to make that choice consistent across the developer tools.
   - A good criteria is the language in which you'd find the best
   - documentation on web development on the web. -->
 
 <!-- LOCALIZATION NOTE (debuggerMenu.label): This is the label for the
   -  application menu item that opens the debugger UI. -->
-<!ENTITY debuggerMenu.label          "Script Debugger">
+<!ENTITY debuggerMenu.label2          "Debugger">
 
 <!-- LOCALIZATION NOTE (remoteDebuggerMenu.label): This is the label for the
   -  application menu item that opens the remote debugger UI. -->
 <!ENTITY remoteDebuggerMenu.label    "Remote Debugger">
 
 <!-- LOCALIZATION NOTE (chromeDebuggerMenu.label): This is the label for the
   -  application menu item that opens the browser debugger UI. -->
 <!ENTITY chromeDebuggerMenu.label    "Browser Debugger">
--- a/browser/locales/en-US/chrome/browser/devtools/debugger.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/debugger.properties
@@ -1,10 +1,10 @@
-# LOCALIZATION NOTE These strings are used inside the Script Debugger
-# which is available from the Web Developer sub-menu -> 'Script Debugger'.
+# LOCALIZATION NOTE These strings are used inside the Debugger
+# which is available from the Web Developer sub-menu -> 'Debugger'.
 # The correct localization of this file might be to keep it in
 # English, or another language commonly spoken among web developers.
 # You want to make that choice consistent across the developer tools.
 # A good criteria is the language in which you'd find the best
 # documentation on web development on the web.
 
 # LOCALIZATION NOTE (remoteDebuggerWindowTitle): The title displayed for the
 # remote debugger window.
--- a/browser/locales/en-US/chrome/browser/devtools/gcli.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/gcli.properties
@@ -100,25 +100,26 @@ nodeParseNone=No matches
 # fuller description of what it does.
 helpDesc=Get help on the available commands
 
 # LOCALIZATION NOTE (helpManual): A fuller description of the 'help' command.
 # Displayed when the user asks for help on what it does.
 helpManual=Provide help either on a specific command (if a search string is provided and an exact match is found) or on the available commands (if a search string is not provided, or if no exact match is found).
 
 # LOCALIZATION NOTE (helpSearchDesc): A very short description of the 'search'
-# parameter to the 'help' command. See helpSearchManual for a fuller
+# parameter to the 'help' command. See helpSearchManual2 for a fuller
 # description of what it does. This string is designed to be shown in a dialog
 # with restricted space, which is why it should be as short as possible.
 helpSearchDesc=Search string
 
-# LOCALIZATION NOTE (helpSearchManual): A fuller description of the 'search'
+# LOCALIZATION NOTE (helpSearchManual2): A fuller description of the 'search'
 # parameter to the 'help' command. Displayed when the user asks for help on
-# what it does.
-helpSearchManual=<strong>search string</strong> to use in narrowing down the displayed commands. Regular expressions not supported.
+# what it does. Inline HTML (e.g. <strong>) can be used to emphasize the core
+# concept.
+helpSearchManual2=<strong>search string</strong> to use in narrowing down the displayed commands. Regular expressions not supported.
 
 # LOCALIZATION NOTE (helpManSynopsis): A heading shown at the top of a help
 # page for a command in the console It labels a summary of the parameters to
 # the command
 helpManSynopsis=Synopsis
 
 # LOCALIZATION NOTE (helpManDescription): A heading shown in a help page for a
 # command in the console. This heading precedes the top level description.
@@ -215,31 +216,63 @@ prefSetCheckHeading=This might void your
 # prefSetCheckHeading and prefSetCheckGo.
 prefSetCheckBody=Changing these advanced settings can be harmful to the stability, security, and performance of this application. You should only continue if you are sure of what you are doing.
 
 # LOCALIZATION NOTE (prefSetCheckGo): The text to enable preference editing.
 # Displayed in a button directly under prefSetCheckHeading and
 # prefSetCheckBody
 prefSetCheckGo=I'll be careful, I promise!
 
+# LOCALIZATION NOTE (prefResetDesc): A very short description of the 'pref
+# reset' command. This string is designed to be shown in a menu alongside the
+# command name, which is why it should be as short as possible. See
+# prefResetManual for a fuller description of what it does.
+prefResetDesc=Reset a setting
+
+# LOCALIZATION NOTE (prefResetManual): A fuller description of the 'pref
+# reset' command. Displayed when the user asks for help on what it does.
+prefResetManual=Reset the value of a setting to the system defaults
+
+# LOCALIZATION NOTE (prefResetSettingDesc): A short description of the
+# 'setting' parameter to the 'pref reset' command. See prefResetSettingManual
+# for a fuller description of what it does. This string is designed to be
+# shown in a dialog with restricted space, which is why it should be as short
+# as possible.
+prefResetSettingDesc=Setting to reset
+
+# LOCALIZATION NOTE (prefResetSettingManual): A fuller description of the
+# 'setting' parameter to the 'pref reset' command. Displayed when the user
+# asks for help on what it does.
+prefResetSettingManual=The name of the setting to reset to the system default value
+
 # LOCALIZATION NOTE (prefOutputFilter): Displayed in the output from the 'pref
 # list' command as a label to an input element that allows the user to filter
 # the results
 prefOutputFilter=Filter
 
 # LOCALIZATION NOTE (prefOutputName): Displayed in the output from the 'pref
 # list' command as a heading to a table. The column contains the names of the
 # available preferences
 prefOutputName=Name
 
 # LOCALIZATION NOTE (prefOutputValue): Displayed in the output from the 'pref
 # list' command as a heading to a table. The column contains the values of the
 # available preferences
 prefOutputValue=Value
 
+# LOCALIZATION NOTE (introDesc): A very short description of the 'intro'
+# command. This string is designed to be shown in a menu alongside the command
+# name, which is why it should be as short as possible. See introManual for a
+# fuller description of what it does.
+introDesc=Show the opening message
+
+# LOCALIZATION NOTE (introManual): A fuller description of the 'intro'
+# command. Displayed when the user asks for help on what it does.
+introManual=Redisplay the message that is shown to new users until they click the 'Got it!' button
+
 # LOCALIZATION NOTE (hideIntroDesc): Short description of the 'hideIntro'
 # setting. Displayed when the user asks for help on the settings.
 hideIntroDesc=Show the initial welcome message
 
 # LOCALIZATION NOTE (eagerHelperDesc): Short description of the 'eagerHelper'
 # setting. Displayed when the user asks for help on the settings.
 eagerHelperDesc=How eager are the tooltips
 
--- a/build/unix/build-toolchain/build-gcc.py
+++ b/build/unix/build-toolchain/build-gcc.py
@@ -272,10 +272,18 @@ build_one_stage({"CC": "gcc", "CXX" : "g
 stage1_tool_inst_dir = stage1_dir + '/inst'
 stage2_dir = build_dir + '/stage2'
 build_one_stage({"PATH"   : stage1_tool_inst_dir + "/bin:/bin:/usr/bin",
                  "CC"     : "gcc -fgnu89-inline",
                  "CXX"    : "g++",
                  "RANLIB" : "true" },
                 stage2_dir, False)
 
+stage2_tool_inst_dir = stage2_dir + '/inst'
+stage3_dir = build_dir + '/stage3'
+build_one_stage({"PATH"   : stage2_tool_inst_dir + "/bin:/bin:/usr/bin",
+                 "CC"     : "gcc -fgnu89-inline",
+                 "CXX"    : "g++",
+                 "RANLIB" : "true" },
+                stage3_dir, False)
+
 build_tar_package(aux_inst_dir + "/bin/tar",
-                  "toolchain.tar", stage2_dir, "inst")
+                  "toolchain.tar", stage3_dir, "inst")
new file mode 100644
--- /dev/null
+++ b/build/unix/mozconfig.asan
@@ -0,0 +1,20 @@
+# Use Clang r155417
+export CC="/tools/clang-3.0/bin/clang -fgnu89-inline"
+export CXX="/tools/clang-3.0/bin/clang++"
+
+# Mandatory flags for ASan
+export ASANFLAGS="-faddress-sanitizer -Dxmalloc=myxmalloc -fPIC"
+export CFLAGS="$ASANFLAGS"
+export CXXFLAGS="$ASANFLAGS"
+export LDFLAGS="-faddress-sanitizer"
+
+# Enable ASan specific code and build workarounds
+ac_add_options --enable-address-sanitizer
+
+# Mandatory options required for ASan builds (both on Linux and Mac)
+export MOZ_DEBUG_SYMBOLS=1
+ac_add_options --enable-debug-symbols
+ac_add_options --disable-install-strip
+ac_add_options --disable-jemalloc
+ac_add_options --disable-crashreporter
+ac_add_options --disable-elf-hack
--- a/build/win32/mozconfig.vs2010-win64
+++ b/build/win32/mozconfig.vs2010-win64
@@ -1,12 +1,13 @@
 export INCLUDE=/c/tools/msvs10/vc/include:/c/tools/msvs10/vc/atlmfc/include:/c/tools/sdks/v7.0/include:/c/tools/sdks/v7.0/include/atl:/c/tools/sdks/dx10/include
 export LIBPATH=/c/tools/msvs10/vc/lib:/c/tools/msvs10/vc/atlmfc/lib
 export LIB=/c/tools/msvs10/vc/lib:/c/tools/msvs10/vc/atlmfc/lib:/c/tools/sdks/v7.0/lib:/c/tools/sdks/dx10/lib
-export PATH="/c/tools/msvs10/Common7/IDE:/c/tools/msvs10/VC/BIN:/c/tools/msvs10/Common7/Tools:/c/tools/msvs10/VC/VCPackages:${PATH}"
 export WIN32_REDIST_DIR=/c/tools/msvs10/VC/redist/x86/Microsoft.VC100.CRT
-
+export MOZ_TOOLS=C:/mozilla-build/moztools
+export PATH="/c/tools/msvs10/Common7/IDE:/c/tools/msvs10/VC/BIN:/c/tools/msvs10/Common7/Tools:/c/tools/msvs10/VC/VCPackages:/c/mozilla-build/moztools:/c/Tools/sdks/v7.0/bin:${PATH}"
 
 mk_add_options "export LIB=$LIB"
 mk_add_options "export LIBPATH=$LIBPATH"
 mk_add_options "export PATH=$PATH"
 mk_add_options "export INCLUDE=$INCLUDE"
 mk_add_options "export WIN32_REDIST_DIR=$WIN32_REDIST_DIR"
+mk_add_options "export MOZ_TOOLS=$MOZ_TOOLS"
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -17,18 +17,18 @@ endif
 ifndef INCLUDED_CONFIG_MK
 include $(topsrcdir)/config/config.mk
 endif
 
 ifndef INCLUDED_VERSION_MK
 include $(topsrcdir)/config/version.mk
 endif
 
+USE_AUTOTARGETS_MK = 1
 include $(topsrcdir)/config/makefiles/makeutils.mk
-include $(topsrcdir)/config/makefiles/autotargets.mk
 
 ifdef SDK_XPIDLSRCS
 XPIDLSRCS += $(SDK_XPIDLSRCS)
 endif
 ifdef SDK_HEADERS
 EXPORTS += $(SDK_HEADERS)
 endif
 
@@ -1591,18 +1591,17 @@ endif # SDK_BINARY
 JAR_MANIFEST := $(srcdir)/jar.mn
 
 chrome::
 	$(MAKE) realchrome
 	$(LOOP_OVER_PARALLEL_DIRS)
 	$(LOOP_OVER_DIRS)
 	$(LOOP_OVER_TOOL_DIRS)
 
-$(FINAL_TARGET)/chrome:
-	$(NSINSTALL) -D $@
+$(FINAL_TARGET)/chrome: $(call mkdir_deps,$(FINAL_TARGET)/chrome)
 
 ifneq (,$(wildcard $(JAR_MANIFEST)))
 ifndef NO_DIST_INSTALL
 libs realchrome:: $(CHROME_DEPS) $(FINAL_TARGET)/chrome
 	$(PYTHON) $(MOZILLA_DIR)/config/JarMaker.py \
 	  $(QUIET) -j $(FINAL_TARGET)/chrome \
 	  $(MAKE_JARS_FLAGS) $(XULPPFLAGS) $(DEFINES) $(ACDEFINES) \
 	  $(JAR_MANIFEST)
--- a/content/base/src/nsDOMBlobBuilder.cpp
+++ b/content/base/src/nsDOMBlobBuilder.cpp
@@ -65,17 +65,17 @@ nsDOMMultipartFile::GetSize(PRUint64* aL
       PRUint64 l = 0;
   
       nsresult rv = blob->GetSize(&l);
       NS_ENSURE_SUCCESS(rv, rv);
   
       length += l;
     }
   
-    NS_ENSURE_TRUE(length.valid(), NS_ERROR_FAILURE);
+    NS_ENSURE_TRUE(length.isValid(), NS_ERROR_FAILURE);
 
     mLength = length.value();
   }
 
   *aLength = mLength;
   return NS_OK;
 }
 
--- a/content/base/src/nsDOMBlobBuilder.h
+++ b/content/base/src/nsDOMBlobBuilder.h
@@ -34,19 +34,18 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsDOMBlobBuilder_h
 #define nsDOMBlobBuilder_h
 
 #include "nsDOMFile.h"
-#include "CheckedInt.h"
 
-#include "mozilla/StandardInteger.h"
+#include "mozilla/CheckedInt.h"
 
 using namespace mozilla;
 
 class nsDOMMultipartFile : public nsDOMFileBase,
                            public nsIJSNativeInitializer
 {
 public:
   // Create as a file
@@ -123,20 +122,20 @@ protected:
   {
     if (mDataBufferLen >= mDataLen + aSize) {
       mDataLen += aSize;
       return true;
     }
 
     // Start at 1 or we'll loop forever.
     CheckedUint32 bufferLen = NS_MAX<PRUint32>(mDataBufferLen, 1);
-    while (bufferLen.valid() && bufferLen.value() < mDataLen + aSize)
+    while (bufferLen.isValid() && bufferLen.value() < mDataLen + aSize)
       bufferLen *= 2;
 
-    if (!bufferLen.valid())
+    if (!bufferLen.isValid())
       return false;
 
     // PR_ memory functions are still fallible
     void* data = PR_Realloc(mData, bufferLen.value());
     if (!data)
       return false;
 
     mData = data;
--- a/content/base/src/nsDOMFile.cpp
+++ b/content/base/src/nsDOMFile.cpp
@@ -56,18 +56,18 @@
 #include "nsISeekableStream.h"
 #include "nsIUnicharInputStream.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsIUUIDGenerator.h"
 #include "nsBlobProtocolHandler.h"
 #include "nsStringStream.h"
-#include "CheckedInt.h"
 #include "nsJSUtils.h"
+#include "mozilla/CheckedInt.h"
 #include "mozilla/Preferences.h"
 
 #include "plbase64.h"
 #include "prmem.h"
 #include "dombindings.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -222,17 +222,17 @@ ParseSize(PRInt64 aSize, PRInt64& aStart
   }
   else if (aEnd < 0) {
     newEndOffset += aSize;
   }
   else if (aEnd > aSize) {
     newEndOffset = aSize;
   }
 
-  if (!newStartOffset.valid() || !newEndOffset.valid() ||
+  if (!newStartOffset.isValid() || !newEndOffset.isValid() ||
       newStartOffset.value() >= newEndOffset.value()) {
     aStart = aEnd = 0;
   }
   else {
     aStart = newStartOffset.value();
     aEnd = newEndOffset.value();
   }
 }
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -1,9 +1,10 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=80: */
 /* ***** 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/
  *
@@ -104,16 +105,17 @@
 #include "nsIFileChannel.h"
 #include "mozilla/Telemetry.h"
 #include "jsfriendapi.h"
 #include "sampler.h"
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "nsIDOMFormData.h"
 
 #include "nsWrapperCacheInlines.h"
+#include "nsStreamListenerWrapper.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define LOAD_STR "load"
 #define ERROR_STR "error"
 #define ABORT_STR "abort"
 #define TIMEOUT_STR "timeout"
@@ -3041,35 +3043,41 @@ nsXMLHttpRequest::Send(JSContext *aCx, n
   mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
   mChannel->SetNotificationCallbacks(this);
 
   // Create our listener
   nsCOMPtr<nsIStreamListener> listener = this;
   if (mState & XML_HTTP_REQUEST_MULTIPART) {
     Telemetry::Accumulate(Telemetry::MULTIPART_XHR_RESPONSE, 1);
     listener = new nsMultipartProxyListener(listener);
-    if (!listener) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
   } else {
     Telemetry::Accumulate(Telemetry::MULTIPART_XHR_RESPONSE, 0);
   }
 
   // Blocking gets are common enough out of XHR that we should mark
   // the channel slow by default for pipeline purposes
   AddLoadFlags(mChannel, nsIRequest::INHIBIT_PIPELINE);
 
   if (!IsSystemXHR()) {
     // Always create a nsCORSListenerProxy here even if it's
     // a same-origin request right now, since it could be redirected.
     listener = new nsCORSListenerProxy(listener, mPrincipal, mChannel,
                                        withCredentials, true, &rv);
-    NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
     NS_ENSURE_SUCCESS(rv, rv);
   }
+  else {
+    // Because of bug 682305, we can't let listener be the XHR object itself
+    // because JS wouldn't be able to use it. So if we haven't otherwise
+    // created a listener around 'this', do so now.
+
+    listener = new nsStreamListenerWrapper(listener);
+  }
+
+  NS_ASSERTION(listener != this,
+               "Using an object as a listener that can't be exposed to JS");
 
   // Bypass the network cache in cases where it makes no sense:
   // 1) Multipart responses are very large and would likely be doomed by the
   //    cache once they grow too large, so they are not worth caching.
   // 2) POST responses are always unique, and we provide no API that would
   //    allow our consumers to specify a "cache key" to access old POST
   //    responses, so they are not worth caching.
   if ((mState & XML_HTTP_REQUEST_MULTIPART) || method.EqualsLiteral("POST")) {
--- a/content/base/test/chrome/Makefile.in
+++ b/content/base/test/chrome/Makefile.in
@@ -70,15 +70,16 @@ include $(topsrcdir)/config/rules.mk
     test_bug571390.xul \
     test_bug574596.html \
     test_bug683852.xul \
     test_bug599295.html \
     test_bug650776.html \
     test_bug650784.html \
     test_bug752226-3.xul \
     test_bug752226-4.xul \
+    test_bug682305.html \
     $(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
 
 libs:: $(_CHROME_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/base/test/chrome/test_bug682305.html
@@ -0,0 +1,159 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=682305
+-->
+<head>
+  <title>XMLHttpRequest send and channel implemented in JS</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"  src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+  <script type="application/javascript"  src="chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank"
+   href="https://bugzilla.mozilla.org/show_bug.cgi?id=682305">Mozilla Bug 682305</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.8">
+SimpleTest.waitForExplicitFinish();
+
+/*
+ * Register a custom nsIProtocolHandler service
+ * in order to be able to implement *and use* an 
+ * nsIChannel component written in Javascript.
+ */
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cr = Components.results;
+var Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+var SimpleURI = Cc["@mozilla.org/network/simple-uri;1"];
+var ios = Cc["@mozilla.org/network/io-service;1"]
+            .getService(Ci.nsIIOService);
+
+var PROTOCOL_SCHEME = "jsproto";
+
+
+function CustomChannel(uri) {
+	this.URI = this.originalURI = uri;
+}
+CustomChannel.prototype = {
+  URI: null,
+  originalURI: null,
+  contentCharset: "utf-8",
+  contentLength: 0,
+  contentType: "text/plain",
+  owner: Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal),
+  securityInfo: null,
+  notificationCallbacks: null,
+  loadFlags: 0,
+  loadGroup: null,
+  name: null,
+  status: Cr.NS_OK,
+  asyncOpen: function(listener, context) {
+    let stream = this.open();
+    try {
+      listener.onStartRequest(this, context);
+    } catch(e) {}
+    try {
+      listener.onDataAvailable(this, context, stream, 0, stream.available());
+    } catch(e) {}
+    try {
+      listener.onStopRequest(this, context, Cr.NS_OK);
+    } catch(e) {}
+  },
+  open: function() {
+    let data = "bar";
+    let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
+    stream.setData(data, data.length);
+    return stream;
+  },
+  isPending: function() {
+    return false;
+  },
+  cancel: function() {
+    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+  },
+  suspend: function() {
+    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+  },
+  resume: function() {
+    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+  },
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel, Ci.nsIRequest])
+};
+
+
+function CustomProtocol() {}
+CustomProtocol.prototype = {
+  get scheme() {
+    return PROTOCOL_SCHEME;
+  },
+  get protocolFlags() {
+    return (Ci.nsIProtocolHandler.URI_NORELATIVE |
+            Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE |
+            Ci.nsIProtocolHandler.URI_DANGEROUS_TO_LOAD);
+  },
+  get defaultPort() {
+    return -1;
+  },
+  allowPort: function allowPort() {
+    return false;
+  },
+  newURI: function newURI(spec, charset, baseURI) {
+    var uri = SimpleURI.createInstance(Ci.nsIURI)
+    uri.spec = spec;
+    return uri.QueryInterface(Ci.nsIURI);
+  },
+  newChannel: function newChannel(URI) {    
+    return new CustomChannel(URI);
+  },
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
+                                         Ci.nsISupportsWeakReference,
+                                         Ci.nsIProtocolHandler])
+};
+
+var gFactory = {
+  register: function() {
+    var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+
+    var classID = Components.ID("{ed064287-1e76-49ba-a28d-dc74394a8334}");
+    var description = PROTOCOL_SCHEME + ": protocol";
+    var contractID = "@mozilla.org/network/protocol;1?name=" + PROTOCOL_SCHEME;
+    var factory = XPCOMUtils._getFactory(CustomProtocol);
+
+    registrar.registerFactory(classID, description, contractID, factory);
+
+    this.unregister = function() {
+      registrar.unregisterFactory(classID, factory);
+      delete this.unregister;
+    };
+  }
+};
+
+// Register the custom procotol handler
+gFactory.register();
+
+// Then, checks if XHR works with it
+var xhr = new XMLHttpRequest();
+xhr.open("GET", PROTOCOL_SCHEME + ":foo", true);
+xhr.onload = function () {
+  is(xhr.responseText, "bar", "protocol doesn't work");
+  gFactory.unregister();
+  SimpleTest.finish();
+}
+try {
+  xhr.send(null);
+} catch(e) {
+  ok(false, e);
+}
+</script>
+</pre>
+</body>
+</html>
--- a/content/canvas/src/CanvasUtils.h
+++ b/content/canvas/src/CanvasUtils.h
@@ -35,17 +35,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef _CANVASUTILS_H_
 #define _CANVASUTILS_H_
 
 #include "prtypes.h"
 
-#include "CheckedInt.h"
+#include "mozilla/CheckedInt.h"
 
 class nsHTMLCanvasElement;
 class nsIPrincipal;
 
 namespace mozilla {
 
 namespace gfx {
 class Matrix;
@@ -58,19 +58,19 @@ using namespace gfx;
 // Check that the rectangle [x,y,w,h] is a subrectangle of [0,0,realWidth,realHeight]
 
 inline bool CheckSaneSubrectSize(PRInt32 x, PRInt32 y, PRInt32 w, PRInt32 h,
                             PRInt32 realWidth, PRInt32 realHeight) {
     CheckedInt32 checked_xmost  = CheckedInt32(x) + w;
     CheckedInt32 checked_ymost  = CheckedInt32(y) + h;
 
     return w >= 0 && h >= 0 && x >= 0 && y >= 0 &&
-        checked_xmost.valid() &&
+        checked_xmost.isValid() &&
         checked_xmost.value() <= realWidth &&
-        checked_ymost.valid() &&
+        checked_ymost.isValid() &&
         checked_ymost.value() <= realHeight;
 }
 
 // Flag aCanvasElement as write-only if drawing an image with aPrincipal
 // onto it would make it such.
 
 void DoDrawImageSecurityCheck(nsHTMLCanvasElement *aCanvasElement,
                               nsIPrincipal *aPrincipal,
--- a/content/canvas/src/CustomQS_Canvas2D.h
+++ b/content/canvas/src/CustomQS_Canvas2D.h
@@ -35,17 +35,17 @@
  * 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 ***** */
 
 #include "nsDOMError.h"
 #include "nsIDOMCanvasRenderingContext2D.h"
-#include "CheckedInt.h"
+#include "mozilla/CheckedInt.h"
 #include "nsMathUtils.h"
 #include "CustomQS_Canvas.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
 typedef NS_STDCALL_FUNCPROTO(nsresult, CanvasStyleSetterType, nsIDOMCanvasRenderingContext2D,
                              SetStrokeStyle_multi, (const nsAString &, nsISupports *));
@@ -164,17 +164,17 @@ CreateImageData(JSContext* cx, JSObject*
     using mozilla::CheckedInt;
 
     if (w == 0)
         w = 1;
     if (h == 0)
         h = 1;
 
     CheckedInt<uint32_t> len = CheckedInt<uint32_t>(w) * h * 4;
-    if (!len.valid()) {
+    if (!len.isValid()) {
         return xpc_qsThrow(cx, NS_ERROR_DOM_INDEX_SIZE_ERR);
     }
 
     // Create the fast typed array; it's initialized to 0 by default.
     JSObject* darray = JS_NewUint8ClampedArray(cx, len.value());
     JS::AutoObjectRooter rd(cx, darray);
     if (!darray) {
         return false;
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -420,17 +420,17 @@ WebGLContext::SetDimensions(PRInt32 widt
     // We're going to create an entirely new context.  If our
     // generation is not 0 right now (that is, if this isn't the first
     // context we're creating), we may have to dispatch a context lost
     // event.
 
     // If incrementing the generation would cause overflow,
     // don't allow it.  Allowing this would allow us to use
     // resource handles created from older context generations.
-    if (!(mGeneration+1).valid())
+    if (!(mGeneration + 1).isValid())
         return NS_ERROR_FAILURE; // exit without changing the value of mGeneration
 
     gl::ContextFormat format(gl::ContextFormat::BasicRGBA32);
     if (mOptions.depth) {
         format.depth = 24;
         format.minDepth = 16;
     }
 
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -59,19 +59,19 @@
 #include "nsIJSNativeInitializer.h"
 #include "nsContentUtils.h"
 #include "nsWrapperCache.h"
 #include "nsIObserver.h"
 
 #include "GLContextProvider.h"
 #include "Layers.h"
 
-#include "CheckedInt.h"
 #include "nsDataHashtable.h"
 
+#include "mozilla/CheckedInt.h"
 #include "mozilla/dom/ImageData.h"
 
 #ifdef XP_MACOSX
 #include "ForceDiscreteGPUHelperCGL.h"
 #endif
 
 #include "angle/ShaderLang.h"
 
@@ -476,17 +476,17 @@ public:
     {
         mArray.RemoveElementSorted(Entry(ElementType(), monotonicHandle),
                                    typename Entry::Comparator());
     }
 
 private:
     WebGLMonotonicHandle NextMonotonicHandle() {
         ++mCurrentMonotonicHandle;
-        if (!mCurrentMonotonicHandle.valid())
+        if (!mCurrentMonotonicHandle.isValid())
             NS_RUNTIMEABORT("ran out of monotonic ids!");
         return mCurrentMonotonicHandle.value();
     }
 
     nsTArray<Entry> mArray;
     CheckedInt<WebGLMonotonicHandle> mCurrentMonotonicHandle;
 };
 
@@ -1743,17 +1743,17 @@ public:
     }
 
     const ImageInfo& ImageInfoAt(size_t level, size_t face) const {
         return const_cast<WebGLTexture*>(this)->ImageInfoAt(level, face);
     }
 
     bool HasImageInfoAt(size_t level, size_t face) const {
         CheckedUint32 checked_index = CheckedUint32(level) * mFacesCount + face;
-        return checked_index.valid() &&
+        return checked_index.isValid() &&
                checked_index.value() < mImageInfos.Length() &&
                ImageInfoAt(level, face).mIsDefined;
     }
 
     static size_t FaceForTarget(WebGLenum target) {
         return target == LOCAL_GL_TEXTURE_2D ? 0 : target - LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
     }
 
@@ -2323,17 +2323,17 @@ public:
     bool HasBothShaderTypesAttached() {
         return
             HasAttachedShaderOfType(LOCAL_GL_VERTEX_SHADER) &&
             HasAttachedShaderOfType(LOCAL_GL_FRAGMENT_SHADER);
     }
 
     bool NextGeneration()
     {
-        if (!(mGeneration+1).valid())
+        if (!(mGeneration + 1).isValid())
             return false; // must exit without changing mGeneration
         ++mGeneration;
         return true;
     }
 
     /* Called only after LinkProgram */
     bool UpdateInfo();
 
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -682,17 +682,17 @@ WebGLContext::BufferSubData(GLenum targe
 
     if (byteOffset < 0)
         return ErrorInvalidValue("bufferSubData: negative offset");
 
     if (!boundBuffer)
         return ErrorInvalidOperation("BufferData: no buffer bound!");
 
     CheckedUint32 checked_neededByteLength = CheckedUint32(byteOffset) + data->mLength;
-    if (!checked_neededByteLength.valid())
+    if (!checked_neededByteLength.isValid())
         return ErrorInvalidOperation("bufferSubData: integer overflow computing the needed byte length");
 
     if (checked_neededByteLength.value() > boundBuffer->ByteLength())
         return ErrorInvalidOperation("BufferSubData: not enough data - operation requires %d bytes, but buffer only has %d bytes",
                                      checked_neededByteLength.value(), boundBuffer->ByteLength());
 
     MakeContextCurrent();
 
@@ -721,17 +721,17 @@ WebGLContext::BufferSubData(WebGLenum ta
 
     if (byteOffset < 0)
         return ErrorInvalidValue("bufferSubData: negative offset");
 
     if (!boundBuffer)
         return ErrorInvalidOperation("BufferSubData: no buffer bound!");
 
     CheckedUint32 checked_neededByteLength = CheckedUint32(byteOffset) + data.mLength;
-    if (!checked_neededByteLength.valid())
+    if (!checked_neededByteLength.isValid())
         return ErrorInvalidOperation("bufferSubData: integer overflow computing the needed byte length");
 
     if (checked_neededByteLength.value() > boundBuffer->ByteLength())
         return ErrorInvalidOperation("BufferSubData: not enough data -- operation requires %d bytes, but buffer only has %d bytes",
                                      checked_neededByteLength.value(), boundBuffer->ByteLength());
 
     MakeContextCurrent();
 
@@ -936,17 +936,17 @@ WebGLContext::CopyTexSubImage2D_base(Web
 
         PRUint32 texelSize = 0;
         if (!ValidateTexFormatAndType(internalformat, LOCAL_GL_UNSIGNED_BYTE, -1, &texelSize, info))
             return;
 
         CheckedUint32 checked_neededByteLength = 
             GetImageSize(height, width, texelSize, mPixelStoreUnpackAlignment);
 
-        if (!checked_neededByteLength.valid())
+        if (!checked_neededByteLength.isValid())
             return ErrorInvalidOperation("%s: integer overflow computing the needed buffer size", info);
 
         PRUint32 bytesNeeded = checked_neededByteLength.value();
 
         // now that the size is known, create the buffer
 
         // We need some zero pages, because GL doesn't guarantee the
         // contents of a texture allocated with NULL data.
@@ -1557,17 +1557,17 @@ WebGLContext::DoFakeVertexAttrib0(WebGLu
 {
     int whatDoesAttrib0Need = WhatDoesVertexAttrib0Need();
 
     if (whatDoesAttrib0Need == VertexAttrib0Status::Default)
         return true;
 
     CheckedUint32 checked_dataSize = CheckedUint32(vertexCount) * 4 * sizeof(WebGLfloat);
     
-    if (!checked_dataSize.valid()) {
+    if (!checked_dataSize.isValid()) {
         ErrorOutOfMemory("Integer overflow trying to construct a fake vertex attrib 0 array for a draw-operation "
                          "with %d vertices. Try reducing the number of vertices.", vertexCount);
         return false;
     }
     
     WebGLuint dataSize = checked_dataSize.value();
 
     if (!mFakeVertexAttrib0BufferObject) {
@@ -1774,17 +1774,17 @@ WebGLContext::DrawArrays(GLenum mode, We
         return;
 
     PRInt32 maxAllowedCount = 0;
     if (!ValidateBuffers(&maxAllowedCount, "drawArrays"))
         return;
 
     CheckedInt32 checked_firstPlusCount = CheckedInt32(first) + count;
 
-    if (!checked_firstPlusCount.valid())
+    if (!checked_firstPlusCount.isValid())
         return ErrorInvalidOperation("drawArrays: overflow in first+count");
 
     if (checked_firstPlusCount.value() > maxAllowedCount)
         return ErrorInvalidOperation("drawArrays: bound vertex attribute buffers do not have sufficient size for given first and count");
 
     MakeContextCurrent();
 
     if (mBoundFramebuffer) {
@@ -1842,63 +1842,63 @@ WebGLContext::DrawElements(WebGLenum mod
         if (byteOffset % 2 != 0)
             return ErrorInvalidOperation("DrawElements: invalid byteOffset for UNSIGNED_SHORT (must be a multiple of 2)");
     } else if (type == LOCAL_GL_UNSIGNED_BYTE) {
         checked_byteCount = count;
     } else {
         return ErrorInvalidEnum("DrawElements: type must be UNSIGNED_SHORT or UNSIGNED_BYTE");
     }
 
-    if (!checked_byteCount.valid())
+    if (!checked_byteCount.isValid())
         return ErrorInvalidValue("DrawElements: overflow in byteCount");
 
     // If there is no current program, this is silently ignored.
     // Any checks below this depend on a program being available.
     if (!mCurrentProgram)
         return;
 
     if (!mBoundElementArrayBuffer)
         return ErrorInvalidOperation("DrawElements: must have element array buffer binding");
 
     if (!mBoundElementArrayBuffer->Data())
         return ErrorInvalidOperation("drawElements: bound element array buffer doesn't have any data");
 
     CheckedUint32 checked_neededByteCount = checked_byteCount + byteOffset;
 
-    if (!checked_neededByteCount.valid())
+    if (!checked_neededByteCount.isValid())
         return ErrorInvalidOperation("DrawElements: overflow in byteOffset+byteCount");
 
     if (checked_neededByteCount.value() > mBoundElementArrayBuffer->ByteLength())
         return ErrorInvalidOperation("DrawElements: bound element array buffer is too small for given count and offset");
 
     PRInt32 maxAllowedCount = 0;
     if (!ValidateBuffers(&maxAllowedCount, "drawElements"))
       return;
 
     PRInt32 maxIndex
       = type == LOCAL_GL_UNSIGNED_SHORT
         ? mBoundElementArrayBuffer->FindMaxUshortElement()
         : mBoundElementArrayBuffer->FindMaxUbyteElement();
 
     CheckedInt32 checked_maxIndexPlusOne = CheckedInt32(maxIndex) + 1;
 
-    if (!checked_maxIndexPlusOne.valid() ||
+    if (!checked_maxIndexPlusOne.isValid() ||
         checked_maxIndexPlusOne.value() > maxAllowedCount)
     {
         // the index array contains invalid indices for the current drawing state, but they
         // might not be used by the present drawElements call, depending on first and count.
 
         PRInt32 maxIndexInSubArray
           = type == LOCAL_GL_UNSIGNED_SHORT
             ? mBoundElementArrayBuffer->FindMaxElementInSubArray<GLushort>(count, byteOffset)
             : mBoundElementArrayBuffer->FindMaxElementInSubArray<GLubyte>(count, byteOffset);
 
         CheckedInt32 checked_maxIndexInSubArrayPlusOne = CheckedInt32(maxIndexInSubArray) + 1;
 
-        if (!checked_maxIndexInSubArrayPlusOne.valid() ||
+        if (!checked_maxIndexInSubArrayPlusOne.isValid() ||
             checked_maxIndexInSubArrayPlusOne.value() > maxAllowedCount)
         {
             return ErrorInvalidOperation(
                 "DrawElements: bound vertex attribute buffers do not have sufficient "
                 "size for given indices from the bound element array");
         }
     }
 
@@ -3878,17 +3878,17 @@ WebGLContext::ReadPixels(WebGLint x, Web
     CheckedUint32 checked_neededByteLength =
         GetImageSize(height, width, bytesPerPixel, mPixelStorePackAlignment);
 
     CheckedUint32 checked_plainRowSize = CheckedUint32(width) * bytesPerPixel;
 
     CheckedUint32 checked_alignedRowSize =
         RoundedToNextMultipleOf(checked_plainRowSize, mPixelStorePackAlignment);
 
-    if (!checked_neededByteLength.valid())
+    if (!checked_neededByteLength.isValid())
         return ErrorInvalidOperation("ReadPixels: integer overflow computing the needed buffer size");
 
     if (checked_neededByteLength.value() > dataByteLen)
         return ErrorInvalidOperation("ReadPixels: buffer too small");
 
     // Check the format and type params to assure they are an acceptable pair (as per spec)
     switch (format) {
         case LOCAL_GL_RGBA: {
@@ -5596,17 +5596,17 @@ WebGLContext::TexImage2D_base(WebGLenum 
     CheckedUint32 checked_neededByteLength = 
         GetImageSize(height, width, srcTexelSize, mPixelStoreUnpackAlignment);
 
     CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
 
     CheckedUint32 checked_alignedRowSize =
         RoundedToNextMultipleOf(checked_plainRowSize.value(), mPixelStoreUnpackAlignment);
 
-    if (!checked_neededByteLength.valid())
+    if (!checked_neededByteLength.isValid())
         return ErrorInvalidOperation("texImage2D: integer overflow computing the needed buffer size");
 
     PRUint32 bytesNeeded = checked_neededByteLength.value();
 
     if (byteLength && byteLength < bytesNeeded)
         return ErrorInvalidOperation("TexImage2D: not enough data for operation (need %d, have %d)",
                                  bytesNeeded, byteLength);
 
@@ -5837,17 +5837,17 @@ WebGLContext::TexSubImage2D_base(WebGLen
     CheckedUint32 checked_neededByteLength = 
         GetImageSize(height, width, srcTexelSize, mPixelStoreUnpackAlignment);
 
     CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize;
 
     CheckedUint32 checked_alignedRowSize = 
         RoundedToNextMultipleOf(checked_plainRowSize.value(), mPixelStoreUnpackAlignment);
 
-    if (!checked_neededByteLength.valid())
+    if (!checked_neededByteLength.isValid())
         return ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size");
 
     PRUint32 bytesNeeded = checked_neededByteLength.value();
  
     if (byteLength < bytesNeeded)
         return ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength);
 
     WebGLTexture *tex = activeBoundTextureForTarget(target);
--- a/content/canvas/src/WebGLContextValidate.cpp
+++ b/content/canvas/src/WebGLContextValidate.cpp
@@ -35,18 +35,17 @@
  * 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 ***** */
 
 #include "WebGLContext.h"
 
 #include "mozilla/Preferences.h"
-
-#include "CheckedInt.h"
+#include "mozilla/CheckedInt.h"
 
 #include "jsfriendapi.h"
 
 #if defined(USE_ANGLE)
 #include "angle/ShaderLang.h"
 #endif
 
 #include <algorithm>
@@ -138,30 +137,30 @@ WebGLContext::ValidateBuffers(PRInt32 *m
             continue;
 
         // the base offset
         CheckedInt32 checked_byteLength
           = CheckedInt32(vd.buf->ByteLength()) - vd.byteOffset;
         CheckedInt32 checked_sizeOfLastElement
           = CheckedInt32(vd.componentSize()) * vd.size;
 
-        if (!checked_byteLength.valid() ||
-            !checked_sizeOfLastElement.valid())
+        if (!checked_byteLength.isValid() ||
+            !checked_sizeOfLastElement.isValid())
         {
           ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
           return false;
         }
 
         if (checked_byteLength.value() < checked_sizeOfLastElement.value()) {
           *maxAllowedCount = 0;
         } else {
           CheckedInt32 checked_maxAllowedCount
             = ((checked_byteLength - checked_sizeOfLastElement) / vd.actualStride()) + 1;
 
-          if (!checked_maxAllowedCount.valid()) {
+          if (!checked_maxAllowedCount.isValid()) {
             ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
             return false;
           }
 
           if (*maxAllowedCount == -1 || *maxAllowedCount > checked_maxAllowedCount.value())
               *maxAllowedCount = checked_maxAllowedCount.value();
         }
     }
@@ -396,37 +395,37 @@ bool WebGLContext::ValidateTexImage2DTar
     return true;
 }
 
 bool WebGLContext::ValidateCompressedTextureSize(WebGLint level, WebGLenum format, WebGLsizei width,
                                                  WebGLsizei height, uint32_t byteLength, const char* info)
 {
     CheckedUint32 calculated_byteLength = 0;
     CheckedUint32 checked_byteLength = byteLength;
-    if (!checked_byteLength.valid()) {
+    if (!checked_byteLength.isValid()) {
         ErrorInvalidValue("%s: data length out of bounds", info);
         return false;
     }
 
     switch (format) {
         case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
         case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
         {
             calculated_byteLength = ((CheckedUint32(width) + 3) / 4) * ((CheckedUint32(height) + 3) / 4) * 8;
-            if (!calculated_byteLength.valid() || !(checked_byteLength == calculated_byteLength)) {
+            if (!calculated_byteLength.isValid() || !(checked_byteLength == calculated_byteLength)) {
                 ErrorInvalidValue("%s: data size does not match dimensions", info);
                 return false;
             }
             break;
         }
         case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
         case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
         {
             calculated_byteLength = ((CheckedUint32(width) + 3) / 4) * ((CheckedUint32(height) + 3) / 4) * 16;
-            if (!calculated_byteLength.valid() || !(checked_byteLength == calculated_byteLength)) {
+            if (!calculated_byteLength.isValid() || !(checked_byteLength == calculated_byteLength)) {
                 ErrorInvalidValue("%s: data size does not match dimensions", info);
                 return false;
             }
             break;
         }
     }
 
     switch (format) {
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -103,24 +103,24 @@
 #include "nsFrameLoader.h"
 #include "nsBidi.h"
 #include "nsBidiPresUtils.h"
 #include "Layers.h"
 #include "CanvasUtils.h"
 #include "nsIMemoryReporter.h"
 #include "nsStyleUtil.h"
 #include "CanvasImageCache.h"
-#include "CheckedInt.h"
 
 #include <algorithm>
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
 #include "mozilla/Assertions.h"
+#include "mozilla/CheckedInt.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/PBrowserParent.h"
 #include "mozilla/ipc/DocumentRendererParent.h"
 #include "mozilla/ipc/PDocumentRendererParent.h"
 
 // windows.h (included by chromium code) defines this, in its infinite wisdom
 #undef DrawText
@@ -3921,24 +3921,24 @@ nsCanvasRenderingContext2D::GetImageData
                                               int32_t aY,
                                               uint32_t aWidth,
                                               uint32_t aHeight,
                                               JSObject** aRetval)
 {
     MOZ_ASSERT(aWidth && aHeight);
 
     CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
-    if (!len.valid()) {
+    if (!len.isValid()) {
         return NS_ERROR_DOM_INDEX_SIZE_ERR;
     }
 
     CheckedInt<int32_t> rightMost = CheckedInt<int32_t>(aX) + aWidth;
     CheckedInt<int32_t> bottomMost = CheckedInt<int32_t>(aY) + aHeight;
 
-    if (!rightMost.valid() || !bottomMost.valid()) {
+    if (!rightMost.isValid() || !bottomMost.isValid()) {
         return NS_ERROR_DOM_SYNTAX_ERR;
     }
 
     JSObject* darray = JS_NewUint8ClampedArray(aCx, len.value());
     if (!darray) {
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
@@ -4061,29 +4061,29 @@ nsCanvasRenderingContext2D::PutImageData
 
     if (hasDirtyRect) {
         // fix up negative dimensions
         if (dirtyWidth < 0) {
             NS_ENSURE_TRUE(dirtyWidth != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
 
             CheckedInt32 checkedDirtyX = CheckedInt32(dirtyX) + dirtyWidth;
 
-            if (!checkedDirtyX.valid())
+            if (!checkedDirtyX.isValid())
                 return NS_ERROR_DOM_INDEX_SIZE_ERR;
 
             dirtyX = checkedDirtyX.value();
             dirtyWidth = -(int32)dirtyWidth;
         }
 
         if (dirtyHeight < 0) {
             NS_ENSURE_TRUE(dirtyHeight != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
 
             CheckedInt32 checkedDirtyY = CheckedInt32(dirtyY) + dirtyHeight;
 
-            if (!checkedDirtyY.valid())
+            if (!checkedDirtyY.isValid())
                 return NS_ERROR_DOM_INDEX_SIZE_ERR;
 
             dirtyY = checkedDirtyY.value();
             dirtyHeight = -(int32)dirtyHeight;
         }
 
         // bound the dirty rect within the imageData rectangle
         dirtyRect = imageDataRect.Intersect(gfxRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight));
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
@@ -99,24 +99,24 @@
 #include "nsFrameLoader.h"
 #include "nsBidi.h"
 #include "nsBidiPresUtils.h"
 #include "Layers.h"
 #include "CanvasUtils.h"
 #include "nsIMemoryReporter.h"
 #include "nsStyleUtil.h"
 #include "CanvasImageCache.h"
-#include "CheckedInt.h"
 
 #include <algorithm>
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
 #include "mozilla/Assertions.h"
+#include "mozilla/CheckedInt.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/PBrowserParent.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/ipc/DocumentRendererParent.h"
 #include "mozilla/ipc/PDocumentRendererParent.h"
 #include "mozilla/Preferences.h"
@@ -4090,24 +4090,24 @@ nsCanvasRenderingContext2DAzure::GetImag
                                                    int32_t aY,
                                                    uint32_t aWidth,
                                                    uint32_t aHeight,
                                                    JSObject** aRetval)
 {
   MOZ_ASSERT(aWidth && aHeight);
 
   CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
-  if (!len.valid()) {
+  if (!len.isValid()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   CheckedInt<int32_t> rightMost = CheckedInt<int32_t>(aX) + aWidth;
   CheckedInt<int32_t> bottomMost = CheckedInt<int32_t>(aY) + aHeight;
 
-  if (!rightMost.valid() || !bottomMost.valid()) {
+  if (!rightMost.isValid() || !bottomMost.isValid()) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   JSObject* darray = JS_NewUint8ClampedArray(aCx, len.value());
   if (!darray) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
@@ -4228,29 +4228,29 @@ nsCanvasRenderingContext2DAzure::PutImag
 
   if (hasDirtyRect) {
     // fix up negative dimensions
     if (dirtyWidth < 0) {
       NS_ENSURE_TRUE(dirtyWidth != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
 
       CheckedInt32 checkedDirtyX = CheckedInt32(dirtyX) + dirtyWidth;
 
-      if (!checkedDirtyX.valid())
+      if (!checkedDirtyX.isValid())
           return NS_ERROR_DOM_INDEX_SIZE_ERR;
 
       dirtyX = checkedDirtyX.value();
       dirtyWidth = -(int32)dirtyWidth;
     }
 
     if (dirtyHeight < 0) {
       NS_ENSURE_TRUE(dirtyHeight != INT_MIN, NS_ERROR_DOM_INDEX_SIZE_ERR);
 
       CheckedInt32 checkedDirtyY = CheckedInt32(dirtyY) + dirtyHeight;
 
-      if (!checkedDirtyY.valid())
+      if (!checkedDirtyY.isValid())
           return NS_ERROR_DOM_INDEX_SIZE_ERR;
 
       dirtyY = checkedDirtyY.value();
       dirtyHeight = -(int32)dirtyHeight;
     }
 
     // bound the dirty rect within the imageData rectangle
     dirtyRect = imageDataRect.Intersect(IntRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight));
--- a/content/html/content/src/nsHTMLCanvasElement.cpp
+++ b/content/html/content/src/nsHTMLCanvasElement.cpp
@@ -33,20 +33,20 @@
  * 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 ***** */
 
 #include "nsHTMLCanvasElement.h"
 
 #include "mozilla/Base64.h"
+#include "mozilla/CheckedInt.h"
 #include "nsNetUtil.h"
 #include "prmem.h"
 #include "nsDOMFile.h"
-#include "CheckedInt.h"
 
 #include "nsIScriptSecurityManager.h"
 #include "nsIXPConnect.h"
 #include "jsapi.h"
 #include "nsContentUtils.h"
 #include "nsJSUtils.h"
 #include "nsMathUtils.h"
 #include "nsStreamUtils.h"
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -93,16 +93,19 @@
 #include "nsIDOMNotifyAudioAvailableEvent.h"
 #include "nsMediaFragmentURIParser.h"
 #include "nsURIHashKey.h"
 #include "nsJSUtils.h"
 #include "MediaStreamGraph.h"
 #include "nsDOMMediaStream.h"
 #include "nsIScriptError.h"
 
+#include "nsCSSParser.h"
+#include "nsIMediaList.h"
+
 #ifdef MOZ_OGG
 #include "nsOggDecoder.h"
 #endif
 #ifdef MOZ_WAVE
 #include "nsWaveDecoder.h"
 #endif
 #ifdef MOZ_WEBM
 #include "nsWebMDecoder.h"
@@ -845,16 +848,22 @@ void nsHTMLMediaElement::NotifyAudioAvai
 }
 
 void nsHTMLMediaElement::LoadFromSourceChildren()
 {
   NS_ASSERTION(mDelayingLoadEvent,
                "Should delay load event (if in document) during load");
   NS_ASSERTION(mIsLoadingFromSourceChildren,
                "Must remember we're loading from source children");
+
+  nsIDocument* parentDoc = OwnerDoc()->GetParentDocument();
+  if (parentDoc) {
+    parentDoc->FlushPendingNotifications(Flush_Layout);
+  }
+
   while (true) {
     nsIContent* child = GetNextSource();
     if (!child) {
       // Exhausted candidates, wait for more candidates to be appended to
       // the media element.
       mLoadWaitStatus = WAITING_FOR_SOURCE;
       mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE;
       ChangeDelayLoadStatus(false);
@@ -871,21 +880,35 @@ void nsHTMLMediaElement::LoadFromSourceC
     }
 
     // If we have a type attribute, it must be a supported type.
     nsAutoString type;
     if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type) &&
         GetCanPlay(type) == CANPLAY_NO) {
       DispatchAsyncSourceError(child);
       const PRUnichar* params[] = { type.get(), src.get() };
-      ReportLoadError("MediaLoadUnsupportedType", params, ArrayLength(params));
+      ReportLoadError("MediaLoadUnsupportedTypeAttribute", params, ArrayLength(params));
       continue;
     }
-    LOG(PR_LOG_DEBUG, ("%p Trying load from <source>=%s type=%s", this,
-      NS_ConvertUTF16toUTF8(src).get(), NS_ConvertUTF16toUTF8(type).get()));
+    nsAutoString media;
+    if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::media, media) && !media.IsEmpty()) {
+      nsCSSParser cssParser;
+      nsRefPtr<nsMediaList> mediaList(new nsMediaList());
+      cssParser.ParseMediaList(media, NULL, 0, mediaList, false);
+      nsIPresShell* presShell = OwnerDoc()->GetShell();
+      if (presShell && !mediaList->Matches(presShell->GetPresContext(), NULL)) {
+        DispatchAsyncSourceError(child);
+        const PRUnichar* params[] = { media.get(), src.get() };
+        ReportLoadError("MediaLoadSourceMediaNotMatched", params, ArrayLength(params));
+        continue;
+      }
+    }
+    LOG(PR_LOG_DEBUG, ("%p Trying load from <source>=%s type=%s media=%s", this,
+      NS_ConvertUTF16toUTF8(src).get(), NS_ConvertUTF16toUTF8(type).get(),
+      NS_ConvertUTF16toUTF8(media).get()));
 
     nsCOMPtr<nsIURI> uri;
     NewURIFromString(src, getter_AddRefs(uri));
     if (!uri) {
       DispatchAsyncSourceError(child);
       const PRUnichar* params[] = { src.get() };
       ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params));
       continue;
--- a/content/html/content/src/nsHTMLSourceElement.cpp
+++ b/content/html/content/src/nsHTMLSourceElement.cpp
@@ -109,16 +109,17 @@ NS_INTERFACE_TABLE_HEAD(nsHTMLSourceElem
 NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLSourceElement)
 
 
 NS_IMPL_ELEMENT_CLONE(nsHTMLSourceElement)
 
 
 NS_IMPL_URI_ATTR(nsHTMLSourceElement, Src, src)
 NS_IMPL_STRING_ATTR(nsHTMLSourceElement, Type, type)
+NS_IMPL_STRING_ATTR(nsHTMLSourceElement, Media, media)
 
 nsresult
 nsHTMLSourceElement::BindToTree(nsIDocument *aDocument,
                                 nsIContent *aParent,
                                 nsIContent *aBindingParent,
                                 bool aCompileEventHandlers)
 {
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument,
--- a/content/mathml/content/src/nsMathMLElement.cpp
+++ b/content/mathml/content/src/nsMathMLElement.cpp
@@ -238,66 +238,122 @@ nsMapRuleToAttributesFunc
 nsMathMLElement::GetAttributeMappingFunction() const
 {
   // It doesn't really matter what our tag is here, because only attributes
   // that satisfy IsAttributeMapped will be stored in the mapped attributes
   // list and available to the mapping function
   return &MapMathMLAttributesInto;
 }
 
-// ================
-// Utilities for parsing and retrieving numeric values
-
-/*
-The REC says:
-  An explicit plus sign ('+') is not allowed as part of a numeric value
-  except when it is specifically listed in the syntax (as a quoted '+'
-  or "+"),
-
-  Units allowed
-  ID  Description
-  em  ems (font-relative unit traditionally used for horizontal lengths)
-  ex  exs (font-relative unit traditionally used for vertical lengths)
-  px  pixels, or pixel size of a "typical computer display"
-  in  inches (1 inch = 2.54 centimeters)
-  cm  centimeters
-  mm  millimeters
-  pt  points (1 point = 1/72 inch)
-  pc  picas (1 pica = 12 points)
-  %   percentage of default value
-
-Implementation here:
-  The numeric value is valid only if it is of the form [-] nnn.nnn
-  [h/v-unit]
-*/
-
+/* static */ bool
+nsMathMLElement::ParseNamedSpaceValue(const nsString& aString,
+                                      nsCSSValue&     aCSSValue,
+                                      PRUint32        aFlags)
+{
+   PRInt32 i = 0;
+   // See if it is one of the 'namedspace' (ranging -7/18em, -6/18, ... 7/18em)
+   if (aString.EqualsLiteral("veryverythinmathspace")) {
+     i = 1;
+   } else if (aString.EqualsLiteral("verythinmathspace")) {
+     i = 2;
+   } else if (aString.EqualsLiteral("thinmathspace")) {
+     i = 3;
+   } else if (aString.EqualsLiteral("mediummathspace")) {
+     i = 4;
+   } else if (aString.EqualsLiteral("thickmathspace")) {
+     i = 5;
+   } else if (aString.EqualsLiteral("verythickmathspace")) {
+     i = 6;
+   } else if (aString.EqualsLiteral("veryverythickmathspace")) {
+     i = 7;
+   } else if (aFlags & PARSE_ALLOW_NEGATIVE) {
+     if (aString.EqualsLiteral("negativeveryverythinmathspace")) {
+       i = -1;
+     } else if (aString.EqualsLiteral("negativeverythinmathspace")) {
+       i = -2;
+     } else if (aString.EqualsLiteral("negativethinmathspace")) {
+       i = -3;
+     } else if (aString.EqualsLiteral("negativemediummathspace")) {
+       i = -4;
+     } else if (aString.EqualsLiteral("negativethickmathspace")) {
+       i = -5;
+     } else if (aString.EqualsLiteral("negativeverythickmathspace")) {
+       i = -6;
+     } else if (aString.EqualsLiteral("negativeveryverythickmathspace")) {
+       i = -7;
+     }
+   }
+   if (0 != i) { 
+     aCSSValue.SetFloatValue(float(i)/float(18), eCSSUnit_EM);
+     return true;
+   }
+   
+   return false;
+}
+ 
+// The REC says:
+//
+// "Most presentation elements have attributes that accept values representing
+// lengths to be used for size, spacing or similar properties. The syntax of a
+// length is specified as
+//
+// number | number unit | namedspace
+//
+// There should be no space between the number and the unit of a length."
+// 
+// "A trailing '%' represents a percent of the default value. The default
+// value, or how it is obtained, is listed in the table of attributes for each
+// element. [...] A number without a unit is intepreted as a multiple of the
+// default value."
+//
+// "The possible units in MathML are:
+//  
+// Unit Description
+// em   an em (font-relative unit traditionally used for horizontal lengths)
+// ex   an ex (font-relative unit traditionally used for vertical lengths)
+// px   pixels, or size of a pixel in the current display
+// in   inches (1 inch = 2.54 centimeters)
+// cm   centimeters
+// mm   millimeters
+// pt   points (1 point = 1/72 inch)
+// pc   picas (1 pica = 12 points)
+// %    percentage of default value"
+//
+// The numbers are defined that way:
+// - unsigned-number: "a string of decimal digits with up to one decimal point
+//   (U+002E), representing a non-negative terminating decimal number (a type of
+//   rational number)"
+// - number: "an optional prefix of '-' (U+002D), followed by an unsigned
+//   number, representing a terminating decimal number (a type of rational
+//   number)"
+//
 /* static */ bool
 nsMathMLElement::ParseNumericValue(const nsString& aString,
                                    nsCSSValue&     aCSSValue,
                                    PRUint32        aFlags)
 {
   nsAutoString str(aString);
   str.CompressWhitespace(); // aString is const in this code...
 
   PRInt32 stringLength = str.Length();
   if (!stringLength)
     return false;
 
+  if (ParseNamedSpaceValue(aString, aCSSValue, aFlags)) {
+    return true;
+  }
+
   nsAutoString number, unit;
 
   // see if the negative sign is there
   PRInt32 i = 0;
   PRUnichar c = str[0];
   if (c == '-') {
     number.Append(c);
     i++;
-
-    // skip any space after the negative sign
-    if (i < stringLength && nsCRT::IsAsciiSpace(str[i]))
-      i++;
   }
 
   // Gather up characters that make up the number
   bool gotDot = false;
   for ( ; i < stringLength; i++) {
     c = str[i];
     if (gotDot && c == '.')
       return false;  // two dots encountered
@@ -353,16 +409,24 @@ nsMathMLElement::ParseNumericValue(const
   return true;
 }
 
 void
 nsMathMLElement::MapMathMLAttributesInto(const nsMappedAttributes* aAttributes,
                                          nsRuleData* aData)
 {
   if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Font)) {
+    // scriptsizemultiplier
+    //
+    // "Specifies the multiplier to be used to adjust font size due to changes
+    // in scriptlevel.
+    //
+    // values: number
+    // default: 0.71
+    //
     const nsAttrValue* value =
       aAttributes->GetAttr(nsGkAtoms::scriptsizemultiplier_);
     nsCSSValue* scriptSizeMultiplier =
       aData->ValueForScriptSizeMultiplier();
     if (value && value->Type() == nsAttrValue::eString &&
         scriptSizeMultiplier->GetUnit() == eCSSUnit_Null) {
       nsAutoString str(value->GetStringValue());
       str.CompressWhitespace();
@@ -372,23 +436,46 @@ nsMathMLElement::MapMathMLAttributesInto
         float floatValue = str.ToFloat(&errorCode);
         // Negative scriptsizemultipliers are not parsed
         if (NS_SUCCEEDED(errorCode) && floatValue >= 0.0f) {
           scriptSizeMultiplier->SetFloatValue(floatValue, eCSSUnit_Number);
         }
       }
     }
 
+    // scriptminsize
+    //
+    // "Specifies the minimum font size allowed due to changes in scriptlevel.
+    // Note that this does not limit the font size due to changes to mathsize."
+    //
+    // values: length
+    // default: 8pt
+    //
+    // We don't allow negative values.
+    // XXXfredw Should we allow unitless values? (bug 411227)
+    // XXXfredw Does a relative unit give a multiple of the default value?
+    //
     value = aAttributes->GetAttr(nsGkAtoms::scriptminsize_);
     nsCSSValue* scriptMinSize = aData->ValueForScriptMinSize();
     if (value && value->Type() == nsAttrValue::eString &&
         scriptMinSize->GetUnit() == eCSSUnit_Null) {
       ParseNumericValue(value->GetStringValue(), *scriptMinSize, 0);
     }
 
+    // scriptlevel
+    // 
+    // "Changes the scriptlevel in effect for the children. When the value is
+    // given without a sign, it sets scriptlevel to the specified value; when a
+    // sign is given, it increments ("+") or decrements ("-") the current
+    // value. (Note that large decrements can result in negative values of
+    // scriptlevel, but these values are considered legal.)"
+    //
+    // values: ( "+" | "-" )? unsigned-integer
+    // default: inherited
+    //
     value = aAttributes->GetAttr(nsGkAtoms::scriptlevel_);
     nsCSSValue* scriptLevel = aData->ValueForScriptLevel();
     if (value && value->Type() == nsAttrValue::eString &&
         scriptLevel->GetUnit() == eCSSUnit_Null) {
       nsAutoString str(value->GetStringValue());
       str.CompressWhitespace();
       if (str.Length() > 0) {
         PRInt32 errorCode;
@@ -403,16 +490,38 @@ nsMathMLElement::MapMathMLAttributesInto
             scriptLevel->SetIntValue(intValue, eCSSUnit_Integer);
           } else {
             scriptLevel->SetFloatValue(intValue, eCSSUnit_Number);
           }
         }
       }
     }
 
+    // mathsize
+    //
+    // "Specifies the size to display the token content. The values 'small' and
+    // 'big' choose a size smaller or larger than the current font size, but
+    // leave the exact proportions unspecified; 'normal' is allowed for
+    // completeness, but since it is equivalent to '100%' or '1em', it has no
+    // effect."
+    //
+    // values: "small" | "normal" | "big" | length
+    // default: inherited
+    //
+    // fontsize
+    //
+    // "Specified the size for the token. Deprecated in favor of mathsize."
+    //
+    // values: length
+    // default: inherited
+    //
+    // In both cases, we don't allow negative values.
+    // XXXfredw Should we allow unitless values? (bug 411227)
+    // XXXfredw Does a relative unit give a multiple of the default value?
+    //  
     bool parseSizeKeywords = true;
     value = aAttributes->GetAttr(nsGkAtoms::mathsize_);
     if (!value) {
       parseSizeKeywords = false;
       value = aAttributes->GetAttr(nsGkAtoms::fontsize_);
     }
     nsCSSValue* fontSize = aData->ValueForFontSize();
     if (value && value->Type() == nsAttrValue::eString &&
@@ -430,39 +539,82 @@ nsMathMLElement::MapMathMLAttributesInto
           if (str.EqualsASCII(sizes[i])) {
             fontSize->SetIntValue(values[i], eCSSUnit_Enumerated);
             break;
           }
         }
       }
     }
 
+    // fontfamily
+    //
+    // "Should be the name of a font that may be available to a MathML renderer,
+    // or a CSS font specification; See Section 6.5 Using CSS with MathML and
+    // CSS for more information. Deprecated in favor of mathvariant."
+    //
+    // values: string
+    // 
     value = aAttributes->GetAttr(nsGkAtoms::fontfamily_);
     nsCSSValue* fontFamily = aData->ValueForFontFamily();
     if (value && value->Type() == nsAttrValue::eString &&
         fontFamily->GetUnit() == eCSSUnit_Null) {
       fontFamily->SetStringValue(value->GetStringValue(), eCSSUnit_Families);
     }
   }
 
+  // mathbackground
+  // 
+  // "Specifies the background color to be used to fill in the bounding box of
+  // the element and its children. The default, 'transparent', lets the
+  // background color, if any, used in the current rendering context to show
+  // through."
+  // 
+  // values: color | "transparent" 
+  // default: "transparent"
+  //
+  // background
+  //
+  // "Specified the background color to be used to fill in the bounding box of
+  // the element and its children. Deprecated in favor of mathbackground."
+  //
+  // values: color | "transparent"
+  // default: "transparent"
+  //
   if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Background)) {
     const nsAttrValue* value =
       aAttributes->GetAttr(nsGkAtoms::mathbackground_);
     if (!value) {
       value = aAttributes->GetAttr(nsGkAtoms::background);
     }
     nsCSSValue* backgroundColor = aData->ValueForBackgroundColor();
     if (value && backgroundColor->GetUnit() == eCSSUnit_Null) {
       nscolor color;
       if (value->GetColorValue(color)) {
         backgroundColor->SetColorValue(color);
       }
     }
   }
 
+  // mathcolor
+  //
+  // "Specifies the foreground color to use when drawing the components of this
+  // element, such as the content for token elements or any lines, surds, or
+  // other decorations. It also establishes the default mathcolor used for
+  // child elements when used on a layout element."
+  //
+  // values: color
+  // default: inherited
+  //
+  // color
+  // 
+  // "Specified the color for the token. Deprecated in favor of mathcolor." 
+  //
+  // values: color
+  // default: inherited
+  //
   if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Color)) {
     const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::mathcolor_);
     if (!value) {
       value = aAttributes->GetAttr(nsGkAtoms::color);
     }
     nscolor color;
     nsCSSValue* colorValue = aData->ValueForColor();
     if (value && value->GetColorValue(color) &&
--- a/content/mathml/content/src/nsMathMLElement.h
+++ b/content/mathml/content/src/nsMathMLElement.h
@@ -84,16 +84,20 @@ public:
 
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
   virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const;
 
   enum {
     PARSE_ALLOW_UNITLESS = 0x01, // unitless 0 will be turned into 0px
     PARSE_ALLOW_NEGATIVE = 0x02
   };
+  static bool ParseNamedSpaceValue(const nsString& aString,
+                                   nsCSSValue&     aCSSValue,
+                                   PRUint32        aFlags);
+
   static bool ParseNumericValue(const nsString& aString,
                                   nsCSSValue&     aCSSValue,
                                   PRUint32        aFlags);
 
   static void MapMathMLAttributesInto(const nsMappedAttributes* aAttributes, 
                                       nsRuleData* aRuleData);
   
   virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
--- a/content/media/VideoUtils.h
+++ b/content/media/VideoUtils.h
@@ -35,23 +35,22 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef VideoUtils_h
 #define VideoUtils_h
 
 #include "mozilla/ReentrantMonitor.h"
+#include "mozilla/CheckedInt.h"
 
 #include "nsRect.h"
 #include "nsIThreadManager.h"
 #include "nsThreadUtils.h"
 
-#include "CheckedInt.h"
-
 using mozilla::CheckedInt64;
 using mozilla::CheckedUint64;
 using mozilla::CheckedInt32;
 using mozilla::CheckedUint32;
 
 // This file contains stuff we'd rather put elsewhere, but which is
 // dependent on other changes which we don't want to wait for. We plan to
 // remove this file in the near future.
--- a/content/media/nsBuiltinDecoderReader.cpp
+++ b/content/media/nsBuiltinDecoderReader.cpp
@@ -159,18 +159,18 @@ VideoData* VideoData::Create(nsVideoInfo
     NS_WARNING("Invalid plane size");
     return nsnull;
   }
 
   // Ensure the picture size specified in the headers can be extracted out of
   // the frame we've been supplied without indexing out of bounds.
   CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
   CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height);
-  if (!xLimit.valid() || xLimit.value() > aBuffer.mPlanes[0].mStride ||
-      !yLimit.valid() || yLimit.value() > aBuffer.mPlanes[0].mHeight)
+  if (!xLimit.isValid() || xLimit.value() > aBuffer.mPlanes[0].mStride ||
+      !yLimit.isValid() || yLimit.value() > aBuffer.mPlanes[0].mHeight)
   {
     // The specified picture dimensions can't be contained inside the video
     // frame, we'll stomp memory if we try to copy it. Fail.
     NS_WARNING("Overflowing picture rect");
     return nsnull;
   }
 
   nsAutoPtr<VideoData> v(new VideoData(aOffset,
@@ -341,17 +341,17 @@ nsresult nsBuiltinDecoderReader::DecodeT
           }
         }
       }
       const AudioData* audio = mAudioQueue.PeekFront();
       if (!audio)
         break;
       CheckedInt64 startFrame = UsecsToFrames(audio->mTime, mInfo.mAudioRate);
       CheckedInt64 targetFrame = UsecsToFrames(aTarget, mInfo.mAudioRate);
-      if (!startFrame.valid() || !targetFrame.valid()) {
+      if (!startFrame.isValid() || !targetFrame.isValid()) {
         return NS_ERROR_FAILURE;
       }
       if (startFrame.value() + audio->mFrames <= targetFrame.value()) {
         // Our seek target lies after the frames in this AudioData. Pop it
         // off the queue, and keep decoding forwards.
         delete mAudioQueue.PopFront();
         audio = nsnull;
         continue;
@@ -385,17 +385,17 @@ nsresult nsBuiltinDecoderReader::DecodeT
       }
       PRUint32 frames = audio->mFrames - static_cast<PRUint32>(framesToPrune);
       PRUint32 channels = audio->mChannels;
       nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[frames * channels]);
       memcpy(audioData.get(),
              audio->mAudioData.get() + (framesToPrune * channels),
              frames * channels * sizeof(AudioDataValue));
       CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudioRate);
-      if (!duration.valid()) {
+      if (!duration.isValid()) {
         return NS_ERROR_FAILURE;
       }
       nsAutoPtr<AudioData> data(new AudioData(audio->mOffset,
                                               aTarget,
                                               duration.value(),
                                               frames,
                                               audioData.forget(),
                                               channels));
--- a/content/media/nsBuiltinDecoderStateMachine.cpp
+++ b/content/media/nsBuiltinDecoderStateMachine.cpp
@@ -542,17 +542,17 @@ void nsBuiltinDecoderStateMachine::SendO
   NS_ASSERTION(aOutput->GetChannels() == aAudio->mChannels,
                "Wrong number of channels");
 
   // This logic has to mimic AudioLoop closely to make sure we write
   // the exact same silences
   CheckedInt64 audioWrittenOffset = UsecsToFrames(mInfo.mAudioRate,
       aStream->mAudioFramesWrittenBaseTime + mStartTime) + aStream->mAudioFramesWritten;
   CheckedInt64 frameOffset = UsecsToFrames(mInfo.mAudioRate, aAudio->mTime);
-  if (!audioWrittenOffset.valid() || !frameOffset.valid())
+  if (!audioWrittenOffset.isValid() || !frameOffset.isValid())
     return;
   if (audioWrittenOffset.value() < frameOffset.value()) {
     // Write silence to catch up
     LOG(PR_LOG_DEBUG, ("%p Decoder writing %d frames of silence to MediaStream",
                        mDecoder.get(), PRInt32(frameOffset.value() - audioWrittenOffset.value())));
     AudioSegment silence;
     silence.InitFrom(*aOutput);
     silence.InsertNullDataAtStart(frameOffset.value() - audioWrittenOffset.value());
@@ -1110,17 +1110,17 @@ void nsBuiltinDecoderStateMachine::Audio
     // Calculate the number of frames that have been pushed onto the audio
     // hardware.
     CheckedInt64 playedFrames = UsecsToFrames(audioStartTime, rate) +
                                               audioDuration;
     // Calculate the timestamp of the next chunk of audio in numbers of
     // samples.
     CheckedInt64 sampleTime = UsecsToFrames(s->mTime, rate);
     CheckedInt64 missingFrames = sampleTime - playedFrames;
-    if (!missingFrames.valid() || !sampleTime.valid()) {
+    if (!missingFrames.isValid() || !sampleTime.isValid()) {
       NS_WARNING("Int overflow adding in AudioLoop()");
       break;
     }
 
     PRInt64 framesWritten = 0;
     if (missingFrames.value() > 0) {
       // The next audio chunk begins some time after the end of the last chunk
       // we pushed to the audio hardware. We must push silence into the audio
@@ -1134,17 +1134,17 @@ void nsBuiltinDecoderStateMachine::Audio
                                   channels, playedFrames.value());
     } else {
       framesWritten = PlayFromAudioQueue(sampleTime.value(), channels);
     }
     audioDuration += framesWritten;
     {
       ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
       CheckedInt64 playedUsecs = FramesToUsecs(audioDuration, rate) + audioStartTime;
-      if (!playedUsecs.valid()) {
+      if (!playedUsecs.isValid()) {
         NS_WARNING("Int overflow calculating audio end time");
         break;
       }
       mAudioEndTime = playedUsecs.value();
     }
   }
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
--- a/content/media/ogg/nsOggCodecState.cpp
+++ b/content/media/ogg/nsOggCodecState.cpp
@@ -348,28 +348,28 @@ PRInt64 nsTheoraState::Time(th_info* aIn
   }
   // Implementation of th_granule_frame inlined here to operate
   // on the th_info structure instead of the theora_state.
   int shift = aInfo->keyframe_granule_shift; 
   ogg_int64_t iframe = aGranulepos >> shift;
   ogg_int64_t pframe = aGranulepos - (iframe << shift);
   PRInt64 frameno = iframe + pframe - TH_VERSION_CHECK(aInfo, 3, 2, 1);
   CheckedInt64 t = ((CheckedInt64(frameno) + 1) * USECS_PER_S) * aInfo->fps_denominator;
-  if (!t.valid())
+  if (!t.isValid())
     return -1;
   t /= aInfo->fps_numerator;
-  return t.valid() ? t.value() : -1;
+  return t.isValid() ? t.value() : -1;
 }
 
 PRInt64 nsTheoraState::StartTime(PRInt64 granulepos) {
   if (granulepos < 0 || !mActive || mInfo.fps_numerator == 0) {
     return -1;
   }
   CheckedInt64 t = (CheckedInt64(th_granule_frame(mCtx, granulepos)) * USECS_PER_S) * mInfo.fps_denominator;
-  if (!t.valid())
+  if (!t.isValid())
     return -1;
   return t.value() / mInfo.fps_numerator;
 }
 
 PRInt64
 nsTheoraState::MaxKeyframeOffset()
 {
   // Determine the maximum time in microseconds by which a key frame could
@@ -617,17 +617,17 @@ PRInt64 nsVorbisState::Time(PRInt64 gran
 }
 
 PRInt64 nsVorbisState::Time(vorbis_info* aInfo, PRInt64 aGranulepos)
 {
   if (aGranulepos == -1 || aInfo->rate == 0) {
     return -1;
   }
   CheckedInt64 t = CheckedInt64(aGranulepos) * USECS_PER_S;
-  if (!t.valid())
+  if (!t.isValid())
     t = 0;
   return t.value() / aInfo->rate;
 }
 
 bool
 nsVorbisState::IsHeader(ogg_packet* aPacket)
 {
   // The first byte in each Vorbis header packet is either 0x01, 0x03, or 0x05,
@@ -879,17 +879,17 @@ bool nsOpusState::DecodeHeader(ogg_packe
 /* Return the timestamp (in microseconds) equivalent to a granulepos. */
 PRInt64 nsOpusState::Time(PRInt64 granulepos)
 {
   if (!mActive || granulepos < 0)
     return -1;
 
   // Ogg Opus always runs at a granule rate of 48 kHz.
   CheckedInt64 t = CheckedInt64(granulepos - mPreSkip) * USECS_PER_S;
-  return t.valid() ? t.value() / mRate : -1;
+  return t.isValid() ? t.value() / mRate : -1;
 }
 
 bool nsOpusState::IsHeader(ogg_packet* aPacket)
 {
   return aPacket->bytes >= 16 &&
          (!memcmp(aPacket->packet, "OpusHead\0", 9) ||
           !memcmp(aPacket->packet, "OpusTags", 8));
 }
@@ -1043,34 +1043,34 @@ bool nsSkeletonState::DecodeIndex(ogg_pa
   if (timeDenom == 0) {
     LOG(PR_LOG_DEBUG, ("Ogg Skeleton Index packet for stream %u has 0 "
                        "timestamp denominator.", serialno));
     return (mActive = false);
   }
 
   // Extract the start time.
   CheckedInt64 t = CheckedInt64(LEInt64(p + INDEX_FIRST_NUMER_OFFSET)) * USECS_PER_S;
-  if (!t.valid()) {
+  if (!t.isValid()) {
     return (mActive = false);
   } else {
     startTime = t.value() / timeDenom;
   }
 
   // Extract the end time.
   t = LEInt64(p + INDEX_LAST_NUMER_OFFSET) * USECS_PER_S;
-  if (!t.valid()) {
+  if (!t.isValid()) {
     return (mActive = false);
   } else {
     endTime = t.value() / timeDenom;
   }
 
   // Check the numKeyPoints value read, ensure we're not going to run out of
   // memory while trying to decode the index packet.
   CheckedInt64 minPacketSize = (CheckedInt64(numKeyPoints) * MIN_KEY_POINT_SIZE) + INDEX_KEYPOINT_OFFSET;
-  if (!minPacketSize.valid())
+  if (!minPacketSize.isValid())
   {
     return (mActive = false);
   }
   
   PRInt64 sizeofIndex = aPacket->bytes - INDEX_KEYPOINT_OFFSET;
   PRInt64 maxNumKeyPoints = sizeofIndex / MIN_KEY_POINT_SIZE;
   if (aPacket->bytes < minPacketSize.value() ||
       numKeyPoints > maxNumKeyPoints || 
@@ -1098,32 +1098,32 @@ bool nsSkeletonState::DecodeIndex(ogg_pa
   CheckedInt64 time = 0;
   while (p < limit &&
          numKeyPointsRead < numKeyPoints)
   {
     PRInt64 delta = 0;
     p = ReadVariableLengthInt(p, limit, delta);
     offset += delta;
     if (p == limit ||
-        !offset.valid() ||
+        !offset.isValid() ||
         offset.value() > mLength ||
         offset.value() < 0)
     {
       return (mActive = false);
     }
     p = ReadVariableLengthInt(p, limit, delta);
     time += delta;
-    if (!time.valid() ||
+    if (!time.isValid() ||
         time.value() > endTime ||
         time.value() < startTime)
     {
       return (mActive = false);
     }
     CheckedInt64 timeUsecs = time * USECS_PER_S;
-    if (!timeUsecs.valid())
+    if (!timeUsecs.isValid())
       return mActive = false;
     timeUsecs /= timeDenom;
     keyPoints->Add(offset.value(), timeUsecs.value());
     numKeyPointsRead++;
   }
 
   PRInt32 keyPointsRead = keyPoints->Length();
   if (keyPointsRead > 0) {
@@ -1223,18 +1223,18 @@ nsresult nsSkeletonState::GetDuration(co
       endTime = index->mEndTime;
     }
     if (index->mStartTime < startTime) {
       startTime = index->mStartTime;
     }
   }
   NS_ASSERTION(endTime > startTime, "Duration must be positive");
   CheckedInt64 duration = CheckedInt64(endTime) - startTime;
-  aDuration = duration.valid() ? duration.value() : 0;
-  return duration.valid() ? NS_OK : NS_ERROR_FAILURE;
+  aDuration = duration.isValid() ? duration.value() : 0;
+  return duration.isValid() ? NS_OK : NS_ERROR_FAILURE;
 }
 
 bool nsSkeletonState::DecodeHeader(ogg_packet* aPacket)
 {
   if (IsSkeletonBOS(aPacket)) {
     PRUint16 verMajor = LEUint16(aPacket->packet + SKELETON_VERSION_MAJOR_OFFSET);
     PRUint16 verMinor = LEUint16(aPacket->packet + SKELETON_VERSION_MINOR_OFFSET);
 
--- a/content/media/raw/nsRawReader.cpp
+++ b/content/media/raw/nsRawReader.cpp
@@ -82,17 +82,17 @@ nsresult nsRawReader::ReadMetadata(nsVid
   if (!(mMetadata.headerPacketID == 0 /* Packet ID of 0 for the header*/ &&
         mMetadata.codecID == RAW_ID /* "YUV" */ &&
         mMetadata.majorVersion == 0 &&
         mMetadata.minorVersion == 1))
     return NS_ERROR_FAILURE;
 
   CheckedUint32 dummy = CheckedUint32(static_cast<PRUint32>(mMetadata.frameWidth)) *
                           static_cast<PRUint32>(mMetadata.frameHeight);
-  NS_ENSURE_TRUE(dummy.valid(), NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(dummy.isValid(), NS_ERROR_FAILURE);
 
   if (mMetadata.aspectDenominator == 0 ||
       mMetadata.framerateDenominator == 0)
     return NS_ERROR_FAILURE; // Invalid data
 
   // Determine and verify frame display size.
   float pixelAspectRatio = static_cast<float>(mMetadata.aspectNumerator) / 
                             mMetadata.aspectDenominator;
@@ -263,17 +263,17 @@ nsresult nsRawReader::Seek(PRInt64 aTime
 
   PRUint32 frame = mCurrentFrame;
   if (aTime >= UINT_MAX)
     return NS_ERROR_FAILURE;
   mCurrentFrame = aTime * mFrameRate / USECS_PER_S;
 
   CheckedUint32 offset = CheckedUint32(mCurrentFrame) * mFrameSize;
   offset += sizeof(nsRawVideoHeader);
-  NS_ENSURE_TRUE(offset.valid(), NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(offset.isValid(), NS_ERROR_FAILURE);
 
   nsresult rv = resource->Seek(nsISeekableStream::NS_SEEK_SET, offset.value());
   NS_ENSURE_SUCCESS(rv, rv);
 
   mVideoQueue.Erase();
 
   while(mVideoQueue.GetSize() == 0) {
     bool keyframeSkip = false;
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -156,16 +156,17 @@ include $(topsrcdir)/config/rules.mk
 		test_streams_element_capture_reset.html \
 		test_timeupdate_small_files.html \
 		test_too_many_elements.html \
 		test_volume.html \
 		test_video_to_canvas.html \
 		use_large_cache.js \
 		test_audiowrite.html \
 		test_mozHasAudio.html \
+		test_source_media.html \
 		$(NULL)
 
 # Don't run in suite
 ifndef MOZ_SUITE
 _TEST_FILES += test_play_twice.html
 else
 $(warning test_play_twice.html is disabled pending investigation. Bug 598252)
 endif
new file mode 100644
--- /dev/null
+++ b/content/media/test/test_source_media.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Media test: media attribute for the source element.</title>
+<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<script type="text/javascript" src="manifest.js"></script>
+<script type="text/javascript" src="../../html/content/test/reflect.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="text/javascript">
+  var testCount = 0;
+  function notifyFinished() {
+    testCount++;
+    if (testCount == 2) {
+      SimpleTest.finish();
+    }
+  }
+  SimpleTest.waitForExplicitFinish();
+
+  reflectString({
+    element: document.createElement("source"),
+    attribute: "media",
+  });
+
+  var media = getPlayableVideo(gSmallTests);
+
+  if (media == null) {
+    todo(false, "No media supported.");
+    SimpleTest.finish();
+  } else {
+    var v = document.createElement('video');
+    v.innerHTML = "<source src=\"" + media.name + "?fail\" media=\"not all\">" +
+                  "<source src=\""+ media.name + "?pass\" media=\"all\">";
+    var v2 = document.createElement("video");
+    v2.innerHTML = "<source src=\""+ media.name +"?pass\">" +
+                   "<source src=\""+ media.name + "?fail\" media=\"all\">";
+    document.body.appendChild(v);
+    document.body.appendChild(v2);
+
+    v.addEventListener("loadedmetadata", function(e) {
+        ok(/pass/.test(e.target.currentSrc),
+          "The source has been chosen according to the media attribute.");
+        notifyFinished();
+        });
+    v2.addEventListener("loadedmetadata", function(e) {
+        ok(/pass/.test(e.target.currentSrc),
+          "If no media attribute is specified, it defaults to \'all\'.")
+        notifyFinished();
+    });
+  }
+
+</script>
+</pre>
+</body>
+</html>
--- a/content/media/webm/nsWebMBufferedParser.cpp
+++ b/content/media/webm/nsWebMBufferedParser.cpp
@@ -204,41 +204,39 @@ void nsWebMBufferedParser::Append(const 
       break;
     }
   }
 
   NS_ASSERTION(p == aBuffer + aLength, "Must have parsed to end of data.");
   mCurrentOffset += aLength;
 }
 
-void nsWebMBufferedState::CalculateBufferedForRange(nsTimeRanges* aBuffered,
-                                                    PRInt64 aStartOffset, PRInt64 aEndOffset,
-                                                    PRUint64 aTimecodeScale,
-                                                    PRInt64 aStartTimeOffsetNS)
+bool nsWebMBufferedState::CalculateBufferedForRange(PRInt64 aStartOffset, PRInt64 aEndOffset,
+                                                    PRUint64* aStartTime, PRUint64* aEndTime)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   // Find the first nsWebMTimeDataOffset at or after aStartOffset.
   PRUint32 start;
   mTimeMapping.GreatestIndexLtEq(aStartOffset, start);
   if (start == mTimeMapping.Length()) {
-    return;
+    return false;
   }
 
   // Find the first nsWebMTimeDataOffset at or before aEndOffset.
   PRUint32 end;
   if (!mTimeMapping.GreatestIndexLtEq(aEndOffset, end) && end > 0) {
     // No exact match, so adjust end to be the first entry before
     // aEndOffset.
     end -= 1;
   }
 
   // Range is empty.
   if (end <= start) {
-    return;
+    return false;
   }
 
   NS_ASSERTION(mTimeMapping[start].mOffset >= aStartOffset &&
                mTimeMapping[end].mOffset <= aEndOffset,
                "Computed time range must lie within data range.");
   if (start > 0) {
     NS_ASSERTION(mTimeMapping[start - 1].mOffset <= aStartOffset,
                  "Must have found least nsWebMTimeDataOffset for start");
@@ -247,19 +245,19 @@ void nsWebMBufferedState::CalculateBuffe
     NS_ASSERTION(mTimeMapping[end + 1].mOffset >= aEndOffset,
                  "Must have found greatest nsWebMTimeDataOffset for end");
   }
 
   // The timestamp of the first media sample, in ns. We must subtract this
   // from the ranges' start and end timestamps, so that those timestamps are
   // normalized in the range [0,duration].
 
-  double startTime = (mTimeMapping[start].mTimecode * aTimecodeScale - aStartTimeOffsetNS) / NS_PER_S;
-  double endTime = (mTimeMapping[end].mTimecode * aTimecodeScale - aStartTimeOffsetNS) / NS_PER_S;
-  aBuffered->Add(startTime, endTime);
+  *aStartTime = mTimeMapping[start].mTimecode;
+  *aEndTime = mTimeMapping[end].mTimecode;
+  return true;
 }
 
 void nsWebMBufferedState::NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRInt64 aOffset)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   PRUint32 idx;
   if (!mRangeParsers.GreatestIndexLtEq(aOffset, idx)) {
     // If the incoming data overlaps an already parsed range, adjust the
--- a/content/media/webm/nsWebMBufferedParser.h
+++ b/content/media/webm/nsWebMBufferedParser.h
@@ -220,20 +220,18 @@ public:
     MOZ_COUNT_CTOR(nsWebMBufferedState);
   }
 
   ~nsWebMBufferedState() {
     MOZ_COUNT_DTOR(nsWebMBufferedState);
   }
 
   void NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRInt64 aOffset);
-  void CalculateBufferedForRange(nsTimeRanges* aBuffered,
-                                 PRInt64 aStartOffset, PRInt64 aEndOffset,
-                                 PRUint64 aTimecodeScale,
-                                 PRInt64 aStartTimeOffsetNS);
+  bool CalculateBufferedForRange(PRInt64 aStartOffset, PRInt64 aEndOffset,
+                                 PRUint64* aStartTime, PRUint64* aEndTime);
 
 private:
   // Synchronizes access to the mTimeMapping array.
   ReentrantMonitor mReentrantMonitor;
 
   // Sorted (by offset) map of data offsets to timecodes.  Populated
   // on the main thread as data is received and parsed by nsWebMBufferedParsers.
   nsTArray<nsWebMTimeDataOffset> mTimeMapping;
--- a/content/media/webm/nsWebMReader.cpp
+++ b/content/media/webm/nsWebMReader.cpp
@@ -443,30 +443,30 @@ bool nsWebMReader::DecodeAudioPacket(nes
     mAudioStartUsec = tstamp_usecs;
   }
   // If there's a gap between the start of this audio chunk and the end of
   // the previous audio chunk, we need to increment the packet count so that
   // the vorbis decode doesn't use data from before the gap to help decode
   // from after the gap.
   CheckedInt64 tstamp_frames = UsecsToFrames(tstamp_usecs, rate);
   CheckedInt64 decoded_frames = UsecsToFrames(mAudioStartUsec, rate);
-  if (!tstamp_frames.valid() || !decoded_frames.valid()) {
+  if (!tstamp_frames.isValid() || !decoded_frames.isValid()) {
     NS_WARNING("Int overflow converting WebM times to frames");
     return false;
   }
   decoded_frames += mAudioFrames;
-  if (!decoded_frames.valid()) {
+  if (!decoded_frames.isValid()) {
     NS_WARNING("Int overflow adding decoded_frames");
     return false;
   }
   if (tstamp_frames.value() > decoded_frames.value()) {
 #ifdef DEBUG
     CheckedInt64 usecs = FramesToUsecs(tstamp_frames.value() - decoded_frames.value(), rate);
     LOG(PR_LOG_DEBUG, ("WebMReader detected gap of %lld, %lld frames, in audio stream\n",
-      usecs.valid() ? usecs.value(): -1,
+      usecs.isValid() ? usecs.value() : -1,
       tstamp_frames.value() - decoded_frames.value()));
 #endif
     mPacketCount++;
     mAudioStartUsec = tstamp_usecs;
     mAudioFrames = 0;
   }
 
   PRInt32 total_frames = 0;
@@ -496,28 +496,28 @@ bool nsWebMReader::DecodeAudioPacket(nes
       for (PRUint32 j = 0; j < mChannels; ++j) {
         VorbisPCMValue* channel = pcm[j];
         for (PRUint32 i = 0; i < PRUint32(frames); ++i) {
           buffer[i*mChannels + j] = MOZ_CONVERT_VORBIS_SAMPLE(channel[i]);
         }
       }
 
       CheckedInt64 duration = FramesToUsecs(frames, rate);
-      if (!duration.valid()) {
+      if (!duration.isValid()) {
         NS_WARNING("Int overflow converting WebM audio duration");
         return false;
       }
       CheckedInt64 total_duration = FramesToUsecs(total_frames, rate);
-      if (!total_duration.valid()) {
+      if (!total_duration.isValid()) {
         NS_WARNING("Int overflow converting WebM audio total_duration");
         return false;
       }
       
       CheckedInt64 time = total_duration + tstamp_usecs;
-      if (!time.valid()) {
+      if (!time.isValid()) {
         NS_WARNING("Int overflow adding total_duration and tstamp_usecs");
         nestegg_free_packet(aPacket);
         return false;
       };
 
       total_frames += frames;
       mAudioQueue.Push(new AudioData(aOffset,
                                      time.value(),
@@ -786,34 +786,55 @@ nsresult nsWebMReader::GetBuffered(nsTim
   MediaResource* resource = mDecoder->GetResource();
 
   uint64_t timecodeScale;
   if (!mContext || nestegg_tstamp_scale(mContext, &timecodeScale) == -1) {
     return NS_OK;
   }
 
   // Special case completely cached files.  This also handles local files.
-  if (resource->IsDataCachedToEndOfResource(0)) {
+  bool isFullyCached = resource->IsDataCachedToEndOfResource(0);
+  if (isFullyCached) {
     uint64_t duration = 0;
     if (nestegg_duration(mContext, &duration) == 0) {
       aBuffered->Add(0, duration / NS_PER_S);
     }
-  } else {
+  }
+
+  PRUint32 bufferedLength = 0;
+  aBuffered->GetLength(&bufferedLength);
+
+  // Either we the file is not fully cached, or we couldn't find a duration in
+  // the WebM bitstream.
+  if (!isFullyCached || !bufferedLength) {
     MediaResource* resource = mDecoder->GetResource();
     nsTArray<MediaByteRange> ranges;
     nsresult res = resource->GetCachedRanges(ranges);
     NS_ENSURE_SUCCESS(res, res);
 
-    PRInt64 startTimeOffsetNS = aStartTime * NS_PER_USEC;
     for (PRUint32 index = 0; index < ranges.Length(); index++) {
-      mBufferedState->CalculateBufferedForRange(aBuffered,
-                                                ranges[index].mStart,
-                                                ranges[index].mEnd,
-                                                timecodeScale,
-                                                startTimeOffsetNS);
+      PRUint64 start, end;
+      bool rv = mBufferedState->CalculateBufferedForRange(ranges[index].mStart,
+                                                          ranges[index].mEnd,
+                                                          &start, &end);
+      if (rv) {
+        double startTime = start * timecodeScale / NS_PER_S - aStartTime;
+        double endTime = end * timecodeScale / NS_PER_S - aStartTime;
+
+        // If this range extends to the end of the file, the true end time
+        // is the file's duration.
+        if (resource->IsDataCachedToEndOfResource(ranges[index].mStart)) {
+          uint64_t duration = 0;
+          if (nestegg_duration(mContext, &duration) == 0) {
+            endTime = duration / NS_PER_S;
+          }
+        }
+
+        aBuffered->Add(startTime, endTime);
+      }
     }
   }
 
   return NS_OK;
 }
 
 void nsWebMReader::NotifyDataArrived(const char* aBuffer, PRUint32 aLength, PRInt64 aOffset)
 {
--- a/dom/base/BrowserElementParent.js
+++ b/dom/base/BrowserElementParent.js
@@ -62,27 +62,27 @@ BrowserElementParent.prototype = {
     try {
       return prefs.getBoolPref(BROWSER_FRAMES_ENABLED_PREF);
     }
     catch(e) {
       return false;
     }
   },
 
-  _observeInProcessBrowserFrameShown: function(frameLoader, data) {
+  _observeInProcessBrowserFrameShown: function(frameLoader) {
     debug("In-process browser frame shown " + frameLoader);
-    this._setUpMessageManagerListeners(frameLoader, data);
+    this._setUpMessageManagerListeners(frameLoader);
   },
 
-  _observeRemoteBrowserFrameShown: function(frameLoader, data) {
+  _observeRemoteBrowserFrameShown: function(frameLoader) {
     debug("Remote browser frame shown " + frameLoader);
-    this._setUpMessageManagerListeners(frameLoader, data);
+    this._setUpMessageManagerListeners(frameLoader);
   },
 
-  _setUpMessageManagerListeners: function(frameLoader, data) {
+  _setUpMessageManagerListeners: function(frameLoader) {
     let frameElement = frameLoader.QueryInterface(Ci.nsIFrameLoader).ownerElement;
     if (!frameElement) {
       debug("No frame element?");
       return;
     }
 
     let mm = frameLoader.messageManager;
 
@@ -145,20 +145,20 @@ BrowserElementParent.prototype = {
       this._init();
       break;
     case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
       if (data == BROWSER_FRAMES_ENABLED_PREF) {
         this._init();
       }
       break;
     case 'remote-browser-frame-shown':
-      this._observeRemoteBrowserFrameShown(subject, data);
+      this._observeRemoteBrowserFrameShown(subject);
       break;
     case 'in-process-browser-frame-shown':
-      this._observeInProcessBrowserFrameShown(subject, data);
+      this._observeInProcessBrowserFrameShown(subject);
       break;
     case 'content-document-global-created':
       this._observeContentGlobalCreated(subject);
       break;
     }
   },
 };
 
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -133,28 +133,35 @@ UnwrapObject(JSContext* cx, JSObject* ob
   /* It's the wrong sort of DOM object */
   return NS_ERROR_XPC_BAD_CONVERT_JS;
 }
 
 inline bool
 IsArrayLike(JSContext* cx, JSObject* obj)
 {
   MOZ_ASSERT(obj);
-  // For simplicity, check for security wrappers up front
+  // For simplicity, check for security wrappers up front.  In case we
+  // have a security wrapper, don't forget to enter the compartment of
+  // the underlying object after unwrapping.
+  JSAutoEnterCompartment ac;
   if (js::IsWrapper(obj)) {
     obj = XPCWrapper::Unwrap(cx, obj, false);
     if (!obj) {
       // Let's say it's not
       return false;
     }
+
+    if (!ac.enter(cx, obj)) {
+      return false;
+    }
   }
 
   // XXXbz need to detect platform objects (including listbinding
   // ones) with indexGetters here!
-  return JS_IsArrayObject(cx, obj);
+  return JS_IsArrayObject(cx, obj) || JS_IsTypedArrayObject(obj, cx);
 }
 
 inline bool
 IsPlatformObject(JSContext* cx, JSObject* obj)
 {
   // XXXbz Should be treating list-binding objects as platform objects
   // too?  The one consumer so far wants non-array-like platform
   // objects, so listbindings that have an indexGetter should test
@@ -577,12 +584,90 @@ InitIds(JSContext* cx, Spec* specs, jsid
 
 JSBool
 QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp);
 JSBool
 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
 JSBool
 ThrowingConstructorWorkers(JSContext* cx, unsigned argc, JS::Value* vp);
 
+template<class T>
+class NonNull
+{
+public:
+  NonNull()
+#ifdef DEBUG
+    : inited(false)
+#endif
+  {}
+
+  operator T&() {
+    MOZ_ASSERT(inited);
+    MOZ_ASSERT(ptr, "NonNull<T> was set to null");
+    return *ptr;
+  }
+
+  void operator=(T* t) {
+    ptr = t;
+    MOZ_ASSERT(ptr);
+#ifdef DEBUG
+    inited = true;
+#endif
+  }
+
+  T** Slot() {
+#ifdef DEBUG
+    inited = true;
+#endif
+    return &ptr;
+  }
+
+protected:
+  T* ptr;
+#ifdef DEBUG
+  bool inited;
+#endif
+};
+
+template<class T>
+class OwningNonNull
+{
+public:
+  OwningNonNull()
+#ifdef DEBUG
+    : inited(false)
+#endif
+  {}
+
+  operator T&() {
+    MOZ_ASSERT(inited);
+    MOZ_ASSERT(ptr, "OwningNonNull<T> was set to null");
+    return *ptr;
+  }
+
+  void operator=(T* t) {
+    init(t);
+  }
+
+  void operator=(const already_AddRefed<T>& t) {
+    init(t);
+  }
+
+protected:
+  template<typename U>
+  void init(U t) {
+    ptr = t;
+    MOZ_ASSERT(ptr);
+#ifdef DEBUG
+    inited = true;
+#endif
+  }
+
+  nsRefPtr<T> ptr;
+#ifdef DEBUG
+  bool inited;
+#endif
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_BindingUtils_h__ */
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1110,22 +1110,22 @@ class CastableObjectUnwrapper():
         self.substitution = { "type" : descriptor.nativeType,
                               "protoID" : "prototypes::id::" + descriptor.name,
                               "source" : source,
                               "target" : target,
                               "codeOnFailure" : codeOnFailure }
 
     def __str__(self):
         return string.Template(
-"""  {
-    nsresult rv = UnwrapObject<${protoID}>(cx, ${source}, &${target});
-    if (NS_FAILED(rv)) {
-      ${codeOnFailure}
-    }
-  }""").substitute(self.substitution)
+"""{
+  nsresult rv = UnwrapObject<${protoID}>(cx, ${source}, ${target});
+  if (NS_FAILED(rv)) {
+    ${codeOnFailure}
+  }
+}""").substitute(self.substitution)
 
 class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper):
     """
     As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails
     """
     def __init__(self, descriptor, source, target):
         CastableObjectUnwrapper.__init__(self, descriptor, source, target,
                                          "return Throw<%s>(cx, rv);" %
@@ -1145,204 +1145,404 @@ class CallbackObjectUnwrapper:
         self.descriptor = descriptor
         self.substitution = { "nativeType" : descriptor.nativeType,
                               "source" : source,
                               "target" : target,
                               "codeOnFailure" :  codeOnFailure }
 
     def __str__(self):
         if self.descriptor.workers:
-            return string.Template("""
-    ${target} = ${source};""").substitute(self.substitution)
-
-        return string.Template("""
-    nsresult rv;
-    XPCCallContext ccx(JS_CALLER, cx);
-    if (!ccx.IsValid()) {
-      rv = NS_ERROR_XPC_BAD_CONVERT_JS;
-      ${codeOnFailure}
-    }
-
-    const nsIID& iid = NS_GET_IID(${nativeType});
-    nsRefPtr<nsXPCWrappedJS> wrappedJS;
-    rv = nsXPCWrappedJS::GetNewOrUsed(ccx, ${source}, iid,
-                                      NULL, getter_AddRefs(wrappedJS));
-    if (NS_FAILED(rv) || !wrappedJS) {
-      ${codeOnFailure}
-    }
-
-    ${target} = do_QueryObject(wrappedJS.get());
-    if (!${target}) {
-      ${codeOnFailure}
-    }""").substitute(self.substitution)
-
-def getArgumentConversionTemplate(type, descriptor):
-    if type.isSequence() or type.isArray():
-        raise TypeError("Can't handle sequence or array arguments yet")
-
-    if descriptor is not None:
-        assert(type.isInterface())
+            return string.Template(
+                "${target} = ${source};"
+                ).substitute(self.substitution)
+
+        return string.Template(
+            """nsresult rv;
+XPCCallContext ccx(JS_CALLER, cx);
+if (!ccx.IsValid()) {
+  rv = NS_ERROR_XPC_BAD_CONVERT_JS;
+  ${codeOnFailure}
+}
+
+const nsIID& iid = NS_GET_IID(${nativeType});
+nsRefPtr<nsXPCWrappedJS> wrappedJS;
+rv = nsXPCWrappedJS::GetNewOrUsed(ccx, ${source}, iid,
+                                  NULL, getter_AddRefs(wrappedJS));
+if (NS_FAILED(rv) || !wrappedJS) {
+  ${codeOnFailure}
+}
+
+// Use a temp nsCOMPtr for the null-check, because ${target} might be
+// OwningNonNull, not an nsCOMPtr.
+nsCOMPtr<${nativeType}> tmp = do_QueryObject(wrappedJS.get());
+if (!tmp) {
+  ${codeOnFailure}
+}
+${target} = tmp.forget();""").substitute(self.substitution)
+
+def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
+                                    isDefinitelyObject=False,
+                                    isSequenceMember=False):
+    """
+    Get a template for converting a JS value to a native object based on the
+    given type and descriptor.  If failureCode is given, then we're actually
+    testing whether we can convert the argument to the desired type.  That
+    means that failures to convert due to the JS value being the wrong type of
+    value need to use failureCode instead of throwing exceptions.  Failures to
+    convert that are due to JS exceptions (from toString or valueOf methods) or
+    out of memory conditions need to throw exceptions no matter what
+    failureCode is.
+
+    If isDefinitelyObject is True, that means we know the value
+    isObject() and we have no need to recheck that.
+
+    The return value from this function is a tuple consisting of three things:
+
+    1)  A string representing the conversion code.  This will have template
+        substitution performed on it as follows:
+
+          ${val} replaced by an expression for the JS::Value in question
+          ${valPtr} is a pointer to the JS::Value in question
+          ${holderName} replaced by the holder's name, if any
+          ${declName} replaced by the declaration's name
+
+    2)  A CGThing representing the native C++ type we're converting to
+        (declType).  This is allowed to be None if the conversion code is
+        supposed to be used as-is.
+    3)  A CGThing representing the type of a "holder" (holderType) which will
+        hold a possible reference to the C++ thing whose type we returned in #1,
+        or None if no such holder is needed.
+
+    ${declName} must be in scope before the generated code is entered.
+
+    If holderType is not None then ${holderName} must be in scope
+    before the generated code is entered.
+    """
+
+    # A helper function for wrapping up the template body for
+    # possibly-nullable objecty stuff
+    def wrapObjectTemplate(templateBody, isDefinitelyObject, type,
+                           codeToSetNull, isWorker):
+        if not isDefinitelyObject:
+            # Handle the non-object cases by wrapping up the whole
+            # thing in an if cascade.
+            templateBody = (
+                "if (${val}.isObject()) {\n" +
+                CGIndenter(CGGeneric(templateBody)).define() + "\n")
+            if type.nullable():
+                templateBody += (
+                    "} else if (${val}.isNullOrUndefined()) {\n"
+                    "  %s;\n" % codeToSetNull)
+            templateBody += (
+                "} else {\n"
+                "  return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
+                "}" % toStringBool(not isWorker))
+        return templateBody
+
+    if type.isArray():
+        raise TypeError("Can't handle array arguments yet")
+
+    if type.isSequence():
+        if isSequenceMember:
+            raise TypeError("Can't handle sequences of sequences")
+        if failureCode is not None:
+            raise TypeError("Can't handle sequences when failureCode is not None")
+        nullable = type.nullable();
+        if nullable:
+            type = type.inner;
+
+        elementType = type.inner;
+        # We don't know anything about the object-ness of the things
+        # we wrap, so don't pass through isDefinitelyObject
+        (elementTemplate, elementDeclType,
+         elementHolderType) = getJSToNativeConversionTemplate(
+            elementType, descriptorProvider, isSequenceMember=True)
+        if elementHolderType is not None:
+            raise TypeError("Shouldn't need holders for sequences")
+
+        # Have to make sure to use a fallible array, because it's trivial for
+        # page JS to create things with very large lengths.
+        typeName = CGWrapper(elementDeclType, pre="nsTArray< ", post=" >")
+        if nullable:
+            typeName = CGWrapper(typeName, pre="Nullable< ", post=" >")
+        templateBody = ("""JSObject* seq = &${val}.toObject();\n
+if (!IsArrayLike(cx, seq)) {
+  return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
+}
+uint32_t length;
+// JS_GetArrayLength actually works on all objects
+if (!JS_GetArrayLength(cx, seq, &length)) {
+  return false;
+}
+// Jump through a hoop to do a fallible allocation but later end up with
+// an infallible array.
+FallibleTArray< %s > arr;
+if (!arr.SetCapacity(length)) {
+  return Throw<%s>(cx, NS_ERROR_OUT_OF_MEMORY);
+}
+for (uint32_t i = 0; i < length; ++i) {
+  jsval temp;
+  if (!JS_GetElement(cx, seq, i, &temp)) {
+    return false;
+  }
+""" % (toStringBool(descriptorProvider.workers),
+       elementDeclType.define(),
+       toStringBool(descriptorProvider.workers)))
+
+        templateBody += CGIndenter(CGGeneric(
+                string.Template(elementTemplate).substitute(
+                    {
+                        "val" : "temp",
+                        "declName" : "*arr.AppendElement()"
+                        }
+                    ))).define()
+
+        templateBody += """
+}
+// And the other half of the hoop-jump"""
+        if nullable:
+            templateBody += """
+${declName}.SetValue().SwapElements(arr);
+"""
+        else:
+            templateBody += """
+${declName}.SwapElements(arr);
+"""
+        templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
+                                          type, "${declName}.SetNull()",
+                                          descriptorProvider.workers)
+        return (templateBody, typeName, None)
+
+    if type.isInterface() and not type.isArrayBuffer():
+        descriptor = descriptorProvider.getDescriptor(
+            type.unroll().inner.identifier.name)
         # This is an interface that we implement as a concrete class
         # or an XPCOM interface.
+
+        # Allow null pointers for nullable types and old-binding classes
         argIsPointer = type.nullable() or type.unroll().inner.isExternal()
+
+        # Sequences and non-worker callbacks have to hold a strong ref to the
+        # thing being passed down.
+        forceOwningType = (descriptor.interface.isCallback() and
+                           not descriptor.workers) or isSequenceMember
+
+        typeName = descriptor.nativeType
+        typePtr = typeName + "*"
+
+        # Compute a few things:
+        #  - declType is the type we want to return as the first element of our
+        #    tuple.
+        #  - holderType is the type we want to return as the third element
+        #    of our tuple.
+        #  - declInit is the initializer expression for our decl, if any.
+        #  - target is where a pointer to the object is being stored
+
+        # Set up some sensible defaults for these things insofar as we can.
+        holderType = None
         if argIsPointer:
-            nameSuffix = ""
-        else:
-            nameSuffix = "_ptr"
-
-        # If we're going to QI, we want an nsCOMPtr.  But note that XPConnect
-        # unwrapping may or may not QI, and we don't know whether it will.  So
-        # we use a raw pointer for the isExternal() case, and if a ref is needed
-        # it'll be handled by the xpc_qsSelfRef we put on the stack later.
-        if descriptor.castable or type.unroll().inner.isExternal() or descriptor.workers:
-            declType = "  ${typeName}*"
+            if forceOwningType:
+                declType = "nsRefPtr<" + typeName + ">"
+            else:
+                declType = typePtr
+            target = "&${declName}"
         else:
-            declType = "  nsCOMPtr<${typeName}>"
-        template = declType + " ${name}%s;\n" % nameSuffix
-
-        # We have to be very careful here to put anything that might need to
-        # hold references across the C++ call in |template| and not
-        # |templateBody|, since things in |templateBody| will go out of scope
-        # before the call happens.
-        templateBody = "  if (${argVal}.isObject()) {"
+            if forceOwningType:
+                declType = "OwningNonNull<" + typeName + ">"
+            else:
+                declType = "NonNull<" + typeName + ">"
+            target = "${declName}.Slot()"
+
+        templateBody = ""
         if descriptor.castable:
-            templateBody += str(FailureFatalCastableObjectUnwrapper(
-                    descriptor,
-                    "&${argVal}.toObject()",
-                    "${name}"+nameSuffix)).replace("\n", "\n  ") + "\n"
+            if failureCode is not None:
+                templateBody += str(CastableObjectUnwrapper(
+                        descriptor,
+                        "&${val}.toObject()",
+                        target,
+                        failureCode))
+            else:
+                templateBody += str(FailureFatalCastableObjectUnwrapper(
+                        descriptor,
+                        "&${val}.toObject()",
+                        target))
         elif descriptor.interface.isCallback():
             templateBody += str(CallbackObjectUnwrapper(
                     descriptor,
-                    "&${argVal}.toObject()",
-                    "${name}"+nameSuffix)) + "\n"
+                    "&${val}.toObject()",
+                    "${declName}",
+                    codeOnFailure=failureCode))
         elif descriptor.workers:
-            templateBody += """
-    ${name}%s = &${argVal}.toObject();
-    MOZ_ASSERT(${name}%s);
-""" % (nameSuffix, nameSuffix)
+            templateBody += "${declName} = &${val}.toObject();"
         else:
-            template += "  xpc_qsSelfRef tmpRef_${name};\n"
-            template += "  jsval tmpVal_${name} = ${argVal};\n"
-            templateBody += """
-    ${typeName}* tmp;
-    if (NS_FAILED(xpc_qsUnwrapArg<${typeName}>(cx, ${argVal}, &tmp, &tmpRef_${name}.ptr,
-                                               &tmpVal_${name}))) {
-      return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
-    }
-    MOZ_ASSERT(tmp);
-    ${name}%s = tmp;
-""" % (toStringBool(not descriptor.workers), nameSuffix)
-
-        if type.nullable():
+            # Either external, or new-binding non-castable.  We always have a
+            # holder for these, because we don't actually know whether we have
+            # to addref when unwrapping or not.  So we just pass an
+            # getter_AddRefs(nsCOMPtr) to XPConnect and if we'll need a release
+            # it'll put a non-null pointer in there.
+            if forceOwningType:
+                # Don't return a holderType in this case; our declName
+                # will just own stuff.
+                templateBody += "nsCOMPtr<" + typeName + "> ${holderName};"
+            else:
+                holderType = "nsCOMPtr<" + typeName + ">"
             templateBody += (
-                "  } else if (${argVal}.isNullOrUndefined()) {\n"
-                "    ${name}%s = NULL;\n" % nameSuffix)
-
-        templateBody += (
-            "  } else {\n"
-            "    return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
-            "  }\n" % toStringBool(not descriptor.workers))
-
-        template += templateBody
-
-        if not argIsPointer:
-            template += "  ${typeName} &${name} = *${name}_ptr;\n"
-            
-        return template
+                "jsval tmpVal = ${val};\n" +
+                typePtr + " tmp;\n"
+                "if (NS_FAILED(xpc_qsUnwrapArg<" + typeName + ">(cx, ${val}, &tmp, getter_AddRefs(${holderName}), &tmpVal))) {\n")
+            if failureCode is not None:
+                templateBody += "  " + failureCode + "\n"
+            else:
+                templateBody += (
+                    "  return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
+                    % toStringBool(not descriptor.workers))
+            templateBody += ("}\n"
+                "MOZ_ASSERT(tmp);\n")
+
+            if not isDefinitelyObject:
+                # Our tmpVal will go out of scope, so we can't rely on it
+                # for rooting
+                templateBody += (
+                    "if (tmpVal != ${val} && !${holderName}) {\n"
+                    "  // We have to have a strong ref, because we got this off\n"
+                    "  // some random object that might get GCed\n"
+                    "  ${holderName} = tmp;\n"
+                    "}\n")
+
+            # And store our tmp, before it goes out of scope.
+            templateBody += "${declName} = tmp;"
+
+        templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
+                                          type, "${declName} = NULL",
+                                          descriptor.workers)
+
+        declType = CGGeneric(declType)
+        if holderType is not None:
+            holderType = CGGeneric(holderType)
+        return (templateBody, declType, holderType)
 
     if type.isArrayBuffer():
+        if isSequenceMember:
+            raise TypeError("Can't handle sequences of arraybuffers")
+        declType = "JSObject*"
         template = (
-            "  JSObject* ${name};\n"
-            "  if (${argVal}.isObject() && JS_IsArrayBufferObject(&${argVal}.toObject(), cx)) {\n"
-            "    ${name} = &${argVal}.toObject();\n"
-            "  }")
+            "if (${val}.isObject() && JS_IsArrayBufferObject(&${val}.toObject(), cx)) {\n"
+            "  ${declName} = &${val}.toObject();\n"
+            "}")
         if type.nullable():
             template += (
-                " else if (${argVal}.isNullOrUndefined()) {\n"
-                "    ${name} = NULL;\n"
-                "  }")
+                " else if (${val}.isNullOrUndefined()) {\n"
+                "  ${declName} = NULL;\n"
+                "}")
 
         template += (
             # XXXbz We don't know whether we're on workers, so play it safe
             " else {\n"
-            "    return Throw<false>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
-            "  }")
-
-        return template
-
-    if type.isInterface():
-        raise TypeError("Interface type with no descriptor: " + type)
+            "  return Throw<false>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
+            "}")
+
+        return (template, CGGeneric(declType), None)
 
     if type.isString():
+        if isSequenceMember:
+            raise TypeError("Can't handle sequences of strings")
         # XXXbz Need to figure out string behavior based on extended args?  Also, how to
         # detect them?
 
         # For nullable strings that are not otherwise annotated, null
         # and undefined become null strings.
         if type.nullable():
             nullBehavior = "eNull"
             undefinedBehavior = "eNull"
         else:
             nullBehavior = "eStringify"
             undefinedBehavior = "eStringify"
 
         return (
-            "  const xpc_qsDOMString ${name}(cx, ${argVal}, ${argPtr},\n"
-            "                             xpc_qsDOMString::%s,\n"
-            "                             xpc_qsDOMString::%s);\n"
-            "  if (!${name}.IsValid()) {\n"
-            "    return false;\n"
-            "  }\n" % (nullBehavior, undefinedBehavior))
+            "const xpc_qsDOMString ${declName}(cx, ${val}, ${valPtr},\n"
+            "                           xpc_qsDOMString::%s,\n"
+            "                           xpc_qsDOMString::%s);\n"
+            "if (!${declName}.IsValid()) {\n"
+            "  return false;\n"
+            "}" % (nullBehavior, undefinedBehavior), None, None)
 
     if type.isEnum():
         if type.nullable():
             raise TypeError("We don't support nullable enumerated arguments "
                             "yet")
         enum = type.inner.identifier.name
         return (
-            "  %(enumtype)s ${name};\n"
-            "  {\n"
-            "    bool ok;\n"
-            "    ${name} = static_cast<%(enumtype)s>(FindEnumStringIndex(cx, ${argVal}, %(values)s, &ok));\n"
-            "    if (!ok) {\n"
-            "      return false;\n"
-            "    }\n"
-            "  }" % { "enumtype" : enum,
-                      "values" : enum + "Values::strings" })
+            "{\n"
+            "  bool ok;\n"
+            "  ${declName} = static_cast<%(enumtype)s>(FindEnumStringIndex(cx, ${val}, %(values)s, &ok));\n"
+            "  if (!ok) {\n"
+            "    return false;\n"
+            "  }\n"
+            "}" % { "enumtype" : enum,
+                      "values" : enum + "Values::strings" },
+            CGGeneric(enum), None)
 
     if type.isCallback():
+        if isSequenceMember:
+            raise TypeError("Can't handle sequences of callbacks")
         # XXXbz we're going to assume that callback types are always
         # nullable and always have [TreatNonCallableAsNull] for now.
         return (
-            "  JSObject* ${name};\n"
-            "  if (${argVal}.isObject() && JS_ObjectIsCallable(cx, &${argVal}.toObject())) {\n"
-            "    ${name} = &${argVal}.toObject();\n"
-            "  } else {\n"
-            "    ${name} = NULL;\n"
-            "  }\n")
+            "if (${val}.isObject() && JS_ObjectIsCallable(cx, &${val}.toObject())) {\n"
+            "  ${declName} = &${val}.toObject();\n"
+            "} else {\n"
+            "  ${declName} = NULL;\n"
+            "}", CGGeneric("JSObject*"), None)
 
     if type.isAny():
-        return "  JS::Value ${name} = ${argVal};\n"
+        if isSequenceMember:
+            raise TypeError("Can't handle sequences of 'any'")
+        return ("${declName} = ${val};", CGGeneric("JS::Value"), None)
 
     if not type.isPrimitive():
         raise TypeError("Need conversion for argument type '%s'" % type)
 
     # XXXbz need to add support for [EnforceRange] and [Clamp]
+    typeName = builtinNames[type.tag()]
     if type.nullable():
-        return ("  Nullable<${typeName}> ${name};\n"
-                "  if (${argVal}.isNullOrUndefined()) {\n"
-                "    ${name}.SetNull();\n"
-                "  } else if (!ValueToPrimitive<${typeName}>(cx, ${argVal}, &${name}.SetValue())) {\n"
-                "    return false;\n"
-                "  }\n")
+        return ("if (${val}.isNullOrUndefined()) {\n"
+                "  ${declName}.SetNull();\n"
+                "} else if (!ValueToPrimitive<" + typeName + ">(cx, ${val}, &${declName}.SetValue())) {\n"
+                "  return false;\n"
+                "}", CGGeneric("Nullable<" + typeName + ">"), None)
     else:
-        return ("  ${typeName} ${name};\n"
-                "  if (!ValueToPrimitive<${typeName}>(cx, ${argVal}, &${name})) {\n"
-                "    return false;\n"
-                "  }\n")
+        return ("if (!ValueToPrimitive<" + typeName + ">(cx, ${val}, &${declName})) {\n"
+                "  return false;\n"
+                "}", CGGeneric(typeName), None)
+
+def instantiateJSToNativeConversionTemplate(templateTuple, replacements):
+    """
+    Take a tuple as returned by getJSToNativeConversionTemplate and a set of
+    replacements as required by the strings in such a tuple, and generate code
+    to convert into stack C++ types.
+    """
+    (templateBody, declType, holderType) = templateTuple
+    result = CGList([], "\n")
+    if holderType is not None:
+        result.append(
+            CGList([holderType, CGGeneric(" "),
+                    CGGeneric(replacements["holderName"]),
+                    CGGeneric(";")]))
+    if declType is not None:
+        result.append(
+            CGList([declType, CGGeneric(" "),
+                    CGGeneric(replacements["declName"]),
+                    CGGeneric(";")]))
+    result.append(CGGeneric(
+            string.Template(templateBody).substitute(replacements)
+            ))
+    # Add an empty CGGeneric to get an extra newline after the argument
+    # conversion.
+    result.append(CGGeneric(""))
+    return result;
 
 def convertConstIDLValueToJSVal(value):
     if isinstance(value, IDLNullValue):
         return "JSVAL_NULL"
     tag = value.type.tag()
     if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
                IDLType.Tags.uint16, IDLType.Tags.int32]:
         return "INT_TO_JSVAL(%s)" % (value.value)
@@ -1358,66 +1558,59 @@ def convertConstIDLValueToJSVal(value):
 
 def convertIDLDefaultValueToJSVal(value):
     if value.type:
         tag = value.type.tag()
         if tag == IDLType.Tags.domstring:
             assert False # Not implemented!
     return convertConstIDLValueToJSVal(value)
 
-unindenter = re.compile("^  ", re.MULTILINE)
 class CGArgumentConverter(CGThing):
     """
     A class that takes an IDL argument object, its index in the
     argument list, and the argv and argc strings and generates code to
     unwrap the argument to the right native type.
     """
     def __init__(self, argument, index, argv, argc, descriptorProvider):
         CGThing.__init__(self)
         self.argument = argument
         # XXXbz should optional jsval args get JSVAL_VOID? What about
         # others?
-        self.replacementVariables = {
+        replacer = {
             "index" : index,
             "argc" : argc,
             "argv" : argv,
-            "defaultValue" : "JSVAL_VOID",
-            "name" : "arg%d" % index
+            "defaultValue" : "JSVAL_VOID"
+            }
+        self.replacementVariables = {
+            "declName" : "arg%d" % index,
+            "holderName" : ("arg%d" % index) + "_holder"
             }
         if argument.optional:
             if argument.defaultValue:
-                self.replacementVariables["defaultValue"] = convertIDLDefaultValueToJSVal(argument.defaultValue)
-            self.replacementVariables["argVal"] = string.Template(
+                replacer["defaultValue"] = convertIDLDefaultValueToJSVal(argument.defaultValue)
+            self.replacementVariables["val"] = string.Template(
                 "(${index} < ${argc} ? ${argv}[${index}] : ${defaultValue})"
-                ).substitute(self.replacementVariables)
-            self.replacementVariables["argPtr"] = string.Template(
+                ).substitute(replacer)
+            self.replacementVariables["valPtr"] = string.Template(
                 "(${index} < ${argc} ? &${argv}[${index}] : NULL)"
-                ).substitute(self.replacementVariables)
+                ).substitute(replacer)
         else:
-            self.replacementVariables["argVal"] = string.Template(
+            self.replacementVariables["val"] = string.Template(
                 "${argv}[${index}]"
-                ).substitute(self.replacementVariables)
-            self.replacementVariables["argPtr"] = (
-                "&" + self.replacementVariables["argVal"])
-        self.descriptor = None
-        if argument.type.isPrimitive():
-            self.replacementVariables["typeName"] = builtinNames[argument.type.tag()]
-        elif argument.type.isInterface() and not argument.type.isArrayBuffer():
-            descriptor = descriptorProvider.getDescriptor(
-                argument.type.unroll().inner.identifier.name)
-            self.descriptor = descriptor
-            self.replacementVariables["typeName"] = descriptor.nativeType
+                ).substitute(replacer)
+            self.replacementVariables["valPtr"] = (
+                "&" + self.replacementVariables["val"])
+        self.descriptorProvider = descriptorProvider
 
     def define(self):
-        return string.Template(
-            re.sub(unindenter,
-                   "",
-                   getArgumentConversionTemplate(self.argument.type,
-                                                 self.descriptor))
-            ).substitute(self.replacementVariables)
+        return instantiateJSToNativeConversionTemplate(
+            getJSToNativeConversionTemplate(self.argument.type,
+                                            self.descriptorProvider),
+            self.replacementVariables).define()
 
 def getWrapTemplateForType(type, descriptorProvider, result, successCode):
     """
     Reflect a C++ value stored in "result", of IDL type "type" into JS.  The
     "successCode" is the code to run once we have successfully done the
     conversion.  The resulting string should be used with string.Template, it
     needs the following keys when substituting: jsvalPtr/jsvalRef/obj.
     """
@@ -1455,18 +1648,55 @@ def getWrapTemplateForType(type, descrip
                CGIndenter(CGGeneric(failureCode)).define() + "\n" +
                "}\n" +
                successCode) % (wrapCall)
         return str
     
     if type is None or type.isVoid():
         return setValue("JSVAL_VOID")
 
-    if type.isSequence() or type.isArray():
-        raise TypeError("Can't handle sequence or array return values yet")
+    if type.isArray():
+        raise TypeError("Can't handle array return values yet")
+
+    if type.isSequence():
+        if type.nullable():
+            # Nullable sequences are Nullable< nsTArray<T> >
+            return """
+if (%s.IsNull()) {
+%s
+}
+%s""" % (result, CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define(),
+         getWrapTemplateForType(type.inner, descriptorProvider,
+                                "%s.Value()" % result, successCode))
+
+        # Now do non-nullable sequences.  We use setting the element
+        # in the array as our succcess code because when we succeed in
+        # wrapping that's what we should do.
+        innerTemplate = wrapForType(
+            type.inner, descriptorProvider,
+            {
+                'result' :  "%s[i]" % result,
+                'successCode': ("if (!JS_SetElement(cx, returnArray, i, &tmp)) {\n"
+                                "  return false;\n"
+                                "}"),
+                'jsvalRef': "tmp",
+                'jsvalPtr': "&tmp"
+                }
+            )
+        innerTemplate = CGIndenter(CGGeneric(innerTemplate)).define()
+        return ("""
+uint32_t length = %s.Length();
+JSObject *returnArray = JS_NewArrayObject(cx, length, NULL);
+if (!returnArray) {
+  return false;
+}
+jsval tmp;
+for (uint32_t i = 0; i < length; ++i) {
+%s
+}\n""" % (result, innerTemplate)) + setValue("JS::ObjectValue(*returnArray)")
 
     if type.isInterface() and not type.isArrayBuffer():
         descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name)
         if type.nullable():
             wrappingCode = ("if (!%s) {\n" % (result) +
                             CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define() + "\n" +
                             "}\n")
         else:
@@ -1577,64 +1807,83 @@ def wrapForType(type, descriptorProvider
     """
     wrap = getWrapTemplateForType(type, descriptorProvider,
                                   templateValues.get('result', 'result'),
                                   templateValues.get('successCode', None))
 
     defaultValues = {'obj': 'obj'}
     return string.Template(wrap).substitute(defaultValues, **templateValues)
 
+def getRetvalDeclarationForType(returnType, descriptorProvider,
+                                resultAlreadyAddRefed):
+    if returnType is None or returnType.isVoid():
+        # Nothing to declare
+        result = None
+    elif returnType.isPrimitive() and returnType.tag() in builtinNames:
+        result = CGGeneric(builtinNames[returnType.tag()])
+        if returnType.nullable():
+            result = CGWrapper(result, pre="Nullable<", post=">")
+    elif returnType.isString():
+        result = CGGeneric("nsString")
+    elif returnType.isEnum():
+        if returnType.nullable():
+            raise TypeError("We don't support nullable enum return values")
+        result = CGGeneric(returnType.inner.identifier.name)
+    elif returnType.isInterface() and not returnType.isArrayBuffer():
+        result = CGGeneric(descriptorProvider.getDescriptor(
+            returnType.unroll().inner.identifier.name).nativeType)
+        if resultAlreadyAddRefed:
+            result = CGWrapper(result, pre="nsRefPtr<", post=">")
+        else:
+            result = CGWrapper(result, post="*")
+    elif returnType.isCallback():
+        # XXXbz we're going to assume that callback types are always
+        # nullable for now.
+        result = CGGeneric("JSObject*")
+    elif returnType.tag() is IDLType.Tags.any:
+        result = CGGeneric("JS::Value")
+    elif returnType.isSequence():
+        nullable = returnType.nullable()
+        if nullable:
+            returnType = returnType.inner
+        # Assume no need to addref for now
+        result = CGWrapper(getRetvalDeclarationForType(returnType.inner,
+                                                       descriptorProvider,
+                                                       False),
+                           pre="nsTArray< ", post=" >")
+        if nullable:
+            result = CGWrapper(result, pre="Nullable< ", post=" >")
+    else:
+        raise TypeError("Don't know how to declare return value for %s" %
+                        returnType)
+    return result
+
+
 class CGCallGenerator(CGThing):
     """
     A class to generate an actual call to a C++ object.  Assumes that the C++
     object is stored in a variable named "self".
     """
     def __init__(self, errorReport, argCount, argsPre, returnType,
                  resultAlreadyAddRefed, descriptorProvider, nativeMethodName, static):
         CGThing.__init__(self)
 
         isFallible = errorReport is not None
 
         args = CGList([CGGeneric("arg" + str(i)) for i in range(argCount)], ", ")
-        resultOutParam = returnType is not None and returnType.isString()
+        resultOutParam = (returnType is not None and
+                          (returnType.isString() or returnType.isSequence()))
         # Return values that go in outparams go here
         if resultOutParam:
             args.append(CGGeneric("result"))
         if isFallible:
             args.append(CGGeneric("rv"))
 
-        if returnType is None or returnType.isVoid():
-            # Nothing to declare
-            result = None
-        elif returnType.isPrimitive() and returnType.tag() in builtinNames:
-            result = CGGeneric(builtinNames[returnType.tag()])
-            if returnType.nullable():
-                result = CGWrapper(result, pre="Nullable<", post=">")
-        elif returnType.isString():
-            result = CGGeneric("nsString")
-        elif returnType.isEnum():
-            if returnType.nullable():
-                raise TypeError("We don't support nullable enum return values")
-            result = CGGeneric(returnType.inner.identifier.name)
-        elif returnType.isInterface() and not returnType.isArrayBuffer():
-            result = CGGeneric(descriptorProvider.getDescriptor(
-                returnType.unroll().inner.identifier.name).nativeType)
-            if resultAlreadyAddRefed:
-                result = CGWrapper(result, pre="nsRefPtr<", post=">")
-            else:
-                result = CGWrapper(result, post="*")
-        elif returnType.isCallback():
-            # XXXbz we're going to assume that callback types are always
-            # nullable for now.
-            result = CGGeneric("JSObject*")
-        elif returnType.tag() is IDLType.Tags.any:
-            result = CGGeneric("JS::Value")
-        else:
-            raise TypeError("Don't know how to declare return value for %s" %
-                            returnType)
+        result = getRetvalDeclarationForType(returnType, descriptorProvider,
+                                             resultAlreadyAddRefed)
 
         # Build up our actual call
         self.cgRoot = CGList([], "\n")
 
         call = CGGeneric(nativeMethodName)
         if static:
             call = CGWrapper(call, pre="%s::" % (descriptorProvider.getDescriptor(
                 returnType.unroll().inner.identifier.name).nativeType))
@@ -1953,81 +2202,42 @@ class CGMethodCall(CGThing):
             if len(interfacesSigs) > 0:
                 caseBody.append(CGGeneric("if (%s.isObject() &&\n"
                                           "    IsPlatformObject(cx, &%s.toObject())) {" %
                                           (distinguishingArg, distinguishingArg)))
                 for sig in interfacesSigs:
                     caseBody.append(CGIndenter(CGGeneric("do {")));
                     type = sig[1][distinguishingIndex].type
                     
-                    # XXXbz this duplicates some argument-unwrapping code!
-                    interfaceDesc = descriptor.getDescriptor(
-                        type.unroll().inner.identifier.name)
-                    argIsPointer = (type.nullable() or
-                                    type.unroll().inner.isExternal())
-                    if argIsPointer:
-                        nameSuffix = ""
-                    else:
-                        nameSuffix = "_ptr"
-                    if (interfaceDesc.castable or
-                        type.unroll().inner.isExternal() or
-                        interfaceDesc.workers):
-                            declType = "  ${typeName}*"
-                    else:
-                        declType = "  nsCOMPtr<${typeName}>"
-                    template = declType + " ${name}%s;\n" % nameSuffix
-                    if interfaceDesc.castable:
-                        template += str(CastableObjectUnwrapper(
-                                interfaceDesc,
-                                "&${argVal}.toObject()",
-                                "${name}"+nameSuffix,
-                                "break;")) + "\n"
-                    elif interfaceDesc.workers:
-                        template += """
-    ${name}%s = &${argVal}.toObject();
-    MOZ_ASSERT(${name}%s);
-""" % (nameSuffix, nameSuffix)
-                    else:
-                        template += "  xpc_qsSelfRef tmpRef_${name};\n"
-                        template += "  jsval tmpVal_${name} = ${argVal};\n"
-                        template += """
-  ${typeName}* tmp;
-  if (NS_FAILED(xpc_qsUnwrapArg<${typeName}>(cx, ${argVal}, &tmp, &tmpRef_${name}.ptr,
-                                             &tmpVal_${name}))) {
-    break;
-  }
-  MOZ_ASSERT(tmp);
-  ${name}%s = tmp;
-""" % nameSuffix
-
-                    if not argIsPointer:
-                        template += "  ${typeName} &${name} = *${name}_ptr;\n"
-
-                    testCode = string.Template(template).substitute(
+                    testCode = instantiateJSToNativeConversionTemplate(
+                        getJSToNativeConversionTemplate(type, descriptor,
+                                                        failureCode="break;",
+                                                        isDefinitelyObject=True),
                         {
-                            "typeName": interfaceDesc.nativeType,
-                            "name" : "arg%d" % distinguishingIndex,
-                            "argVal" : distinguishingArg
-                            }
-                        )
-                    caseBody.append(CGIndenter(CGGeneric(testCode)));
+                            "declName" : "arg%d" % distinguishingIndex,
+                            "holderName" : ("arg%d" % distinguishingIndex) + "_holder",
+                            "val" : distinguishingArg
+                            })
+
+                    # Indent by 4, since we need to indent further than our "do" statement
+                    caseBody.append(CGIndenter(testCode, 4));
                     # If we got this far, we know we unwrapped to the right
                     # interface, so just do the call.  Start conversion with
                     # distinguishingIndex + 1, since we already converted
                     # distinguishingIndex.
-                    caseBody.append(CGIndenter(CGIndenter(
-                            getPerSignatureCall(sig, distinguishingIndex + 1))))
+                    caseBody.append(CGIndenter(
+                            getPerSignatureCall(sig, distinguishingIndex + 1), 4))
                     caseBody.append(CGIndenter(CGGeneric("} while (0);")))
 
                 caseBody.append(CGGeneric("}"))
 
             # XXXbz Now we're supposed to check for distinguishingArg being
             # an array or a platform object that supports indexed
             # properties... skip that last for now.  It's a bit of a pain.
-            pickFirstSignature("%s.isObject() && IsArrayLike(cx, &%s.toObject()" %
+            pickFirstSignature("%s.isObject() && IsArrayLike(cx, &%s.toObject())" %
                                (distinguishingArg, distinguishingArg),
                                lambda s:
                                    (s[1][distinguishingIndex].type.isArray() or
                                     s[1][distinguishingIndex].type.isSequence() or
                                     s[1][distinguishingIndex].type.isObject()))
 
             # Check for Date objects
             # XXXbz Do we need to worry about security wrappers around the Date?
@@ -2163,18 +2373,19 @@ class CGAbstractBindingMethod(CGAbstract
     function to do the rest of the work.  This function should return a
     CGThing which is already properly indented.
     """
     def __init__(self, descriptor, name, args, extendedAttributes):
         self.extendedAttributes = extendedAttributes
         CGAbstractStaticMethod.__init__(self, descriptor, name, "JSBool", args)
 
     def definition_body(self):
-        unwrapThis = CGGeneric(
-            str(FailureFatalCastableObjectUnwrapper(self.descriptor, "obj", "self")))
+        unwrapThis = CGIndenter(CGGeneric(
+            str(FailureFatalCastableObjectUnwrapper(self.descriptor,
+                                                    "obj", "&self"))))
         return CGList([ self.getThis(), unwrapThis,
                         self.generate_code() ], "\n").define()
 
     def getThis(self):
         return CGIndenter(
             CGGeneric("JSObject* obj = JS_THIS_OBJECT(cx, vp);\n"
                       "if (!obj) {\n"
                       "  return false;\n"
--- a/dom/bindings/ErrorResult.h
+++ b/dom/bindings/ErrorResult.h
@@ -42,13 +42,17 @@ public:
   }
 
   nsresult ErrorCode() const {
     return mResult;
   }
 
 private:
   nsresult mResult;
+
+  // Not to be implemented, to make sure people always pass this by
+  // reference, not by value.
+  ErrorResult(const ErrorResult&) MOZ_DELETE;
 };
 
 } // namespace mozilla
 
 #endif /* mozilla_ErrorResult_h */
--- a/dom/bindings/Nullable.h
+++ b/dom/bindings/Nullable.h
@@ -47,16 +47,21 @@ public:
     mIsNull = true;
   }
 
   const T& Value() const {
     MOZ_ASSERT(!mIsNull);
     return mValue;
   }
 
+  T& Value() {
+    MOZ_ASSERT(!mIsNull);
+    return mValue;
+  }
+
   bool IsNull() const {
     return mIsNull;
   }
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -809,20 +809,20 @@ class IDLSequenceType(IDLType):
 
     def __str__(self):
         return self.inner.__str__() + "Sequence"
 
     def nullable(self):
         return False
 
     def isPrimitive(self):
-        return self.inner.isPrimitive()
+        return False;
 
     def isString(self):
-        return self.inner.isString()
+        return False;
 
     def isVoid(self):
         return False
 
     def isSequence(self):
         return True
 
     def isArray(self):
--- a/dom/bluetooth/BluetoothAdapter.cpp
+++ b/dom/bluetooth/BluetoothAdapter.cpp
@@ -99,26 +99,26 @@ class ToggleBtTask : public nsRunnable
       // different implementations per platform. Linux doesn't require
       // bluetooth firmware loading, but code should work otherwise.
       if(!EnsureBluetoothInit()) {
         NS_ERROR("Failed to load bluedroid library.\n");
         return NS_ERROR_FAILURE;
       }
 
       // return 1 if it's enabled, 0 if it's disabled, and -1 on error
-      int isEnabled = sBluedroidFunctions.bt_is_enabled();
+      int isEnabled = IsBluetoothEnabled();
 
       if ((isEnabled == 1 && mEnabled) || (isEnabled == 0 && !mEnabled)) {
         result = true;
       } else if (isEnabled < 0) {
         result = false;
       } else if (mEnabled) {
-        result = (sBluedroidFunctions.bt_enable() == 0) ? true : false;
+        result = (EnableBluetooth() == 0) ? true : false;
       } else {
-        result = (sBluedroidFunctions.bt_disable() == 0) ? true : false;
+        result = (DisableBluetooth() == 0) ? true : false;
       }
 #else
       result = true;
       NS_WARNING("No bluetooth support in this build configuration, faking a success event instead");
 #endif
 
       // Create a result thread and pass it to Main Thread, 
       nsCOMPtr<nsIRunnable> resultRunnable = new ToggleBtResultTask(mAdapterPtr, mDOMRequest, mEnabled, result);
--- a/dom/bluetooth/BluetoothFirmware.cpp
+++ b/dom/bluetooth/BluetoothFirmware.cpp
@@ -9,16 +9,31 @@
 #include "nsDebug.h"
 #include "nsError.h"
 #include <dlfcn.h>
 
 namespace mozilla {
 namespace dom {
 namespace bluetooth {
 
+static struct BluedroidFunctions {
+  bool initialized;
+  bool tried_initialization;
+
+  BluedroidFunctions() :
+    initialized(false),
+    tried_initialization(false)
+  {
+  }
+
+  int (* bt_enable)();
+  int (* bt_disable)();
+  int (* bt_is_enabled)();
+} sBluedroidFunctions;
+
 bool EnsureBluetoothInit() {
   if (sBluedroidFunctions.tried_initialization)
   {
     return sBluedroidFunctions.initialized;
   }
 
   sBluedroidFunctions.initialized = false;
   sBluedroidFunctions.tried_initialization = true;
@@ -44,11 +59,27 @@ bool EnsureBluetoothInit() {
   if(sBluedroidFunctions.bt_is_enabled == NULL) {
     NS_ERROR("Failed to attach bt_is_enabled function");
     return false;
   }
   sBluedroidFunctions.initialized = true;
   return true;
 }
 
+int IsBluetoothEnabled()
+{
+  return sBluedroidFunctions.bt_is_enabled();
+}
+
+int EnableBluetooth()
+{
+  return sBluedroidFunctions.bt_enable();
+}
+
+int DisableBluetooth()
+{
+  return sBluedroidFunctions.bt_disable();
+}
+
+
 }
 }
 }
--- a/dom/bluetooth/BluetoothFirmware.h
+++ b/dom/bluetooth/BluetoothFirmware.h
@@ -6,29 +6,18 @@
 
 #ifndef mozilla_dom_bluetooth_bluetoothfirmware_h__
 #define mozilla_dom_bluetooth_bluetoothfirmware_h__
 
 namespace mozilla {
 namespace dom {
 namespace bluetooth {
 
-static struct BluedroidFunctions {
-  bool initialized;
-  bool tried_initialization;
+bool EnsureBluetoothInit();
+int IsBluetoothEnabled();
+int EnableBluetooth();
+int DisableBluetooth();
 
-  BluedroidFunctions() :
-    initialized(false),
-    tried_initialization(false)
-  {
-  }
-  
-  int (* bt_enable)();
-  int (* bt_disable)();
-  int (* bt_is_enabled)();
-} sBluedroidFunctions;
-
-bool EnsureBluetoothInit();
 }
 }
 }
 
 #endif
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -217,17 +217,20 @@ IDBDatabase::Invalidate()
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   // Make sure we're closed too.
   Close();
 
   // When the IndexedDatabaseManager needs to invalidate databases, all it has
   // is an origin, so we call back into the manager to cancel any prompts for
   // our owner.
-  IndexedDatabaseManager::CancelPromptsForWindow(GetOwner());
+  nsPIDOMWindow* owner = GetOwner();
+  if (owner) {
+    IndexedDatabaseManager::CancelPromptsForWindow(owner);
+  }
 
   mInvalidated = true;
 }
 
 bool
 IDBDatabase::IsInvalidated()
 {
   return !!mInvalidated;
@@ -279,18 +282,16 @@ IDBDatabase::ExitSetVersionTransaction()
   NS_ASSERTION(mRunningVersionChange, "How did that happen?");
   mRunningVersionChange = false;
 }
 
 void
 IDBDatabase::OnUnlink()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(!GetOwner() && !GetScriptOwner(),
-               "Should have been cleared already!");
 
   // We've been unlinked, at the very least we should be able to prevent further
   // transactions from starting and unblock any other SetVersion callers.
   CloseInternal(true);
 
   // No reason for the IndexedDatabaseManager to track us any longer.
   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
   if (mgr) {
@@ -675,31 +676,32 @@ IDBDatabase::Close()
   return NS_OK;
 }
 
 nsresult
 IDBDatabase::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
 {
   NS_ENSURE_TRUE(aVisitor.mDOMEvent, NS_ERROR_UNEXPECTED);
 
-  if (!GetOwner()) {
+  nsPIDOMWindow* owner = GetOwner();
+  if (!owner) {
     return NS_OK;
   }
 
   if (aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
     nsString type;
     nsresult rv = aVisitor.mDOMEvent->GetType(type);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (type.EqualsLiteral(ERROR_EVT_STR)) {
       nsRefPtr<nsDOMEvent> duplicateEvent =
         CreateGenericEvent(type, eDoesNotBubble, eNotCancelable);
       NS_ENSURE_STATE(duplicateEvent);
 
-      nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(GetOwner()));
+      nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(owner));
       NS_ASSERTION(target, "How can this happen?!");
 
       bool dummy;
       rv = target->DispatchEvent(duplicateEvent, &dummy);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
--- a/dom/interfaces/html/nsIDOMHTMLSourceElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLSourceElement.idl
@@ -43,14 +43,15 @@
  * <source> element.
  *
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#source
  *
  * @status UNDER_DEVELOPMENT
  */
 
-[scriptable, uuid(4BF58085-9986-47B5-BB03-62BAA0451497)]
+[scriptable, uuid(af4b7ca2-2694-4672-a182-8a63be79c826)]
 interface nsIDOMHTMLSourceElement : nsIDOMHTMLElement
 {
            attribute DOMString src;
            attribute DOMString type;
+           attribute DOMString media;
 };
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -139,17 +139,19 @@ TimeoutSyncXHRWarning=Use of XMLHttpRequ
 JSONCharsetWarning=An attempt was made to declare a non-UTF-8 encoding for JSON retrieved using XMLHttpRequest. Only UTF-8 is supported for decoding JSON.
 MediaLoadExhaustedCandidates=All candidate resources failed to load. Media load paused.
 MediaLoadSourceMissingSrc=<source> element has no "src" attribute. Media resource load failed.
 # LOCALIZATION NOTE: %1$S is the Http error code the server returned (e.g. 404, 500, etc), %2$S is the URL of the media resource which failed to load.
 MediaLoadHttpError=HTTP load failed with status %1$S. Load of media resource %2$S failed.
 # LOCALIZATION NOTE: %S is the URL of the media resource which failed to load.
 MediaLoadInvalidURI=Invalid URI. Load of media resource %S failed.
 # LOCALIZATION NOTE: %1$S is the media resource's format/codec type (basically equivalent to the file type, e.g. MP4,AVI,WMV,MOV etc), %2$S is the URL of the media resource which failed to load.
-MediaLoadUnsupportedType=Specified "type" of "%1$S" is not supported. Load of media resource %2$S failed.
+MediaLoadUnsupportedTypeAttribute=Specified "type" attribute of "%1$S" is not supported. Load of media resource %2$S failed.
+# LOCALIZATION NOTE: %1$S is the "media" attribute value of the <source> element. It is a media query. %2$S is the URL of the media resource which failed to load.
+MediaLoadSourceMediaNotMatched=Specified "media" attribute of "%1$S" does not match the environment. Load of media resource %2$S failed.
 # LOCALIZATION NOTE: %1$S is the MIME type HTTP header being sent by the web server, %2$S is the URL of the media resource which failed to load.
 MediaLoadUnsupportedMimeType=HTTP "Content-Type" of "%1$S" is not supported. Load of media resource %2$S failed.
 # LOCALIZATION NOTE: %S is the URL of the media resource which failed to load because of error in decoding.
 MediaLoadDecodeError=Media resource %S could not be decoded.
 # LOCALIZATION NOTE: Do not translate "MozBlobBuilder" and "Blob"
 MozBlobBuilderWarning=Use of MozBlobBuilder is deprecated. Use Blob constructor instead.
 # LOCALIZATION NOTE: Do not translate "DOMException", "code" and "name"
 DOMExceptionCodeWarning=Use of DOMException's code attribute is deprecated. Use name instead.
--- a/dom/system/OSFileConstants.cpp
+++ b/dom/system/OSFileConstants.cpp
@@ -221,17 +221,17 @@ static dom::ConstantSpec gWinProperties[
   // CreateFile attributes
   INT_CONSTANT(FILE_ATTRIBUTE_ARCHIVE),
   INT_CONSTANT(FILE_ATTRIBUTE_DIRECTORY),
   INT_CONSTANT(FILE_ATTRIBUTE_NORMAL),
   INT_CONSTANT(FILE_ATTRIBUTE_READONLY),
   INT_CONSTANT(FILE_ATTRIBUTE_TEMPORARY),
 
   // SetFilePointer error constant
-  { "INVALID_HANDLE_VALUE", INT_TO_JSVAL((int)INVALID_HANDLE_VALUE) },
+  { "INVALID_HANDLE_VALUE", INT_TO_JSVAL(INT_PTR(INVALID_HANDLE_VALUE)) },
 
 
   // CreateFile flags
   INT_CONSTANT(FILE_FLAG_DELETE_ON_CLOSE),
 
   // SetFilePointer methods
   INT_CONSTANT(FILE_BEGIN),
   INT_CONSTANT(FILE_CURRENT),
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -516,17 +516,17 @@ protected:
 /* This is the main class used for all the drawing. It is created through the
  * factory and accepts drawing commands. The results of drawing to a target
  * may be used either through a Snapshot or by flushing the target and directly
  * accessing the backing store a DrawTarget was created with.
  */
 class DrawTarget : public RefCounted<DrawTarget>
 {
 public:
-  DrawTarget() : mTransformDirty(false) {}
+  DrawTarget() : mTransformDirty(false), mPermitSubpixelAA(false) {}
   virtual ~DrawTarget() {}
 
   virtual BackendType GetType() const = 0;
   /**
    * Returns a SourceSurface which is a snapshot of the current contents of the DrawTarget.
    * Multiple calls to Snapshot() without any drawing operations in between will
    * normally return the same SourceSurface object.
    */
@@ -787,21 +787,31 @@ public:
    */
   void SetOpaqueRect(const IntRect &aRect) {
     mOpaqueRect = aRect;
   }
 
   const IntRect &GetOpaqueRect() const {
     return mOpaqueRect;
   }
+
+  void SetPermitSubpixelAA(bool aPermitSubpixelAA) {
+    mPermitSubpixelAA = aPermitSubpixelAA;
+  }
+
+  bool GetPermitSubpixelAA() {
+    return mPermitSubpixelAA;
+  }
+
 protected:
   UserData mUserData;
   Matrix mTransform;
   IntRect mOpaqueRect;
   bool mTransformDirty : 1;
+  bool mPermitSubpixelAA : 1;
 
   SurfaceFormat mFormat;
 };
 
 class GFX2D_API Factory
 {
 public:
   static bool HasSSE2();
--- a/gfx/2d/Blur.cpp
+++ b/gfx/2d/Blur.cpp
@@ -34,17 +34,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "mozilla/gfx/Blur.h"
 
 #include <algorithm>
 #include <math.h>
 #include <string.h>
 
-#include "CheckedInt.h"
+#include "mozilla/CheckedInt.h"
 #include "mozilla/Util.h"
 
 #ifndef M_PI
 #define M_PI 3.14159265358979323846
 #endif
 
 using namespace std;
 
@@ -76,17 +76,17 @@ BoxBlurHorizontal(unsigned char* aInput,
 
     int32_t boxSize = aLeftLobe + aRightLobe + 1;
     bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
                                   aWidth <= aSkipRect.XMost();
     if (boxSize == 1) {
         memcpy(aOutput, aInput, aWidth*aRows);
         return;
     }
-    PRUint32 reciprocal = (PRUint64(1) << 32)/boxSize;
+    uint32_t reciprocal = (uint64_t(1) << 32) / boxSize;
 
     for (int32_t y = 0; y < aRows; y++) {
         // Check whether the skip rect intersects this row. If the skip
         // rect covers the whole surface in this row, we can avoid
         // this row entirely (and any others along the skip rect).
         bool inSkipRectY = y >= aSkipRect.y &&
                            y < aSkipRect.YMost();
         if (inSkipRectY && skipRectCoversWholeRow) {
@@ -123,17 +123,17 @@ BoxBlurHorizontal(unsigned char* aInput,
                     pos = min(pos, aWidth - 1);
                     alphaSum += aInput[aWidth * y + pos];
                 }
             }
             int32_t tmp = x - aLeftLobe;
             int32_t last = max(tmp, 0);
             int32_t next = min(tmp + boxSize, aWidth - 1);
 
-            aOutput[aWidth * y + x] = (PRUint64(alphaSum)*reciprocal) >> 32;
+            aOutput[aWidth * y + x] = (uint64_t(alphaSum) * reciprocal) >> 32;
 
             alphaSum += aInput[aWidth * y + next] -
                         aInput[aWidth * y + last];
         }
     }
 }
 
 /**
@@ -154,17 +154,17 @@ BoxBlurVertical(unsigned char* aInput,
 
     int32_t boxSize = aTopLobe + aBottomLobe + 1;
     bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
                                      aRows <= aSkipRect.YMost();
     if (boxSize == 1) {
         memcpy(aOutput, aInput, aWidth*aRows);
         return;
     }
-    PRUint32 reciprocal = (PRUint64(1) << 32)/boxSize;
+    uint32_t reciprocal = (uint64_t(1) << 32) / boxSize;
 
     for (int32_t x = 0; x < aWidth; x++) {
         bool inSkipRectX = x >= aSkipRect.x &&
                            x < aSkipRect.XMost();
         if (inSkipRectX && skipRectCoversWholeColumn) {
             x = aSkipRect.XMost() - 1;
             continue;
         }
@@ -194,17 +194,17 @@ BoxBlurVertical(unsigned char* aInput,
                     pos = min(pos, aRows - 1);
                     alphaSum += aInput[aWidth * pos + x];
                 }
             }
             int32_t tmp = y - aTopLobe;
             int32_t last = max(tmp, 0);
             int32_t next = min(tmp + boxSize, aRows - 1);
 
-            aOutput[aWidth * y + x] = (PRUint64(alphaSum)*reciprocal) >> 32;
+            aOutput[aWidth * y + x] = (uint64_t(alphaSum) * reciprocal) >> 32;
 
             alphaSum += aInput[aWidth * next + x] -
                         aInput[aWidth * last + x];
         }
     }
 }
 
 static void ComputeLobes(int32_t aRadius, int32_t aLobes[3][2])
@@ -404,22 +404,22 @@ AlphaBoxBlur::AlphaBoxBlur(const Rect& a
     mSkipRect -= shadowIntRect.TopLeft();
   } else {
     mSkipRect = IntRect(0, 0, 0, 0);
   }
 
   mRect = IntRect(rect.x, rect.y, rect.width, rect.height);
 
   CheckedInt<int32_t> stride = RoundUpToMultipleOf4(mRect.width);
-  if (stride.valid()) {
+  if (stride.isValid()) {
     mStride = stride.value();
 
     CheckedInt<int32_t> size = CheckedInt<int32_t>(mStride) * mRect.height *
                                sizeof(unsigned char);
-    if (size.valid()) {
+    if (size.isValid()) {
       mData = static_cast<unsigned char*>(malloc(size.value()));
       memset(mData, 0, size.value());
     }
   }
 }
 
 AlphaBoxBlur::~AlphaBoxBlur()
 {
--- a/gfx/2d/DrawTargetD2D.cpp
+++ b/gfx/2d/DrawTargetD2D.cpp
@@ -42,16 +42,18 @@
 #include "PathD2D.h"
 #include "GradientStopsD2D.h"
 #include "ScaledFontDWrite.h"
 #include "ImageScaling.h"
 #include "Logging.h"
 #include "Tools.h"
 #include <algorithm>
 
+#include <dwrite.h>
+
 #ifndef M_PI
 #define M_PI 3.14159265358979323846
 #endif
 
 typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)(
     D2D1_FACTORY_TYPE factoryType,
     REFIID iid,
     CONST D2D1_FACTORY_OPTIONS *pFactoryOptions,
@@ -62,27 +64,34 @@ typedef HRESULT (WINAPI*D3D10CreateEffec
     void *pData,
     SIZE_T DataLength,
     UINT FXFlags,
     ID3D10Device *pDevice,
     ID3D10EffectPool *pEffectPool,
     ID3D10Effect **ppEffect
 );
 
+typedef HRESULT (WINAPI*DWriteCreateFactoryFunc)(
+  DWRITE_FACTORY_TYPE factoryType,
+  REFIID iid,
+  IUnknown **factory
+);
+
 using namespace std;
 
 namespace mozilla {
 namespace gfx {
 
 struct Vertex {
   float x;
   float y;
 };
 
 ID2D1Factory *DrawTargetD2D::mFactory;
+IDWriteFactory *DrawTargetD2D::mDWriteFactory;
 
 // Helper class to restore surface contents that was clipped out but may have
 // been altered by a drawing call.
 class AutoSaveRestoreClippedOut
 {
 public:
   AutoSaveRestoreClippedOut(DrawTargetD2D *aDT)
     : mDT(aDT)
@@ -358,39 +367,19 @@ DrawTargetD2D::DrawSurfaceWithShadow(Sou
 
   RefPtr<ID3D10RenderTargetView> destRTView = mRTView;
   RefPtr<ID3D10Texture2D> destTexture;
   HRESULT hr;
 
   RefPtr<ID3D10Texture2D> maskTexture;
   RefPtr<ID3D10ShaderResourceView> maskSRView;
   if (mPushedClips.size()) {
-    // Here we render a mask of the clipped out area for use as an input to the
-    // shadow drawing.
-
-    CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_A8_UNORM,
-                               mSize.width, mSize.height,
-                               1, 1);
-    desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
-
-    hr = mDevice->CreateTexture2D(&desc, NULL, byRef(maskTexture));
-
-    RefPtr<ID2D1RenderTarget> rt = CreateRTForTexture(maskTexture);
-
-    RefPtr<ID2D1SolidColorBrush> brush;
-    rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), byRef(brush));
-    
-    RefPtr<ID2D1Geometry> geometry = GetClippedGeometry();
-
-    rt->BeginDraw();
-    rt->Clear(D2D1::ColorF(0, 0));
-    rt->FillGeometry(geometry, brush);
-    rt->EndDraw();
-
-    mDevice->CreateShaderResourceView(maskTexture, NULL, byRef(maskSRView));
+    EnsureClipMaskTexture();
+
+    mDevice->CreateShaderResourceView(mCurrentClipMaskTexture, NULL, byRef(maskSRView));
   }
 
   IntSize srcSurfSize;
   ID3D10RenderTargetView *rtViews;
   D3D10_VIEWPORT viewport;
 
   UINT stride = sizeof(Vertex);
   UINT offset = 0;
@@ -886,63 +875,49 @@ DrawTargetD2D::FillGlyphs(ScaledFont *aF
 {
   if (aFont->GetType() != FONT_DWRITE) {
     gfxDebug() << *this << ": Ignoring drawing call for incompatible font.";
     return;
   }
 
   ScaledFontDWrite *font = static_cast<ScaledFontDWrite*>(aFont);
 
-  ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern);
-
-  PrepareForDrawing(rt);
-
   IDWriteRenderingParams *params = NULL;
   if (aRenderOptions) {
     if (aRenderOptions->GetType() != FONT_DWRITE) {
     gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions.";
     // This should never happen.
     MOZ_ASSERT(false);
     } else {
       params = static_cast<const GlyphRenderingOptionsDWrite*>(aRenderOptions)->mParams;
     }
   }
 
+  if (mFormat == FORMAT_B8G8R8A8 && mPermitSubpixelAA &&
+      aOptions.mCompositionOp == OP_OVER && aPattern.GetType() == PATTERN_COLOR) {
+    if (FillGlyphsManual(font, aBuffer,
+                         static_cast<const ColorPattern*>(&aPattern)->mColor,
+                         params, aOptions)) {
+      return;
+    }
+  }
+
+  ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern);
+
+  PrepareForDrawing(rt);
+
   rt->SetTextRenderingParams(params);
 
   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
 
-  DWRITE_GLYPH_RUN glyphRun;
-
-  glyphRun.bidiLevel = 0;
-  glyphRun.fontEmSize = font->mSize;
-  glyphRun.isSideways = FALSE;
-  glyphRun.fontFace = font->mFontFace;
-  glyphRun.glyphCount = aBuffer.mNumGlyphs;
-  
-  std::vector<UINT16> indices;
-  std::vector<FLOAT> advances;
-  std::vector<DWRITE_GLYPH_OFFSET> offsets;
-  indices.resize(aBuffer.mNumGlyphs);
-  advances.resize(aBuffer.mNumGlyphs);
-  offsets.resize(aBuffer.mNumGlyphs);
-
-  memset(&advances.front(), 0, sizeof(FLOAT) * aBuffer.mNumGlyphs);
-  for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
-    indices[i] = aBuffer.mGlyphs[i].mIndex;
-    offsets[i].advanceOffset = aBuffer.mGlyphs[i].mPosition.x;
-    offsets[i].ascenderOffset = -aBuffer.mGlyphs[i].mPosition.y;
-  }
-
-  glyphRun.glyphAdvances = &advances.front();
-  glyphRun.glyphIndices = &indices.front();
-  glyphRun.glyphOffsets = &offsets.front();
+  AutoDWriteGlyphRun autoRun;
+  DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun);
 
   if (brush) {
-    rt->DrawGlyphRun(D2D1::Point2F(), &glyphRun, brush);
+    rt->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush);
   }
 
   FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, Rect(0, 0, (Float)mSize.width, (Float)mSize.height));
 }
 
 void
 DrawTargetD2D::Mask(const Pattern &aSource,
                     const Pattern &aMask,
@@ -976,16 +951,19 @@ DrawTargetD2D::Mask(const Pattern &aSour
 void
 DrawTargetD2D::PushClip(const Path *aPath)
 {
   if (aPath->GetBackendType() != BACKEND_DIRECT2D) {
     gfxDebug() << *this << ": Ignoring clipping call for incompatible path.";
     return;
   }
 
+  mCurrentClipMaskTexture = NULL;
+  mCurrentClippedGeometry = NULL;
+
   RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath));
 
   PushedClip clip;
   clip.mTransform = D2DMatrix(mTransform);
   clip.mPath = pathD2D;
   
   RefPtr<ID2D1Layer> layer;
   pathD2D->mGeometry->GetBounds(clip.mTransform, &clip.mBounds);
@@ -1012,16 +990,18 @@ DrawTargetD2D::PushClip(const Path *aPat
                                          clip.mTransform, 1.0f, NULL,
                                          options), layer);
   }
 }
 
 void
 DrawTargetD2D::PushClipRect(const Rect &aRect)
 {
+  mCurrentClipMaskTexture = NULL;
+  mCurrentClippedGeometry = NULL;
   if (!mTransform.IsRectilinear()) {
     // Whoops, this isn't a rectangle in device space, Direct2D will not deal
     // with this transform the way we want it to.
     // See remarks: http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx
 
     RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
     pathBuilder->MoveTo(aRect.TopLeft());
     pathBuilder->LineTo(aRect.TopRight());
@@ -1043,16 +1023,18 @@ DrawTargetD2D::PushClipRect(const Rect &
   if (mClipsArePushed) {
     mRT->PushAxisAlignedClip(clip.mBounds, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
   }
 }
 
 void
 DrawTargetD2D::PopClip()
 {
+  mCurrentClipMaskTexture = NULL;
+  mCurrentClippedGeometry = NULL;
   if (mClipsArePushed) {
     if (mPushedClips.back().mLayer) {
       mRT->PopLayer();
     } else {
       mRT->PopAxisAlignedClip();
     }
   }
   mPushedClips.pop_back();
@@ -1317,17 +1299,17 @@ DrawTargetD2D::InitD3D10Data()
  */
 bool
 DrawTargetD2D::InitD2DRenderTarget()
 {
   if (!factory()) {
     return false;
   }
 
-  mRT = CreateRTForTexture(mTexture);
+  mRT = CreateRTForTexture(mTexture, mFormat);
 
   if (!mRT) {
     return false;
   }
 
   mRT->BeginDraw();
 
   if (mFormat == FORMAT_B8G8R8X8) {
@@ -1344,34 +1326,17 @@ DrawTargetD2D::PrepareForDrawing(ID2D1Re
     if (mPushedClips.size()) {
       // The transform of clips is relative to the world matrix, since we use the total
       // transform for the clips, make the world matrix identity.
       aRT->SetTransform(D2D1::IdentityMatrix());
       if (aRT == mRT) {
         mTransformDirty = true;
         mClipsArePushed = true;
       }
-      for (std::vector<PushedClip>::iterator iter = mPushedClips.begin();
-            iter != mPushedClips.end(); iter++) {
-        D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE;
-        if (iter->mLayer) {
-          D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE;
-
-          if (mFormat == FORMAT_B8G8R8X8) {
-            options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE;
-          }
-
-          aRT->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), iter->mPath->mGeometry,
-                                                D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
-                                                iter->mTransform, 1.0f, NULL,
-                                                options), iter->mLayer);
-        } else {
-          aRT->PushAxisAlignedClip(iter->mBounds, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
-        }
-      }
+      PushClipsToRT(aRT);
     }
   }
   FlushTransformToRT();
   MarkChanged();
 
   if (aRT == mTempRT) {
     mTempRT->SetTransform(D2DMatrix(mTransform));
   }
@@ -1488,17 +1453,17 @@ DrawTargetD2D::GetRTForOperation(Composi
 
   EnsureViews();
 
   if (!mRTView || !mSRView) {
     gfxDebug() << *this << ": Failed to get required views. Defaulting to OP_OVER.";
     return mRT;
   }
 
-  mTempRT = CreateRTForTexture(mTempTexture);
+  mTempRT = CreateRTForTexture(mTempTexture, FORMAT_B8G8R8A8);
 
   if (!mTempRT) {
     return mRT;
   }
 
   mTempRT->BeginDraw();
 
   mTempRT->Clear(D2D1::ColorF(0, 0));
@@ -1519,23 +1484,17 @@ DrawTargetD2D::FinalizeRTForOperation(Co
   if (aOperator == OP_OVER && !IsPatternSupportedByD2D(aPattern)) {
     return;
   }
 
   if (!mTempRT) {
     return;
   }
 
-  for (int i = mPushedClips.size() - 1; i >= 0; i--) {
-    if (mPushedClips[i].mLayer) {
-      mTempRT->PopLayer();
-    } else {
-      mTempRT->PopAxisAlignedClip();
-    }
-  }
+  PopClipsFromRT(mTempRT);
 
   mRT->Flush();
   mTempRT->Flush();
 
   AutoSaveRestoreClippedOut restoreClippedOut(this);
 
   bool needsWriteBack =
     !IsOperatorBoundByMask(aOperator) && mPushedClips.size();
@@ -1588,47 +1547,65 @@ DrawTargetD2D::FinalizeRTForOperation(Co
   mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), NULL, 0xffffffff);
   
   mDevice->Draw(4, 0);
 }
 
 TemporaryRef<ID2D1Geometry>
 DrawTargetD2D::GetClippedGeometry()
 {
+  if (mCurrentClippedGeometry) {
+    return mCurrentClippedGeometry;
+  }
+
   RefPtr<ID2D1GeometrySink> currentSink;
-  RefPtr<ID2D1PathGeometry> clippedGeometry;
-
-  factory()->CreatePathGeometry(byRef(clippedGeometry));
-  clippedGeometry->Open(byRef(currentSink));
+
+  factory()->CreatePathGeometry(byRef(mCurrentClippedGeometry));
+  mCurrentClippedGeometry->Open(byRef(currentSink));
       
   std::vector<DrawTargetD2D::PushedClip>::iterator iter = mPushedClips.begin();
-  iter->mPath->GetGeometry()->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
-                                    iter->mTransform, currentSink);
-
+
+  if (iter->mPath) {
+    iter->mPath->GetGeometry()->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+                                         iter->mTransform, currentSink);
+  } else {
+    RefPtr<ID2D1RectangleGeometry> rectGeom;
+    factory()->CreateRectangleGeometry(iter->mBounds, byRef(rectGeom));
+    rectGeom->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+                       D2D1::IdentityMatrix(), currentSink);
+  }
   currentSink->Close();
 
   iter++;
   for (;iter != mPushedClips.end(); iter++) {
     RefPtr<ID2D1PathGeometry> newGeom;
     factory()->CreatePathGeometry(byRef(newGeom));
 
     newGeom->Open(byRef(currentSink));
-    clippedGeometry->CombineWithGeometry(iter->mPath->GetGeometry(), D2D1_COMBINE_MODE_INTERSECT,
-                                      iter->mTransform, currentSink);
+
+    if (iter->mPath) {
+      mCurrentClippedGeometry->CombineWithGeometry(iter->mPath->GetGeometry(), D2D1_COMBINE_MODE_INTERSECT,
+                                           iter->mTransform, currentSink);
+    } else {
+      RefPtr<ID2D1RectangleGeometry> rectGeom;
+      factory()->CreateRectangleGeometry(iter->mBounds, byRef(rectGeom));
+      mCurrentClippedGeometry->CombineWithGeometry(rectGeom, D2D1_COMBINE_MODE_INTERSECT,
+                                                   D2D1::IdentityMatrix(), currentSink);
+    }
 
     currentSink->Close();
 
-    clippedGeometry = newGeom;
+    mCurrentClippedGeometry = newGeom;
   }
 
-  return clippedGeometry;
+  return mCurrentClippedGeometry;
 }
 
 TemporaryRef<ID2D1RenderTarget>
-DrawTargetD2D::CreateRTForTexture(ID3D10Texture2D *aTexture)
+DrawTargetD2D::CreateRTForTexture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat)
 {
   HRESULT hr;
 
   RefPtr<IDXGISurface> surface;
   RefPtr<ID2D1RenderTarget> rt;
 
   hr = aTexture->QueryInterface((IDXGISurface**)byRef(surface));
 
@@ -1637,17 +1614,17 @@ DrawTargetD2D::CreateRTForTexture(ID3D10
     return NULL;
   }
 
   D3D10_TEXTURE2D_DESC desc;
   aTexture->GetDesc(&desc);
 
   D2D1_ALPHA_MODE alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
 
-  if (mFormat == FORMAT_B8G8R8X8 && aTexture == mTexture) {
+  if (aFormat == FORMAT_B8G8R8X8 && aTexture == mTexture) {
     alphaMode = D2D1_ALPHA_MODE_IGNORE;
   }
 
   D2D1_RENDER_TARGET_PROPERTIES props =
     D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(desc.Format, alphaMode));
   hr = factory()->CreateDxgiSurfaceRenderTarget(surface, props, byRef(rt));
 
   if (FAILED(hr)) {
@@ -1694,28 +1671,262 @@ DrawTargetD2D::EnsureViews()
     gfxWarning() << *this << "Failed to create rendertarget view for temp texture. Code: " << hr;
   }
 }
 
 void
 DrawTargetD2D::PopAllClips()
 {
   if (mClipsArePushed) {
-    for (int i = mPushedClips.size() - 1; i >= 0; i--) {
-      if (mPushedClips[i].mLayer) {
-        mRT->PopLayer();
-      } else {
-        mRT->PopAxisAlignedClip();
-      }
-    }
+    PopClipsFromRT(mRT);
   
     mClipsArePushed = false;
   }
 }
 
+void
+DrawTargetD2D::PushClipsToRT(ID2D1RenderTarget *aRT)
+{
+  for (std::vector<PushedClip>::iterator iter = mPushedClips.begin();
+        iter != mPushedClips.end(); iter++) {
+    D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE;
+    if (iter->mLayer) {
+      D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE;
+
+      if (mFormat == FORMAT_B8G8R8X8) {
+        options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE;
+      }
+
+      aRT->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), iter->mPath->mGeometry,
+                                            D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
+                                            iter->mTransform, 1.0f, NULL,
+                                            options), iter->mLayer);
+    } else {
+      aRT->PushAxisAlignedClip(iter->mBounds, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+    }
+  }
+}
+
+void
+DrawTargetD2D::PopClipsFromRT(ID2D1RenderTarget *aRT)
+{
+  for (int i = mPushedClips.size() - 1; i >= 0; i--) {
+    if (mPushedClips[i].mLayer) {
+      aRT->PopLayer();
+    } else {
+      aRT->PopAxisAlignedClip();
+    }
+  }
+}
+
+void
+DrawTargetD2D::EnsureClipMaskTexture()
+{
+  if (mCurrentClipMaskTexture || mPushedClips.empty()) {
+    return;
+  }
+  
+  CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_A8_UNORM,
+                             mSize.width,
+                             mSize.height,
+                             1, 1);
+  desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
+
+  HRESULT hr = mDevice->CreateTexture2D(&desc, NULL, byRef(mCurrentClipMaskTexture));
+
+  if (FAILED(hr)) {
+    gfxWarning() << "Failed to create texture for ClipMask!";
+    return;
+  }
+
+  RefPtr<ID2D1RenderTarget> rt = CreateRTForTexture(mCurrentClipMaskTexture, FORMAT_A8);
+
+  if (!rt) {
+    gfxWarning() << "Failed to create RT for ClipMask!";
+    return;
+  }
+  
+  RefPtr<ID2D1SolidColorBrush> brush;
+  rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), byRef(brush));
+    
+  RefPtr<ID2D1Geometry> geometry = GetClippedGeometry();
+
+  rt->BeginDraw();
+  rt->Clear(D2D1::ColorF(0, 0));
+  rt->FillGeometry(geometry, brush);
+  rt->EndDraw();
+}
+
+bool
+DrawTargetD2D::FillGlyphsManual(ScaledFontDWrite *aFont,
+                                const GlyphBuffer &aBuffer,
+                                const Color &aColor,
+                                IDWriteRenderingParams *aParams,
+                                const DrawOptions &aOptions)
+{
+  HRESULT hr;
+
+  RefPtr<IDWriteRenderingParams> params;
+
+  if (aParams) {
+    params = aParams;
+  } else {
+    mRT->GetTextRenderingParams(byRef(params));
+  }
+
+  DWRITE_RENDERING_MODE renderMode = DWRITE_RENDERING_MODE_DEFAULT;
+  if (params) {
+    hr = aFont->mFontFace->GetRecommendedRenderingMode(
+      (FLOAT)aFont->mSize,
+      1.0f,
+      DWRITE_MEASURING_MODE_NATURAL,
+      params,
+      &renderMode);
+    if (FAILED(hr)) {
+      // this probably never happens, but let's play it safe
+      renderMode = DWRITE_RENDERING_MODE_DEFAULT;
+    }
+  }
+
+  // Deal with rendering modes CreateGlyphRunAnalysis doesn't accept.
+  switch (renderMode) {
+  case DWRITE_RENDERING_MODE_ALIASED:
+    // ClearType texture creation will fail in this mode, so bail out
+    return false;
+  case DWRITE_RENDERING_MODE_DEFAULT:
+    // As per DWRITE_RENDERING_MODE documentation, pick Natural for font
+    // sizes under 16 ppem
+    if (aFont->mSize < 16.0f) {
+      renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
+    } else {
+      renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
+    }
+    break;
+  case DWRITE_RENDERING_MODE_OUTLINE:
+    renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
+    break;
+  default:
+    break;
+  }
+
+  DWRITE_MEASURING_MODE measureMode =
+    renderMode <= DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC ? DWRITE_MEASURING_MODE_GDI_CLASSIC :
+    renderMode == DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL ? DWRITE_MEASURING_MODE_GDI_NATURAL :
+    DWRITE_MEASURING_MODE_NATURAL;
+
+  DWRITE_MATRIX mat = DWriteMatrixFromMatrix(mTransform);
+
+  AutoDWriteGlyphRun autoRun;
+  DWriteGlyphRunFromGlyphs(aBuffer, aFont, &autoRun);
+
+  RefPtr<IDWriteGlyphRunAnalysis> analysis;
+  hr = GetDWriteFactory()->CreateGlyphRunAnalysis(&autoRun, 1.0f, &mat,
+                                                  renderMode, measureMode, 0, 0, byRef(analysis));
+
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  RECT bounds;
+  hr = analysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1, &bounds);
+  IntRect rectBounds(bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top);
+  IntRect surfBounds(IntPoint(0, 0), mSize);
+
+  rectBounds.IntersectRect(rectBounds, surfBounds);
+
+  if (rectBounds.IsEmpty()) {
+    // Nothing to do.
+    return true;
+  }
+
+  RefPtr<ID3D10Texture2D> tex = CreateTextureForAnalysis(analysis, rectBounds);
+
+  if (!tex) {
+    return false;
+  }
+
+  RefPtr<ID3D10ShaderResourceView> srView;
+  hr = mDevice->CreateShaderResourceView(tex, NULL, byRef(srView));
+
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  MarkChanged();
+
+  // Prepare our background texture for drawing.
+  PopAllClips();
+  mRT->Flush();
+
+  SetupStateForRendering();
+
+  ID3D10EffectTechnique *technique = mPrivateData->mEffect->GetTechniqueByName("SampleTextTexture");
+
+  mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()->
+    SetFloatVector(ShaderConstantRectD3D10(-1.0f + ((Float(rectBounds.x) / mSize.width) * 2.0f),
+                                           1.0f - (Float(rectBounds.y) / mSize.height * 2.0f),
+                                           (Float(rectBounds.width) / mSize.width) * 2.0f,
+                                           (-Float(rectBounds.height) / mSize.height) * 2.0f));
+  mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()->
+    SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f));
+  FLOAT color[4] = { aColor.r, aColor.g, aColor.b, aColor.a };
+  mPrivateData->mEffect->GetVariableByName("TextColor")->AsVector()->
+    SetFloatVector(color);
+  
+  mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView);
+
+  bool isMasking = false;
+
+  if (!mPushedClips.empty()) {
+    RefPtr<ID2D1Geometry> geom = GetClippedGeometry();
+
+    RefPtr<ID2D1RectangleGeometry> rectGeom;
+    factory()->CreateRectangleGeometry(D2D1::RectF(rectBounds.x, rectBounds.y,
+                                                   rectBounds.width + rectBounds.x,
+                                                   rectBounds.height + rectBounds.y),
+                                       byRef(rectGeom));
+
+    D2D1_GEOMETRY_RELATION relation;
+    if (FAILED(geom->CompareWithGeometry(rectGeom, D2D1::IdentityMatrix(), &relation)) ||
+        relation != D2D1_GEOMETRY_RELATION_CONTAINS) {
+      isMasking = true;
+    }        
+  }
+  
+  if (isMasking) {
+    EnsureClipMaskTexture();
+
+    RefPtr<ID3D10ShaderResourceView> srViewMask;
+    hr = mDevice->CreateShaderResourceView(mCurrentClipMaskTexture, NULL, byRef(srViewMask));
+
+    if (FAILED(hr)) {
+      return false;
+    }
+
+    mPrivateData->mEffect->GetVariableByName("mask")->AsShaderResource()->SetResource(srViewMask);
+
+    mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()->
+      SetFloatVector(ShaderConstantRectD3D10(Float(rectBounds.x) / mSize.width, Float(rectBounds.y) / mSize.height,
+                                             Float(rectBounds.width) / mSize.width, Float(rectBounds.height) / mSize.height));
+
+    technique->GetPassByIndex(1)->Apply(0);
+  } else {
+    technique->GetPassByIndex(0)->Apply(0);
+  }  
+
+  RefPtr<ID3D10RenderTargetView> rtView;
+  ID3D10RenderTargetView *rtViews;
+  mDevice->CreateRenderTargetView(mTexture, NULL, byRef(rtView));
+
+  rtViews = rtView;
+  mDevice->OMSetRenderTargets(1, &rtViews, NULL);
+
+  mDevice->Draw(4, 0);
+}
+
 TemporaryRef<ID2D1Brush>
 DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
 {
   if (IsPatternSupportedByD2D(aPattern)) {
     RefPtr<ID2D1SolidColorBrush> colBrush;
     mRT->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), byRef(colBrush));
     return colBrush;
   }
@@ -1967,16 +2178,80 @@ DrawTargetD2D::CreateGradientTexture(con
   data.pSysMem = &textureData.front();
 
   RefPtr<ID3D10Texture1D> tex;
   mDevice->CreateTexture1D(&desc, &data, byRef(tex));
 
   return tex;
 }
 
+TemporaryRef<ID3D10Texture2D>
+DrawTargetD2D::CreateTextureForAnalysis(IDWriteGlyphRunAnalysis *aAnalysis, const IntRect &aBounds)
+{
+  HRESULT hr;
+
+  uint32_t bufferSize = aBounds.width * aBounds.height * 3;
+
+  RECT bounds;
+  bounds.left = aBounds.x;
+  bounds.top = aBounds.y;
+  bounds.right = aBounds.x + aBounds.width;
+  bounds.bottom = aBounds.y + aBounds.height;
+
+  // Add one byte so we can safely read a 32-bit int when copying the last
+  // 3 bytes.
+  BYTE *texture = new BYTE[bufferSize + 1];
+  hr = aAnalysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1, &bounds, texture, bufferSize);
+
+  if (FAILED(hr)) {
+    delete [] texture;
+    return NULL;
+  }
+
+  int alignedBufferSize = aBounds.width * aBounds.height * 4;
+
+  // Create a one-off immutable texture from system memory.
+  BYTE *alignedTextureData = new BYTE[alignedBufferSize];
+  for (int y = 0; y < aBounds.height; y++) {
+    for (int x = 0; x < aBounds.width; x++) {
+      // Copy 3 Bpp source to 4 Bpp destination memory used for
+      // texture creation. D3D10 has no 3 Bpp texture format we can
+      // use.
+      //
+      // Since we don't care what ends up in the alpha pixel of the
+      // destination, therefor we can simply copy a normal 32 bit
+      // integer each time, filling the alpha pixel of the destination
+      // with the first subpixel of the next pixel from the source.
+      *((int*)(alignedTextureData + (y * aBounds.width + x) * 4)) =
+        *((int*)(texture + (y * aBounds.width + x) * 3));
+    }
+  }
+
+  D3D10_SUBRESOURCE_DATA data;
+  
+  CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
+                             aBounds.width, aBounds.height,
+                             1, 1);
+  desc.Usage = D3D10_USAGE_IMMUTABLE;
+
+  data.SysMemPitch = aBounds.width * 4;
+  data.pSysMem = alignedTextureData;
+
+  RefPtr<ID3D10Texture2D> tex;
+  hr = mDevice->CreateTexture2D(&desc, &data, byRef(tex));
+	
+  delete [] alignedTextureData;
+  delete [] texture;
+
+  if (FAILED(hr)) {
+    return NULL;
+  }
+
+  return tex;
+}
 TemporaryRef<ID2D1Bitmap>
 DrawTargetD2D::CreatePartialBitmapForSurface(SourceSurfaceD2D *aSurface, Matrix &aMatrix)
 {
   RefPtr<ID2D1Bitmap> bitmap;
 
   // This is where things get complicated. The source surface was
   // created for a surface that was too large to fit in a texture.
   // We'll need to figure out if we can work with a partial upload
@@ -2141,16 +2416,38 @@ DrawTargetD2D::SetupEffectForRadialGradi
       GetPassByIndex(offset * 2 + 1)->Apply(0);
   } else {
     mPrivateData->mEffect->GetVariableByName("A")->AsScalar()->SetFloat(A);
     mPrivateData->mEffect->GetTechniqueByName("SampleRadialGradient")->
       GetPassByIndex(offset * 2)->Apply(0);
   }
 }
 
+void
+DrawTargetD2D::SetupStateForRendering()
+{
+  UINT stride = sizeof(Vertex);
+  UINT offset = 0;
+  ID3D10Buffer *buff = mPrivateData->mVB;
+
+  mDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+  mDevice->IASetVertexBuffers(0, 1, &buff, &stride, &offset);
+  mDevice->IASetInputLayout(mPrivateData->mInputLayout);
+
+  D3D10_VIEWPORT viewport;
+  viewport.MaxDepth = 1;
+  viewport.MinDepth = 0;
+  viewport.Height = mSize.height;
+  viewport.Width = mSize.width;
+  viewport.TopLeftX = 0;
+  viewport.TopLeftY = 0;
+
+  mDevice->RSSetViewports(1, &viewport);
+}
+
 ID2D1Factory*
 DrawTargetD2D::factory()
 {
   if (mFactory) {
     return mFactory;
   }
 
   D2D1CreateFactoryFunc createD2DFactory;
@@ -2177,10 +2474,37 @@ DrawTargetD2D::factory()
 
   if (FAILED(hr)) {
     gfxWarning() << "Failed to create Direct2D factory.";
   }
 
   return mFactory;
 }
 
+IDWriteFactory*
+DrawTargetD2D::GetDWriteFactory()
+{
+  if (mDWriteFactory) {
+    return mDWriteFactory;
+  }
+
+  DWriteCreateFactoryFunc createDWriteFactory;
+  HMODULE dwriteModule = LoadLibraryW(L"dwrite.dll");
+  createDWriteFactory = (DWriteCreateFactoryFunc)
+    GetProcAddress(dwriteModule, "DWriteCreateFactory");
+
+  if (!createDWriteFactory) {
+    gfxWarning() << "Failed to locate DWriteCreateFactory function.";
+    return NULL;
+  }
+
+  HRESULT hr = createDWriteFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory),
+                                   reinterpret_cast<IUnknown**>(&mDWriteFactory));
+
+  if (FAILED(hr)) {
+    gfxWarning() << "Failed to create DWrite Factory.";
+  }
+
+  return mDWriteFactory;
+}
+
 }
 }
--- a/gfx/2d/DrawTargetD2D.h
+++ b/gfx/2d/DrawTargetD2D.h
@@ -47,22 +47,25 @@
 #include <sstream>
 
 #ifdef _MSC_VER
 #include <hash_set>
 #else
 #include <unordered_set>
 #endif
 
+struct IDWriteFactory;
+
 namespace mozilla {
 namespace gfx {
 
 class SourceSurfaceD2DTarget;
 class SourceSurfaceD2D;
 class GradientStopsD2D;
+class ScaledFontDWrite;
 
 struct PrivateD3D10DataD2D
 {
   RefPtr<ID3D10Effect> mEffect;
   RefPtr<ID3D10InputLayout> mInputLayout;
   RefPtr<ID3D10Buffer> mVB;
   RefPtr<ID3D10BlendState> mBlendStates[OP_COUNT];
 };
@@ -148,16 +151,17 @@ public:
   virtual void *GetNativeSurface(NativeSurfaceType aType);
 
   bool Init(const IntSize &aSize, SurfaceFormat aFormat);
   bool Init(ID3D10Texture2D *aTexture, SurfaceFormat aFormat);
   bool InitD3D10Data();
 
   static ID2D1Factory *factory();
   static TemporaryRef<ID2D1StrokeStyle> CreateStrokeStyleForOptions(const StrokeOptions &aStrokeOptions);
+  static IDWriteFactory *GetDWriteFactory();
 
   operator std::string() const {
     std::stringstream stream;
     stream << "DrawTargetD2D(" << this << ")";
     return stream.str();
   }
 private:
   friend class AutoSaveRestoreClippedOut;
@@ -182,37 +186,53 @@ private:
     }
   }
   void AddDependencyOnSource(SourceSurfaceD2DTarget* aSource);
 
   ID3D10BlendState *GetBlendStateForOperator(CompositionOp aOperator);
   ID2D1RenderTarget *GetRTForOperation(CompositionOp aOperator, const Pattern &aPattern);
   void FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aPattern, const Rect &aBounds);  void EnsureViews();
   void PopAllClips();
+  void PushClipsToRT(ID2D1RenderTarget *aRT);
+  void PopClipsFromRT(ID2D1RenderTarget *aRT);
 
-  TemporaryRef<ID2D1RenderTarget> CreateRTForTexture(ID3D10Texture2D *aTexture);
+  // This function ensures mCurrentClipMaskTexture contains a texture containing
+  // a mask corresponding with the current DrawTarget clip.
+  void EnsureClipMaskTexture();
+
+  bool FillGlyphsManual(ScaledFontDWrite *aFont,
+                        const GlyphBuffer &aBuffer,
+                        const Color &aColor,
+                        IDWriteRenderingParams *aParams,
+                        const DrawOptions &aOptions = DrawOptions());
+
+  TemporaryRef<ID2D1RenderTarget> CreateRTForTexture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat);
   TemporaryRef<ID2D1Geometry> GetClippedGeometry();
 
   TemporaryRef<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
 
   TemporaryRef<ID3D10Texture1D> CreateGradientTexture(const GradientStopsD2D *aStops);
+  TemporaryRef<ID3D10Texture2D> CreateTextureForAnalysis(IDWriteGlyphRunAnalysis *aAnalysis, const IntRect &aBounds);
 
   // This creates a partially uploaded bitmap for a SourceSurfaceD2D that is
   // too big to fit in a bitmap. It adjusts the passed Matrix to accomodate the
   // partial upload.
   TemporaryRef<ID2D1Bitmap> CreatePartialBitmapForSurface(SourceSurfaceD2D *aSurface, Matrix &aMatrix);
 
   void SetupEffectForRadialGradient(const RadialGradientPattern *aPattern);
+  void SetupStateForRendering();
 
   static const uint32_t test = 4;
 
   IntSize mSize;
 
   RefPtr<ID3D10Device1> mDevice;
   RefPtr<ID3D10Texture2D> mTexture;
+  RefPtr<ID3D10Texture2D> mCurrentClipMaskTexture;
+  RefPtr<ID2D1PathGeometry> mCurrentClippedGeometry;
   mutable RefPtr<ID2D1RenderTarget> mRT;
 
   // Temporary texture and render target used for supporting alternative operators.
   RefPtr<ID3D10Texture2D> mTempTexture;
   RefPtr<ID3D10RenderTargetView> mRTView;
   RefPtr<ID3D10ShaderResourceView> mSRView;
   RefPtr<ID2D1RenderTarget> mTempRT;
   RefPtr<ID3D10RenderTargetView> mTempRTView;
@@ -234,14 +254,15 @@ private:
   TargetSet mDependentTargets;
   // A list of targets which have this object in their mDependentTargets set
   TargetSet mDependingOnTargets;
 
   // True of the current clip stack is pushed to the main RT.
   bool mClipsArePushed;
   PrivateD3D10DataD2D *mPrivateData;
   static ID2D1Factory *mFactory;
+  static IDWriteFactory *mDWriteFactory;
 };
 
 }
 }
 
 #endif /* MOZILLA_GFX_DRAWTARGETD2D_H_ */
--- a/gfx/2d/HelpersD2D.h
+++ b/gfx/2d/HelpersD2D.h
@@ -34,18 +34,21 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef MOZILLA_GFX_HELPERSD2D_H_
 #define MOZILLA_GFX_HELPERSD2D_H_
 
 #include <d2d1.h>
+#include <dwrite.h>
 #include "2D.h"
 
+#include "ScaledFontDWrite.h"
+
 namespace mozilla {
 namespace gfx {
 
 static inline D2D1_POINT_2F D2DPoint(const Point &aPoint)
 {
   return D2D1::Point2F(aPoint.x, aPoint.y);
 }
 
@@ -207,12 +210,84 @@ struct ShaderConstantRectD3D10
   ShaderConstantRectD3D10(float aX, float aY, float aWidth, float aHeight)
     : mX(aX), mY(aY), mWidth(aWidth), mHeight(aHeight)
   { }
 
   // For easy passing to SetVertexShaderConstantF.
   operator float* () { return &mX; }
 };
 
+static DWRITE_MATRIX
+DWriteMatrixFromMatrix(Matrix &aMatrix)
+{
+  DWRITE_MATRIX mat;
+  mat.m11 = aMatrix._11;
+  mat.m12 = aMatrix._12;
+  mat.m21 = aMatrix._21;
+  mat.m22 = aMatrix._22;
+  mat.dx = aMatrix._31;
+  mat.dy = aMatrix._32;
+  return mat;
+}
+
+class AutoDWriteGlyphRun : public DWRITE_GLYPH_RUN
+{
+    static const int kNumAutoGlyphs = 256;
+
+public:
+    AutoDWriteGlyphRun() {
+        glyphCount = 0;
+    }
+
+    ~AutoDWriteGlyphRun() {
+        if (glyphCount > kNumAutoGlyphs) {
+            delete[] glyphIndices;
+            delete[] glyphAdvances;
+            delete[] glyphOffsets;
+        }
+    }
+
+    void allocate(int aNumGlyphs) {
+        glyphCount = aNumGlyphs;
+        if (aNumGlyphs <= kNumAutoGlyphs) {
+            glyphIndices = &mAutoIndices[0];
+            glyphAdvances = &mAutoAdvances[0];
+            glyphOffsets = &mAutoOffsets[0];
+        } else {
+            glyphIndices = new UINT16[aNumGlyphs];
+            glyphAdvances = new FLOAT[aNumGlyphs];
+            glyphOffsets = new DWRITE_GLYPH_OFFSET[aNumGlyphs];
+        }
+    }
+
+private:
+    DWRITE_GLYPH_OFFSET mAutoOffsets[kNumAutoGlyphs];
+    FLOAT               mAutoAdvances[kNumAutoGlyphs];
+    UINT16              mAutoIndices[kNumAutoGlyphs];
+};
+
+static void
+DWriteGlyphRunFromGlyphs(const GlyphBuffer &aGlyphs, ScaledFontDWrite *aFont, AutoDWriteGlyphRun *run)
+{
+  run->allocate(aGlyphs.mNumGlyphs);
+
+  FLOAT *advances = const_cast<FLOAT*>(run->glyphAdvances);
+  UINT16 *indices = const_cast<UINT16*>(run->glyphIndices);
+  DWRITE_GLYPH_OFFSET *offsets = const_cast<DWRITE_GLYPH_OFFSET*>(run->glyphOffsets);
+
+  memset(advances, 0, sizeof(FLOAT) * aGlyphs.mNumGlyphs);
+  for (unsigned int i = 0; i < aGlyphs.mNumGlyphs; i++) {
+    indices[i] = aGlyphs.mGlyphs[i].mIndex;
+    offsets[i].advanceOffset = aGlyphs.mGlyphs[i].mPosition.x;
+    offsets[i].ascenderOffset = -aGlyphs.mGlyphs[i].mPosition.y;
+  }
+    
+  run->bidiLevel = 0;
+  run->fontFace = aFont->mFontFace;
+  run->fontEmSize = aFont->mSize;
+  run->glyphCount = aGlyphs.mNumGlyphs;
+  run->isSideways = FALSE;
+}
+
 }
 }
 
 #endif /* MOZILLA_GFX_HELPERSD2D_H_ */
--- a/gfx/2d/ImageScalingSSE2.cpp
+++ b/gfx/2d/ImageScalingSSE2.cpp
@@ -34,16 +34,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "ImageScaling.h"
 #include "mozilla/Attributes.h"
 
 #include <xmmintrin.h>
+#include <emmintrin.h>
 
 /* The functions below use the following system for averaging 4 pixels:
  *
  * The first observation is that a half-adder is implemented as follows:
  * R = S + 2C or in the case of a and b (a ^ b) + ((a & b) << 1);
  *
  * This can be trivially extended to three pixels by observaring that when
  * doing (a ^ b ^ c) as the sum, the carry is simply the bitwise-or of the
--- a/gfx/2d/ScaledFontDWrite.h
+++ b/gfx/2d/ScaledFontDWrite.h
@@ -54,19 +54,16 @@ public:
     , mSize(aSize)
   {}
 
   virtual FontType GetType() const { return FONT_DWRITE; }
 
   virtual TemporaryRef<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
   virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder);
 
-private:
-  friend class DrawTargetD2D;
-
   void CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink);
 
   RefPtr<IDWriteFontFace> mFontFace;
   Float mSize;
 };
 
 class GlyphRenderingOptionsDWrite : public GlyphRenderingOptions
 {
--- a/gfx/2d/ShadersD2D.fx
+++ b/gfx/2d/ShadersD2D.fx
@@ -9,16 +9,17 @@
 // and <-1.0f, 1.0f> -bottom- to top. The TexCoords desc is specified in texture
 // space <0, 1.0f> left to right and top to bottom. The input vertices of the
 // shader stage always form a rectangle from {0, 0} - {1, 1}
 cbuffer cb0
 {
     float4 QuadDesc;
     float4 TexCoords;
     float4 MaskTexCoords;
+    float4 TextColor;
 }
 
 cbuffer cb1
 {
     float4 BlurOffsetsH[3];
     float4 BlurOffsetsV[3];
     float4 BlurWeights[3];
     float4 ShadowColor;
@@ -45,16 +46,22 @@ struct VS_OUTPUT
 
 struct VS_RADIAL_OUTPUT
 {
     float4 Position : SV_Position;
     float2 MaskTexCoord : TEXCOORD0;
     float2 PixelCoord : TEXCOORD1;
 };
 
+struct PS_TEXT_OUTPUT
+{
+    float4 color;
+    float4 alpha;
+};
+
 Texture2D tex;
 Texture2D mask;
 
 sampler sSampler = sampler_state {
     Filter = MIN_MAG_MIP_LINEAR;
     Texture = tex;
     AddressU = Clamp;
     AddressV = Clamp;
@@ -108,16 +115,29 @@ BlendState ShadowBlendV
   DestBlend = Inv_Src_Alpha;
   BlendOp = Add;
   SrcBlendAlpha = One;
   DestBlendAlpha = Inv_Src_Alpha;
   BlendOpAlpha = Add;
   RenderTargetWriteMask[0] = 0xF;
 };
 
+BlendState bTextBlend
+{
+  AlphaToCoverageEnable = FALSE;
+  BlendEnable[0] = TRUE;
+  SrcBlend = Src1_Color;
+  DestBlend = Inv_Src1_Color;
+  BlendOp = Add;
+  SrcBlendAlpha = Src1_Alpha;
+  DestBlendAlpha = Inv_Src1_Alpha;
+  BlendOpAlpha = Add;
+  RenderTargetWriteMask[0] = 0x0F; // All
+};
+
 VS_OUTPUT SampleTextureVS(float3 pos : POSITION)
 {
     VS_OUTPUT Output;
     Output.Position.w = 1.0f;
     Output.Position.x = pos.x * QuadDesc.z + QuadDesc.x;
     Output.Position.y = pos.y * QuadDesc.w + QuadDesc.y;
     Output.Position.z = 0;
     Output.TexCoord.x = pos.x * TexCoords.z + TexCoords.x;
@@ -273,16 +293,36 @@ float4 SampleMaskShadowVPS( VS_OUTPUT In
     outputColor += BlurWeights[1].y * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].y));
     outputColor += BlurWeights[1].z * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].z));
     outputColor += BlurWeights[1].w * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[1].w));
     outputColor += BlurWeights[2].x * tex.Sample(sShadowSampler, float2(In.TexCoord.x, In.TexCoord.y + BlurOffsetsV[2].x));
 
     return outputColor * mask.Sample(sMaskSampler, In.MaskTexCoord).a;
 };
 
+PS_TEXT_OUTPUT SampleTextTexturePS( VS_OUTPUT In) : SV_Target
+{
+    PS_TEXT_OUTPUT output;
+    output.color = TextColor;
+    output.alpha.rgba = tex.Sample(sSampler, In.TexCoord).bgrg * TextColor.a;
+    return output;
+};
+
+PS_TEXT_OUTPUT SampleTextTexturePSMasked( VS_OUTPUT In) : SV_Target
+{
+    PS_TEXT_OUTPUT output;
+    
+    float maskValue = mask.Sample(sMaskSampler, In.MaskTexCoord).a;
+
+    output.color = TextColor * maskValue;
+    output.alpha.rgba = tex.Sample(sSampler, In.TexCoord).bgrg * TextColor.a * maskValue;
+    
+    return output;
+};
+
 technique10 SampleTexture
 {
     pass P0
     {
         SetRasterizerState(TextureRast);
         SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
         SetGeometryShader(NULL);
         SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTexturePS()));
@@ -370,9 +410,28 @@ technique10 SampleTextureWithShadow
     pass P2
     {
         SetRasterizerState(TextureRast);
         SetBlendState(ShadowBlendV, float4(1.0f, 1.0f, 1.0f, 1.0f), 0xffffffff);
         SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
         SetGeometryShader(NULL);
         SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleMaskShadowVPS()));
     }
- }
+}
+
+technique10 SampleTextTexture
+{
+    pass Unmasked
+    {
+        SetBlendState(bTextBlend, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
+        SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+        SetGeometryShader(NULL);
+        SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTextTexturePS()));
+    }
+    pass Masked
+    {
+        SetBlendState(bTextBlend, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
+        SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS()));
+        SetGeometryShader(NULL);
+        SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTextTexturePSMasked()));
+    }
+}
+
--- a/gfx/2d/Tools.h
+++ b/gfx/2d/Tools.h
@@ -34,16 +34,19 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef MOZILLA_GFX_TOOLS_H_
 #define MOZILLA_GFX_TOOLS_H_
 
 #include "Types.h"
+#if defined(_MSC_VER) && (_MSC_VER < 1600)
+#define hypotf _hypotf
+#endif
 
 namespace mozilla {
 namespace gfx {
 
 static inline bool
 IsOperatorBoundByMask(CompositionOp aOp) {
   switch (aOp) {
   case OP_IN:
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -124,18 +124,16 @@ public:
 
 #else
 
 #error "Platform not recognized"
 
 #endif
 
 #include "mozilla/Preferences.h"
-#include "nsIScreen.h"
-#include "nsIScreenManager.h"
 #include "gfxUtils.h"
 #include "gfxFailure.h"
 #include "gfxASurface.h"
 #include "gfxImageSurface.h"
 #include "gfxPlatform.h"
 #include "GLContextProvider.h"
 #include "GLLibraryEGL.h"
 #include "nsDebug.h"
@@ -1396,36 +1394,25 @@ CreateConfig(EGLConfig* aConfig, PRInt32
         {
             *aConfig = config;
             return true;
         }
     }
     return false;
 }
 
-static int
-GetScreenDepth()
-{
-    nsCOMPtr<nsIScreenManager> screenMgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
-    nsCOMPtr<nsIScreen> screen;
-    screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
-    PRInt32 depth = 24;
-    screen->GetColorDepth(&depth);
-    return depth;
-}
-
 // Return true if a suitable EGLConfig was found and pass it out
 // through aConfig.  Return false otherwise.
 //
 // NB: It's entirely legal for the returned EGLConfig to be valid yet
 // have the value null.
 static bool
 CreateConfig(EGLConfig* aConfig)
 {
-    PRInt32 depth = GetScreenDepth();
+    PRInt32 depth = gfxPlatform::GetPlatform()->GetScreenDepth();
     if (!CreateConfig(aConfig, depth)) {
 #ifdef MOZ_WIDGET_ANDROID
         // Bug 736005
         // Android doesn't always support 16 bit so also try 24 bit
         if (depth == 16) {
             return CreateConfig(aConfig, 24);
         }
 #endif
@@ -1487,17 +1474,17 @@ GLContextProviderEGL::CreateForWindow(ns
 #if defined(XP_WIN) || defined(ANDROID) || defined(MOZ_PLATFORM_MAEMO)
     bool doubleBuffered = true;
 #else
     bool doubleBuffered = false;
 #endif
 
     void* currentContext = sEGLLibrary.fGetCurrentContext();
     if (aWidget->HasGLContext() && currentContext) {
-        PRInt32 depth = GetScreenDepth();
+        PRInt32 depth = gfxPlatform::GetPlatform()->GetScreenDepth();
         void* platformContext = currentContext;
 #ifdef MOZ_WIDGET_QT
         QGLContext* context = const_cast<QGLContext*>(QGLContext::currentContext());
         if (context && context->device()) {
             depth = context->device()->depth();
         }
         doubleBuffered = context->format().doubleBuffer();
         platformContext = context;
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -594,31 +594,43 @@ IntersectWithClip(const nsIntRegion& aRe
   result.And(aRegion, r);
   return result;
 }
 
 static void
 SetAntialiasingFlags(Layer* aLayer, gfxContext* aTarget)
 {
   if (!aTarget->IsCairo()) {
-    // Azure targets don't contain antialiasing flags at this point.
-    return;
+    RefPtr<DrawTarget> dt = aTarget->GetDrawTarget();
+
+    if (dt->GetFormat() != FORMAT_B8G8R8A8) {
+      return;
+    }
+
+    const nsIntRect& bounds = aLayer->GetVisibleRegion().GetBounds();
+    Rect transformedBounds = dt->GetTransform().TransformBounds(Rect(Float(bounds.x), Float(bounds.y),
+                                                                     Float(bounds.width), Float(bounds.height)));
+    transformedBounds.RoundOut();
+    IntRect intTransformedBounds;
+    transformedBounds.ToIntRect(&intTransformedBounds);
+    dt->SetPermitSubpixelAA(!(aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) ||
+                            dt->GetOpaqueRect().Contains(intTransformedBounds));
+  } else {
+    nsRefPtr<gfxASurface> surface = aTarget->CurrentSurface();
+    if (surface->GetContentType() != gfxASurface::CONTENT_COLOR_ALPHA) {
+      // Destination doesn't have alpha channel; no need to set any special flags
+      return;
+    }
+
+    const nsIntRect& bounds = aLayer->GetVisibleRegion().GetBounds();
+    surface->SetSubpixelAntialiasingEnabled(
+        !(aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) ||
+        surface->GetOpaqueRect().Contains(
+          aTarget->UserToDevice(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height))));
   }
-
-  nsRefPtr<gfxASurface> surface = aTarget->CurrentSurface();
-  if (surface->GetContentType() != gfxASurface::CONTENT_COLOR_ALPHA) {
-    // Destination doesn't have alpha channel; no need to set any special flags
-    return;
-  }
-
-  const nsIntRect& bounds = aLayer->GetVisibleRegion().GetBounds();
-  surface->SetSubpixelAntialiasingEnabled(
-      !(aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) ||
-      surface->GetOpaqueRect().Contains(
-        aTarget->UserToDevice(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height))));
 }
 
 already_AddRefed<gfxContext>
 BasicLayerManager::PushGroupForLayer(gfxContext* aContext, Layer* aLayer,
                                      const nsIntRegion& aRegion,
                                      bool* aNeedsClipToVisibleRegion)
 {
   // If we need to call PushGroup, we should clip to the smallest possible
--- a/gfx/layers/d3d10/ThebesLayerD3D10.cpp
+++ b/gfx/layers/d3d10/ThebesLayerD3D10.cpp
@@ -479,16 +479,18 @@ ThebesLayerD3D10::DrawRegion(nsIntRegion
   if (!mDrawTarget && aMode == SURFACE_SINGLE_CHANNEL_ALPHA) {
     context->SetOperator(gfxContext::OPERATOR_CLEAR);
     context->Paint();
     context->SetOperator(gfxContext::OPERATOR_OVER);
   }
 
   if (mD2DSurface) {
     mD2DSurface->SetSubpixelAntialiasingEnabled(!(mContentFlags & CONTENT_COMPONENT_ALPHA));
+  } else if (mDrawTarget) {
+    mDrawTarget->SetPermitSubpixelAA(!(mContentFlags & CONTENT_COMPONENT_ALPHA));
   }
 
   LayerManagerD3D10::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo();
   cbInfo.Callback(this, context, aRegion, nsIntRegion(), cbInfo.CallbackData);
 }
 
 void
 ThebesLayerD3D10::CreateNewTextures(const gfxIntSize &aSize, SurfaceMode aMode)
--- a/gfx/layers/opengl/ImageLayerOGL.cpp
+++ b/gfx/layers/opengl/ImageLayerOGL.cpp
@@ -877,13 +877,16 @@ ShadowImageLayerOGL::LoadAsTexture(GLuin
 
   *aSize = mTexImage->GetSize();
   return true;
 }
 
 void
 ShadowImageLayerOGL::CleanupResources()
 {
+  mYUVTexture[0].Release();
+  mYUVTexture[1].Release();
+  mYUVTexture[2].Release();
   mTexImage = nsnull;
 }
 
 } /* layers */
 } /* mozilla */
--- a/gfx/layers/opengl/ImageLayerOGL.h
+++ b/gfx/layers/opengl/ImageLayerOGL.h
@@ -77,18 +77,18 @@ public:
    * has a texture, it is released. This can be called on any thread.
    */
   void TakeFrom(GLTexture *aOther);
 
   bool IsAllocated() { return mTexture != 0; }
   GLuint GetTextureID() { return mTexture; }
   GLContext *GetGLContext() { return mContext; }
 
+  void Release();
 private:
-  void Release();
 
   nsRefPtr<GLContext> mContext;
   GLuint mTexture;
 };
 
 /**
  * A RecycleBin is owned by an ImageLayer. We store textures in it that we
  * want to recycle from one image to the next. It's a separate object from 
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -33,17 +33,17 @@
  * 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 ***** */
 
 #include "nsIMemoryReporter.h"
 #include "nsMemory.h"
-#include "CheckedInt.h"
+#include "mozilla/CheckedInt.h"
 
 #include "gfxASurface.h"
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
 
 #include "nsRect.h"
 
 #include "cairo.h"
@@ -373,25 +373,25 @@ gfxASurface::CheckSurfaceSize(const gfxI
         NS_WARNING("Surface size too large (exceeds CoreGraphics limit)!");
         return false;
     }
 #endif
 
     // make sure the surface area doesn't overflow a PRInt32
     CheckedInt<PRInt32> tmp = sz.width;
     tmp *= sz.height;
-    if (!tmp.valid()) {
+    if (!tmp.isValid()) {
         NS_WARNING("Surface size too large (would overflow)!");
         return false;
     }
 
     // assuming 4-byte stride, make sure the allocation size
     // doesn't overflow a PRInt32 either
     tmp *= 4;
-    if (!tmp.valid()) {
+    if (!tmp.isValid()) {
         NS_WARNING("Allocation too large (would overflow)!");
         return false;
     }
 
     return true;
 }
 
 /* static */
--- a/gfx/thebes/gfxAndroidPlatform.cpp
+++ b/gfx/thebes/gfxAndroidPlatform.cpp
@@ -58,21 +58,22 @@ static FT_Library gPlatformFTLibrary = N
 
 gfxAndroidPlatform::gfxAndroidPlatform()
 {
     FT_Init_FreeType(&gPlatformFTLibrary);
 
     nsCOMPtr<nsIScreenManager> screenMgr = do_GetService("@mozilla.org/gfx/screenmanager;1");
     nsCOMPtr<nsIScreen> screen;
     screenMgr->GetPrimaryScreen(getter_AddRefs(screen));
-    PRInt32 depth = 24;
-    screen->GetColorDepth(&depth);
+    mScreenDepth = 24;
+    screen->GetColorDepth(&mScreenDepth);
 
-    mOffscreenFormat = depth == 16 ? gfxASurface::ImageFormatRGB16_565 :
-                                     gfxASurface::ImageFormatARGB32;
+    mOffscreenFormat = mScreenDepth == 16
+                       ? gfxASurface::ImageFormatRGB16_565
+                       : gfxASurface::ImageFormatARGB32;
 }
 
 gfxAndroidPlatform::~gfxAndroidPlatform()
 {
     cairo_debug_reset_static_data();
 
     FT_Done_FreeType(gPlatformFTLibrary);
     gPlatformFTLibrary = NULL;
@@ -227,8 +228,14 @@ gfxAndroidPlatform::FontHintingEnabled()
     // XXX when we use content processes to load "apps", we'll want to
     // configure this dynamically based on whether we're an "app
     // content process" or a "browser content process".  The former
     // wants hinting, the latter doesn't since it might be
     // non-reflow-zoomed.
     return (XRE_GetProcessType() != GeckoProcessType_Content);
 #endif //  MOZ_USING_ANDROID_JAVA_WIDGETS
 }
+
+int
+gfxAndroidPlatform::GetScreenDepth() const
+{
+    return mScreenDepth;
+}
--- a/gfx/thebes/gfxAndroidPlatform.h
+++ b/gfx/thebes/gfxAndroidPlatform.h
@@ -98,14 +98,17 @@ public:
     virtual gfxFontGroup *CreateFontGroup(const nsAString &aFamilies,
                                           const gfxFontStyle *aStyle,
                                           gfxUserFontSet* aUserFontSet);
 
     virtual bool FontHintingEnabled() MOZ_OVERRIDE;
 
     FT_Library GetFTLibrary();
 
+    virtual int GetScreenDepth() const;
+
 private:
+    int mScreenDepth;
     gfxImageFormat mOffscreenFormat;
 };
 
 #endif /* GFX_PLATFORM_ANDROID_H */
 
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -925,29 +925,39 @@ private:
 };
 
 
 class THEBES_API gfxContextAutoDisableSubpixelAntialiasing {
 public:
     gfxContextAutoDisableSubpixelAntialiasing(gfxContext *aContext, bool aDisable)
     {
         if (aDisable) {
-            mSurface = aContext->CurrentSurface();
-            if (!mSurface) {
-              return;
+            if (aContext->IsCairo()) {
+                mSurface = aContext->CurrentSurface();
+                if (!mSurface) {
+                  return;
+                }
+                mSubpixelAntialiasingEnabled = mSurface->GetSubpixelAntialiasingEnabled();
+                mSurface->SetSubpixelAntialiasingEnabled(false);
+            } else {
+                mDT = aContext->GetDrawTarget();
+
+                mSubpixelAntialiasingEnabled = mDT->GetPermitSubpixelAA();
+                mDT->SetPermitSubpixelAA(false);
             }
-            mSubpixelAntialiasingEnabled = mSurface->GetSubpixelAntialiasingEnabled();
-            mSurface->SetSubpixelAntialiasingEnabled(false);
         }
     }
     ~gfxContextAutoDisableSubpixelAntialiasing()
     {
         if (mSurface) {
             mSurface->SetSubpixelAntialiasingEnabled(mSubpixelAntialiasingEnabled);
+        } else if (mDT) {
+            mDT->SetPermitSubpixelAA(mSubpixelAntialiasingEnabled);
         }
     }
 
 private:
     nsRefPtr<gfxASurface> mSurface;
+    mozilla::RefPtr<mozilla::gfx::DrawTarget> mDT;
     bool mSubpixelAntialiasingEnabled;
 };
 
 #endif /* GFX_CONTEXT_H */
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -81,16 +81,17 @@
 
 #include "plstr.h"
 #include "nsCRT.h"
 #include "GLContext.h"
 #include "GLContextProvider.h"
 
 #include "mozilla/FunctionTimer.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/Assertions.h"
 
 #include "nsIGfxInfo.h"
 
 using namespace mozilla;
 
 gfxPlatform *gPlatform = nsnull;
 static bool gEverInitialized = false;
 
@@ -1432,8 +1433,15 @@ gfxPlatform::GetLog(eGfxLog aWhichLog)
         break;
     }
 
     return nsnull;
 #else
     return nsnull;
 #endif
 }
+
+int
+gfxPlatform::GetScreenDepth() const
+{
+    MOZ_ASSERT(false, "Not implemented on this platform");
+    return 0;
+}
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -452,16 +452,18 @@ public:
 
     /**
      * Returns a logger if one is available and logging is enabled
      */
     static PRLogModuleInfo* GetLog(eGfxLog aWhichLog);
 
     bool WorkAroundDriverBugs() const { return mWorkAroundDriverBugs; }
 
+    virtual int GetScreenDepth() const;
+
 protected:
     gfxPlatform();
     virtual ~gfxPlatform();
 
     void AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], PRUint32 &aLen, 
                             eFontPrefLang aCharLang, eFontPrefLang aPageLang);
                                                
     PRInt8  mAllowDownloadableFonts;
--- a/image/test/reftest/ImageDocument.css
+++ b/image/test/reftest/ImageDocument.css
@@ -6,9 +6,11 @@ body {
 body > :first-child {
   display: block;
   position: absolute;
   margin: auto;
   top: 0;
   right: 0;
   bottom: 0;
   left: 0;
+  background-color: #fff;
+  color: #222;
 }
--- a/image/test/reftest/encoders-lossless/ImageDocument.css
+++ b/image/test/reftest/encoders-lossless/ImageDocument.css
@@ -6,9 +6,11 @@ body {
 img {
   display: block;
   position: absolute;
   margin: auto;
   top: 0;
   right: 0;
   bottom: 0;
   left: 0;
+  background-color: #fff;
+  color: #222;
 }
--- a/image/test/reftest/gif/ImageDocument.css
+++ b/image/test/reftest/gif/ImageDocument.css
@@ -6,9 +6,11 @@ body {
 body > :first-child {
   display: block;
   position: absolute;
   margin: auto;
   top: 0;
   right: 0;
   bottom: 0;
   left: 0;
+  background-color: #fff;
+  color: #222;
 }
--- a/image/test/reftest/icon/win/ImageDocument.css
+++ b/image/test/reftest/icon/win/ImageDocument.css
@@ -6,9 +6,11 @@ body {
 body > :first-child {
   display: block;
   position: absolute;
   margin: auto;
   top: 0;
   right: 0;
   bottom: 0;
   left: 0;
+  background-color: #fff;
+  color: #222;
 }
--- a/image/test/reftest/icon/win/bug415761.sjs
+++ b/image/test/reftest/icon/win/bug415761.sjs
@@ -12,10 +12,10 @@ function handleRequest(request, response
   src.copyTo(null, dest.leafName);
   var uri = Components.classes["@mozilla.org/network/io-service;1"]
                       .getService(Components.interfaces.nsIIOService)
                       .newFileURI(dest).spec;
   response.setStatusLine(request.httpVersion, 200, "OK");
   response.setHeader("Content-Type", "text/html");
   response.setHeader("Cache-Control", "no-cache");
   response.write("<link rel=\"stylesheet\" href=\"ImageDocument.css\">");
-  response.write("<img src=\"moz-icon:" + uri + "\">");
+  response.write("<img src=\"moz-icon:" + uri + "\" width=\"32\" height=\"32\">");
 }