Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Thu, 02 Feb 2012 15:06:21 -0800
changeset 105709 43b55878da4625751bc635fc425816e95583cf2f
parent 105708 c3797142f9281d11a5a56f6549862ed7eba08dce (current diff)
parent 86089 e777c939a3f957f1f3c42dfc2a4bfe784d046fbf (diff)
child 105710 e60822f61ea7e80e9140a15d302e3f23b0c36972
push id14706
push usereakhgari@mozilla.com
push dateTue, 11 Sep 2012 20:39:52 +0000
treeherdermozilla-inbound@d50bf1edaabe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone13.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/base/nsARIAMap.cpp
accessible/src/base/nsARIAMap.h
accessible/src/base/nsAccessible.cpp
accessible/tests/mochitest/events/Makefile.in
b2g/chrome/jar.mn
b2g/confvars.sh
browser/app/profile/firefox.js
browser/base/content/browser.js
browser/components/sessionstore/test/Makefile.in
browser/components/shell/src/nsGNOMEShellService.cpp
browser/components/shell/src/nsWindowsShellService.cpp
build/mobile/devicemanagerADB.py
config/autoconf.mk.in
config/system-headers
configure.in
content/base/public/nsIDocument.h
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsFrameMessageManager.cpp
content/base/src/nsWebSocket.cpp
content/base/src/nsWebSocket.h
content/canvas/test/test_canvas.html
content/html/content/src/nsHTMLCanvasElement.cpp
content/html/content/src/nsHTMLMediaElement.cpp
content/media/nsAudioStream.cpp
content/smil/nsSMILTimedElement.cpp
content/smil/nsSMILTimedElement.h
content/svg/content/src/nsSVGFilters.cpp
docshell/shistory/src/nsSHEntry.cpp
dom/base/nsDOMWindowUtils.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsJSEnvironment.cpp
dom/base/nsJSEnvironment.h
dom/ipc/ContentChild.cpp
dom/plugins/base/android/ANPBase.h
dom/plugins/base/nsNPAPIPlugin.cpp
dom/plugins/base/nsNPAPIPluginInstance.h
dom/plugins/base/nsPluginInstanceOwner.cpp
dom/plugins/base/nsPluginInstanceOwner.h
dom/plugins/ipc/PluginInstanceParent.cpp
dom/workers/RuntimeService.cpp
dom/workers/WorkerPrivate.cpp
embedding/android/AndroidManifest.xml.in
embedding/android/GeckoAppShell.java
embedding/android/Makefile.in
gfx/gl/GLContext.cpp
gfx/layers/ImageLayers.h
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/basic/BasicImages.cpp
gfx/layers/basic/BasicLayers.cpp
gfx/layers/basic/BasicLayers.h
gfx/layers/d3d10/ImageLayerD3D10.cpp
gfx/layers/d3d10/ImageLayerD3D10.h
gfx/layers/d3d10/LayerManagerD3D10.cpp
gfx/layers/d3d10/LayerManagerD3D10.h
gfx/layers/d3d9/ImageLayerD3D9.cpp
gfx/layers/d3d9/ImageLayerD3D9.h
gfx/layers/d3d9/LayerManagerD3D9.cpp
gfx/layers/d3d9/LayerManagerD3D9.h
gfx/layers/opengl/ImageLayerOGL.cpp
gfx/layers/opengl/ImageLayerOGL.h
gfx/layers/opengl/LayerManagerOGL.cpp
gfx/layers/opengl/LayerManagerOGL.h
gfx/layers/opengl/MacIOSurfaceImageOGL.h
gfx/layers/opengl/MacIOSurfaceImageOGL.mm
gfx/thebes/gfxASurface.h
gfx/thebes/gfxBlur.cpp
gfx/thebes/nsCoreAnimationSupport.mm
image/public/imgIContainer.idl
image/src/RasterImage.cpp
image/src/RasterImage.h
image/src/VectorImage.h
js/src/config/system-headers
js/src/configure.in
js/src/frontend/BytecodeEmitter.cpp
js/src/ion/Ion.cpp
js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js
js/src/jsanalyze.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jsdbgapi.h
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsgc.cpp
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsopcode.cpp
js/src/jsopcode.h
js/src/jsopcode.tbl
js/src/jsscript.cpp
js/src/jswrapper.cpp
js/src/jswrapper.h
js/src/jsxdrapi.h
js/src/jsxml.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/InvokeHelpers.cpp
js/src/shell/js.cpp
js/src/vm/Debugger.cpp
js/src/vm/String.cpp
js/xpconnect/loader/mozJSComponentLoader.cpp
js/xpconnect/src/XPCConvert.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/XPCQuickStubs.cpp
js/xpconnect/src/XPCWrapper.cpp
js/xpconnect/src/XPCWrapper.h
js/xpconnect/src/xpcpublic.h
js/xpconnect/wrappers/AccessCheck.cpp
js/xpconnect/wrappers/WrapperFactory.h
layout/base/FrameLayerBuilder.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsPresContext.h
layout/base/nsPresShell.cpp
layout/build/Makefile.in
layout/generic/nsImageFrame.cpp
layout/generic/nsImageFrame.h
layout/generic/nsObjectFrame.cpp
layout/generic/nsObjectFrame.h
layout/generic/nsVideoFrame.cpp
layout/reftests/svg/reftest.list
layout/style/AnimationCommon.cpp
layout/style/Declaration.cpp
layout/style/Declaration.h
layout/style/GroupRule.h
layout/style/StyleRule.cpp
layout/style/StyleRule.h
layout/style/nsAnimationManager.cpp
layout/style/nsCSSDataBlock.cpp
layout/style/nsCSSDataBlock.h
layout/style/nsCSSRules.cpp
layout/style/nsCSSRules.h
layout/style/nsCSSStyleSheet.cpp
layout/style/nsCSSStyleSheet.h
layout/style/nsCSSValue.cpp
layout/style/nsCSSValue.h
layout/style/nsIStyleSheet.h
layout/style/nsLayoutStylesheetCache.cpp
layout/style/nsLayoutStylesheetCache.h
layout/svg/base/src/nsSVGForeignObjectFrame.cpp
layout/svg/base/src/nsSVGOuterSVGFrame.cpp
layout/svg/base/src/nsSVGUseFrame.cpp
mobile/android/base/AndroidManifest.xml.in
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/Makefile.in
mobile/android/base/resources/drawable-hdpi-v8/abouthome_icon.png
mobile/android/base/resources/drawable-hdpi-v8/abouthome_logo.png
mobile/android/base/resources/drawable-hdpi-v8/abouthome_separator.9.png
mobile/android/base/resources/drawable-hdpi-v8/abouthome_sync_bg.9.png
mobile/android/base/resources/drawable-hdpi-v8/abouthome_sync_logo.png
mobile/android/base/resources/drawable-hdpi-v8/abouthome_sync_pressed_bg.9.png
mobile/android/base/resources/drawable-hdpi-v8/abouthome_topsite_placeholder.png
mobile/android/base/resources/drawable-hdpi-v8/abouthome_topsite_shadow.9.png
mobile/android/base/resources/drawable-hdpi-v8/address_bar_texture.png
mobile/android/base/resources/drawable-hdpi-v8/address_bar_url_bg.9.png
mobile/android/base/resources/drawable-hdpi-v8/address_bar_url_outline.9.png
mobile/android/base/resources/drawable-hdpi-v8/awesomebar_tab.9.png
mobile/android/base/resources/drawable-hdpi-v8/awesomebar_tab_pressed.9.png
mobile/android/base/resources/drawable-hdpi-v8/doorhanger_arrow.png
mobile/android/base/resources/drawable-hdpi-v8/doorhanger_bg.9.png
mobile/android/base/resources/drawable-hdpi-v8/doorhanger_popup_bg.9.png
mobile/android/base/resources/drawable-hdpi-v8/doorhanger_shadow_bg.9.png
mobile/android/base/resources/drawable-hdpi-v8/favicon.png
mobile/android/base/resources/drawable-hdpi-v8/home_bg.png
mobile/android/base/resources/drawable-hdpi-v8/home_star.png
mobile/android/base/resources/drawable-hdpi-v8/ic_addons_empty.png
mobile/android/base/resources/drawable-hdpi-v8/ic_awesomebar_go.png
mobile/android/base/resources/drawable-hdpi-v8/ic_awesomebar_search.png
mobile/android/base/resources/drawable-hdpi-v8/ic_menu_bookmark_add.png
mobile/android/base/resources/drawable-hdpi-v8/ic_menu_bookmark_remove.png
mobile/android/base/resources/drawable-hdpi-v8/ic_menu_find_in_page.png
mobile/android/base/resources/drawable-hdpi-v8/ic_menu_forward.png
mobile/android/base/resources/drawable-hdpi-v8/ic_menu_reload.png
mobile/android/base/resources/drawable-hdpi-v8/ic_menu_save_as_pdf.png
mobile/android/base/resources/drawable-hdpi-v8/ic_menu_share.png
mobile/android/base/resources/drawable-hdpi-v8/site_security_identified.png
mobile/android/base/resources/drawable-hdpi-v8/site_security_verified.png
mobile/android/base/resources/drawable-hdpi-v8/tab_close.png
mobile/android/base/resources/drawable-hdpi-v8/tab_new.png
mobile/android/base/resources/drawable-hdpi-v8/tab_selected.png
mobile/android/base/resources/drawable-hdpi-v8/tab_thumbnail_default.png
mobile/android/base/resources/drawable-hdpi-v8/tab_thumbnail_shadow.png
mobile/android/base/resources/drawable-hdpi-v8/tabs_more.png
mobile/android/base/resources/drawable-hdpi-v8/tabs_normal.png
mobile/android/base/resources/drawable-hdpi-v8/tabs_plus.png
mobile/android/base/resources/drawable-hdpi-v8/tabs_pressed.png
mobile/android/base/resources/drawable-hdpi-v8/urlbar_stop.png
mobile/android/base/resources/drawable-mdpi-v8/abouthome_icon.png
mobile/android/base/resources/drawable-mdpi-v8/abouthome_logo.png
mobile/android/base/resources/drawable-mdpi-v8/abouthome_separator.9.png
mobile/android/base/resources/drawable-mdpi-v8/abouthome_sync_bg.9.png
mobile/android/base/resources/drawable-mdpi-v8/abouthome_sync_logo.png
mobile/android/base/resources/drawable-mdpi-v8/abouthome_sync_pressed_bg.9.png
mobile/android/base/resources/drawable-mdpi-v8/abouthome_topsite_placeholder.png
mobile/android/base/resources/drawable-mdpi-v8/abouthome_topsite_shadow.9.png
mobile/android/base/resources/drawable-mdpi-v8/address_bar_texture.png
mobile/android/base/resources/drawable-mdpi-v8/address_bar_url_bg.9.png
mobile/android/base/resources/drawable-mdpi-v8/address_bar_url_outline.9.png
mobile/android/base/resources/drawable-mdpi-v8/awesomebar_tab.9.png
mobile/android/base/resources/drawable-mdpi-v8/awesomebar_tab_pressed.9.png
mobile/android/base/resources/drawable-mdpi-v8/doorhanger_arrow.png
mobile/android/base/resources/drawable-mdpi-v8/doorhanger_bg.9.png
mobile/android/base/resources/drawable-mdpi-v8/doorhanger_popup_bg.9.png
mobile/android/base/resources/drawable-mdpi-v8/doorhanger_shadow_bg.9.png
mobile/android/base/resources/drawable-mdpi-v8/favicon.png
mobile/android/base/resources/drawable-mdpi-v8/ic_addons_empty.png
mobile/android/base/resources/drawable-mdpi-v8/ic_awesomebar_go.png
mobile/android/base/resources/drawable-mdpi-v8/ic_awesomebar_search.png
mobile/android/base/resources/drawable-mdpi-v8/ic_menu_bookmark_add.png
mobile/android/base/resources/drawable-mdpi-v8/ic_menu_bookmark_remove.png
mobile/android/base/resources/drawable-mdpi-v8/ic_menu_find_in_page.png
mobile/android/base/resources/drawable-mdpi-v8/ic_menu_forward.png
mobile/android/base/resources/drawable-mdpi-v8/ic_menu_reload.png
mobile/android/base/resources/drawable-mdpi-v8/ic_menu_save_as_pdf.png
mobile/android/base/resources/drawable-mdpi-v8/ic_menu_share.png
mobile/android/base/resources/drawable-mdpi-v8/site_security_identified.png
mobile/android/base/resources/drawable-mdpi-v8/site_security_verified.png
mobile/android/base/resources/drawable-mdpi-v8/tab_close.png
mobile/android/base/resources/drawable-mdpi-v8/tab_new.png
mobile/android/base/resources/drawable-mdpi-v8/tab_selected.png
mobile/android/base/resources/drawable-mdpi-v8/tab_thumbnail_default.png
mobile/android/base/resources/drawable-mdpi-v8/tab_thumbnail_shadow.png
mobile/android/base/resources/drawable-mdpi-v8/tabs_more.png
mobile/android/base/resources/drawable-mdpi-v8/tabs_normal.png
mobile/android/base/resources/drawable-mdpi-v8/tabs_plus.png
mobile/android/base/resources/drawable-mdpi-v8/tabs_pressed.png
mobile/android/base/resources/drawable-mdpi-v8/urlbar_stop.png
mobile/xul/app/mobile.js
netwerk/protocol/websocket/WebSocketChannel.cpp
netwerk/protocol/websocket/WebSocketChannel.h
toolkit/components/places/History.cpp
toolkit/components/places/tests/cpp/places_test_harness.h
toolkit/components/telemetry/TelemetryHistograms.h
toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
toolkit/components/url-classifier/nsUrlClassifierDBService.h
toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h
toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp
toolkit/content/license.html
toolkit/content/tests/chrome/test_bug649840.xul
toolkit/content/widgets/popup.xml
toolkit/mozapps/extensions/content/extensions.js
view/src/nsView.cpp
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
widget/android/nsWindow.cpp
widget/windows/nsNativeDragTarget.cpp
widget/windows/nsNativeDragTarget.h
widget/xpwidgets/nsBaseWidget.cpp
xpcom/glue/nsCOMArray.h
xpcom/glue/nsVoidArray.cpp
xpcom/glue/nsVoidArray.h
xpcom/string/public/nsStringBuffer.h
xpcom/string/src/nsSubstring.cpp
--- a/accessible/src/base/nsARIAMap.cpp
+++ b/accessible/src/base/nsARIAMap.cpp
@@ -100,17 +100,17 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] 
     eNoLiveAttr,
     states::READONLY
   },
   {
     "button",
     roles::PUSHBUTTON,
     kUseMapRole,
     eNoValue,
-    eClickAction,
+    ePressAction,
     eNoLiveAttr,
     kNoReqStates,
     eARIAPressed
   },
   {
     "checkbox",
     roles::CHECKBUTTON,
     kUseMapRole,
@@ -215,25 +215,16 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] 
     roles::GRAPHIC,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     kNoReqStates
   },
   {
-    "label",
-    roles::LABEL,
-    kUseMapRole,
-    eNoValue,
-    eNoAction,
-    eNoLiveAttr,
-    kNoReqStates
-  },
-  {
     "link",
     roles::LINK,
     kUseMapRole,
     eNoValue,
     eJumpAction,
     eNoLiveAttr,
     states::LINKED
   },
--- a/accessible/src/base/nsARIAMap.h
+++ b/accessible/src/base/nsARIAMap.h
@@ -73,16 +73,17 @@ enum EValueRule
 /**
  * Used to define if the role requires to expose action.
  */
 enum EActionRule
 {
   eNoAction,
   eActivateAction,
   eClickAction,
+  ePressAction,
   eCheckUncheckAction,
   eExpandAction,
   eJumpAction,
   eOpenCloseAction,
   eSelectAction,
   eSortAction,
   eSwitchAction
 };
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -1823,16 +1823,20 @@ nsAccessible::GetActionName(PRUint8 aInd
    case eActivateAction:
      aName.AssignLiteral("activate");
      return NS_OK;
 
    case eClickAction:
      aName.AssignLiteral("click");
      return NS_OK;
 
+   case ePressAction:
+     aName.AssignLiteral("press");
+     return NS_OK;
+
    case eCheckUncheckAction:
      if (states & states::CHECKED)
        aName.AssignLiteral("uncheck");
      else if (states & states::MIXED)
        aName.AssignLiteral("cycle");
      else
        aName.AssignLiteral("check");
      return NS_OK;
--- a/accessible/tests/mochitest/actions/test_anchors.html
+++ b/accessible/tests/mochitest/actions/test_anchors.html
@@ -44,16 +44,25 @@
           ID: "anchor1",
           actionName: "jump",
           actionIndex: 0,
           events: CLICK_EVENTS,
           eventSeq: [
             new scrollingChecker(getAccessible("bottom1"))
           ]
         },
+        { // jump again (test for bug 437607)
+          ID: "anchor1",
+          actionName: "jump",
+          actionIndex: 0,
+          events: CLICK_EVENTS,
+          eventSeq: [
+            new scrollingChecker(getAccessible("bottom1"))
+          ]
+        },
         {
           ID: "anchor2",
           actionName: "jump",
           actionIndex: 0,
           events: CLICK_EVENTS,
           eventSeq: [
             new scrollingChecker(getAccessible("bottom2"))
           ]
--- a/accessible/tests/mochitest/actions/test_aria.html
+++ b/accessible/tests/mochitest/actions/test_aria.html
@@ -22,17 +22,17 @@
       var actionsArray = [
         {
           ID: "clickable",
           actionName: "click",
           events: CLICK_EVENTS
         },
         {
           ID: "button",
-          actionName: "click",
+          actionName: "press",
           events: CLICK_EVENTS
         },
         {
           ID: "checkbox_unchecked",
           actionName: "check",
           events: CLICK_EVENTS
         },
         {
--- a/accessible/tests/mochitest/browser.js
+++ b/accessible/tests/mochitest/browser.js
@@ -45,16 +45,32 @@ function currentBrowser()
  * Return DOM document of the current tab.
  */
 function currentTabDocument()
 {
   return currentBrowser().contentDocument;
 }
 
 /**
+ * Return browser element of the tab at the given index.
+ */
+function browserAt(aIndex)
+{
+  return tabBrowser().getBrowserAtIndex(aIndex);
+}
+
+/**
+ * Return DOM document of the tab at the given index.
+ */
+function tabDocumentAt(aIndex)
+{
+  return browserAt(aIndex).contentDocument;
+}
+
+/**
  * Return input element of address bar.
  */
 function urlbarInput()
 {
   return browserWindow().document.getElementById("urlbar").inputField;
 }
 
 /**
--- a/accessible/tests/mochitest/events/Makefile.in
+++ b/accessible/tests/mochitest/events/Makefile.in
@@ -77,16 +77,17 @@ include $(topsrcdir)/config/rules.mk
 		test_focus_menu.xul \
 		test_focus_name.html \
 		test_focus_selects.html \
 		test_focus_tabbox.xul \
 		test_focus_tree.xul \
 		test_menu.xul \
 		test_mutation.html \
 		test_mutation.xhtml \
+		test_scroll.xul \
 		test_selection_aria.html \
 		test_selection.html \
 		test_selection.xul \
 		test_statechange.html \
 		test_text_alg.html \
 		test_text.html \
 		test_textattrchange.html \
 		test_tree.xul \
--- a/accessible/tests/mochitest/events/test_scroll.xul
+++ b/accessible/tests/mochitest/events/test_scroll.xul
@@ -1,125 +1,97 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 
-<!-- Firefox tabbrowser -->
-<?xml-stylesheet href="chrome://browser/content/browser.css"
-                 type="text/css"?>
-<!-- SeaMonkey tabbrowser -->
-<?xml-stylesheet href="chrome://navigator/content/navigator.css"
-                 type="text/css"?>
-
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
                  type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
   <script type="application/javascript"
-          src="chrome://browser/content/utilityOverlay.js"/>
+          src="chrome://mochikit/content/chrome-harness.js"/>
 
   <script type="application/javascript"
           src="../common.js" />
   <script type="application/javascript"
           src="../role.js" />
   <script type="application/javascript"
           src="../states.js" />
   <script type="application/javascript"
           src="../events.js" />
-
   <script type="application/javascript"
-          src="chrome://mochikit/content/chrome-harness.js"/>
+          src="../browser.js"></script>
 
   <script type="application/javascript">
   <![CDATA[
 
     ////////////////////////////////////////////////////////////////////////////
-    // Hacks to make xul:tabbrowser work
-
-    const Ci = Components.interfaces;
-    const CC = Components.classes;
-
-    Components.utils.import("resource://gre/modules/Services.jsm");
-
-    var handleDroppedLink = null;
-
-    var XULBrowserWindow = {
-      isBusy: false,
-      setOverLink: function (link, b) {
-      }
-    };
-
-    var gURLBar = {
-      focused: false
-    };
-
-    var gFindBarInitialized = false;
-
-    function goSetCommandEnabled() {}
-
-    ////////////////////////////////////////////////////////////////////////////
     // Tests
 
-    function getTabDocument()
+    function getAnchorJumpInTabDocument(aTabIdx)
     {
-      return getNode("tabBrowser").selectedBrowser.contentDocument;
-    }
-
-    function getAnchorJumpInTabDocument()
-    {
-      return getTabDocument().querySelector("a[name='link1']");
+      var tabDoc = aTabIdx ? tabDocumentAt(aTabIdx) : currentTabDocument();
+      return tabDoc.querySelector("a[name='link1']");
     }
 
-    function loadTab(aTabBrowserID, aURL)
+    function loadTab(aURL)
     {
-      function loadTabChecker()
-      {
-        this.type = EVENT_REORDER;
-        this.match = function loadTabChecker_match(aEvent)
-        {
-          var target = aEvent.accessible;
-          if (target.role == ROLE_INTERNAL_FRAME &&
-              target.parent.parent == getAccessible(getNode(aTabBrowserID).mTabBox.tabpanels)) {
-            return true;
-          }
-          return false;
-        }
-      }
-
-      this.eventSeq = [ new loadTabChecker() ];
+      this.eventSeq = [
+        new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument),
+        new invokerChecker(EVENT_SCROLLING_START, getAnchorJumpInTabDocument)
+      ];
 
       this.invoke = function loadTab_invoke()
       {
-        getNode(aTabBrowserID).loadURI(aURL);
+        tabBrowser().loadURI(aURL);
       }
 
       this.getID = function loadTab_getID()
       {
-        return "load tab " + aURL + " for " + prettyName(aTabBrowserID);
+        return "load tab: " + aURL;
       }
     }
 
-    function advanceFocusIntoTab(aTabBrowserID)
+    function loadTabInBackground(aURL)
     {
       this.eventSeq = [
-        new focusChecker(getTabDocument),
+        new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
+      ];
+
+      this.unexpectedEventSeq = [
+        new invokerChecker(EVENT_SCROLLING_START, getAnchorJumpInTabDocument, 1)
+      ];
+
+      this.invoke = function loadTabInBackground_invoke()
+      {
+        tabBrowser().loadOneTab(aURL, null, "", null, true);
+      }
+
+      this.getID = function loadTabInBackground_getID()
+      {
+        return "load tab in background: " + aURL;
+      }
+    }
+
+    function switchToBackgroundTab()
+    {
+      this.eventSeq = [
         new invokerChecker(EVENT_SCROLLING_START, getAnchorJumpInTabDocument)
       ];
 
-      this.invoke = function advanceFocusIntoTab_invoke()
+      this.invoke = function switchToBackgroundTab_invoke()
       {
-        var tabDoc = getAccessible(getTabDocument());
-        tabDoc.takeFocus();
+        tabBrowser().selectTabAtIndex(1);
       }
 
-      this.getID = function advanceFocusIntoTab_getID()
+      this.getID = function switchToBackgroundTab_getID()
       {
-        return "advance focus into loaded tab";
+        return "switch to background tab";
       }
     }
 
     //gA11yEventDumpToConsole = true; // debug stuff
 
     var gQueue = null;
     function doTest()
     {
@@ -133,73 +105,40 @@
       var jar = getJar(rootDir);
       if (jar) {
         var tmpdir = extractJarToTmp(jar);
         rootDir = "file://" + tmpdir.path + '/';
       }
       var url = rootDir + "scroll.html#link1";
 
       gQueue = new eventQueue();
-      gQueue.push(new loadTab("tabBrowser", url));
-      gQueue.push(new advanceFocusIntoTab("tabBrowser"));
+
+      gQueue.push(new loadTab(url));
+      gQueue.push(new loadTabInBackground(url));
+      gQueue.push(new switchToBackgroundTab());
+      gQueue.onFinish = function() { closeBrowserWindow(); }
+
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
-    addA11yLoadEvent(doTest);
+    openBrowserWindow(doTest);
   ]]>
   </script>
 
-  <hbox flex="1" style="overflow: auto;">
+  <vbox flex="1" style="overflow: auto;">
     <body xmlns="http://www.w3.org/1999/xhtml">
       <a target="_blank"
-         href="https://bugzilla.mozilla.org/show_bug.cgi?id=437607"
-         title="Clicking the 'Skip to main content' link once works, second time fails to initiate a V cursor jump">
-        Mozilla Bug 437607
-      </a><br/>
-      <a target="_blank"
-         href="https://bugzilla.mozilla.org/show_bug.cgi?id=519303"
-         title="Same page links to targets with content fires scrolling start accessible event on leaf text node">
-        Mozilla Bug 519303
-      </a>
-      <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=691734"
          title="Make sure scrolling start event is fired when document receive focus">
         Mozilla Bug 691734
       </a>
 
       <p id="display"></p>
       <div id="content" style="display: none">
       </div>
       <pre id="test">
       </pre>
     </body>
 
-    <vbox flex="1">
-      <!-- Hack to make xul:tabbrowser work -->
-      <menubar>
-        <menu label="menu">
-          <menupopup>
-            <menuitem label="close window hook" id="menu_closeWindow"/>
-            <menuitem label="close hook" id="menu_close"/>
-          </menupopup>
-        </menu>
-      </menubar>
-      <keyset>
-        <key id="key_close"/>
-      </keyset>
-
-      <hbox>
-      <tabs id="tabbrowser-tabs" class="tabbrowser-tabs"
-            tabbrowser="tabBrowser"
-            flex="1">
-        <tab class="tabbrowser-tab" selected="true" label="tab"/>
-      </tabs>
-      </hbox>
-      <tabbrowser id="tabBrowser"
-                  type="content-primary"
-                  tabcontainer="tabbrowser-tabs"
-                  flex="1"/>
-    </vbox>
-    <toolbar id="addon-bar"/>
-  </hbox>
-
+    <vbox id="eventdump"></vbox>
+  </vbox>
 </window>
--- a/accessible/tests/mochitest/test_aria_roles.html
+++ b/accessible/tests/mochitest/test_aria_roles.html
@@ -126,17 +126,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <table role="main" id="main_table">main table
     <tr><td>hi<td></tr></table>
   <table role="navigation" id="navigation_table">navigation table
     <tr><td>hi<td></tr></table>
   <table role="search" id="search_table">search table
     <tr><td>hi<td></tr></table>
 
   <!-- test gEmptyRoleMap -->
-  <table role="label">
+  <table role="button">
     <tr>
       <td id="cell">cell</td>
     </tr>
   </table>
 
   <!-- user agents must not map abstract roles to platform API -->
   <!-- test abstract base type roles -->
   <div role="composite" id="composite">composite</div>
--- a/accessible/tests/mochitest/test_childAtPoint.html
+++ b/accessible/tests/mochitest/test_childAtPoint.html
@@ -32,25 +32,25 @@
       // document.
       testChildAtPoint(txt, -1, 1, false, null);
       testChildAtPoint(txt, -1, 1, true, null);
 
       // ::MustPrune case, point is outside of root accessible.
       testChildAtPoint(txt, -10000, 10000, false, null);
       testChildAtPoint(txt, -10000, 10000, true, null);
 
-      // Not specific case, point is inside of label accessible.
-      var label = getAccessible("label");
-      var labelText = label.firstChild;
-      testChildAtPoint(label, 1, 1, false, labelText);
-      testChildAtPoint(label, 1, 1, true, labelText);
+      // Not specific case, point is inside of btn accessible.
+      var btn = getAccessible("btn");
+      var btnText = btn.firstChild;
+      testChildAtPoint(btn, 1, 1, false, btnText);
+      testChildAtPoint(btn, 1, 1, true, btnText);
   
-      // Not specific case, point is outside of label accessible.
-      testChildAtPoint(label, -1, 1, false, null);
-      testChildAtPoint(label, -1, 1, true, null);
+      // Not specific case, point is outside of btn accessible.
+      testChildAtPoint(btn, -1, 1, false, null);
+      testChildAtPoint(btn, -1, 1, true, null);
 
       // Out of flow accessible testing, do not return out of flow accessible
       // because it's not a child of the accessible even visually it is.
       var rectArea = getNode("area").getBoundingClientRect();
       var outOfFlow = getNode("outofflow");
       outOfFlow.style.left = rectArea.left + "px";
       outOfFlow.style.top = rectArea.top + "px";
 
@@ -73,17 +73,17 @@
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div role="list" id="list">
     <div role="listitem" id="listitem"><span role="image" id="image">img</span>item</div>
   </div>
 
-  <span role="label">label1</span><span role="label" id="label">label2</span>
+  <span role="button">button1</span><span role="button" id="btn">button2</span>
 
   <span role="textbox">textbox1</span><span role="textbox" id="txt">textbox2</span>
 
   <div id="outofflow" style="width: 10px; height: 10px; position: absolute; left: 0px; top: 0px; background-color: yellow;">
   </div>
   <div id="area" style="width: 100px; height: 100px; background-color: blue;"></div>
 
 </body>
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -33,17 +33,16 @@
  * 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 ***** */
 
 #filter substitution
 
 pref("toolkit.defaultChromeURI", "chrome://browser/content/shell.xul");
-pref("general.useragent.compatMode.firefox", true);
 pref("browser.chromeURL", "chrome://browser/content/");
 #ifdef MOZ_OFFICIAL_BRANDING
 pref("browser.homescreenURL", "file:///system/home/homescreen.html");
 #else
 pref("browser.homescreenURL", "file:///data/local/homescreen.html,file:///system/home/homescreen.html");
 #endif
 
 // URL for the dialer application.
@@ -389,23 +388,33 @@ pref("security.fileuri.strict_origin_pol
 // deep within the bowels of the widgetry system.  Remove me when GL
 // compositing isn't default disabled in widget/android.
 pref("layers.acceleration.force-enabled", true);
 
 // screen.enabled and screen.brightness properties.
 pref("dom.screenEnabledProperty.enabled", true);
 pref("dom.screenBrightnessProperty.enabled", true);
 
+// handle links targeting new windows
+// 1=current window/tab, 2=new window, 3=new tab in most recent window
+pref("browser.link.open_newwindow", 3);
+
+// 0: no restrictions - divert everything
+// 1: don't divert window.open at all
+// 2: don't divert window.open with features
+pref("browser.link.open_newwindow.restriction", 0);
+
 // Enable browser frame
 pref("dom.mozBrowserFramesEnabled", true);
 pref("dom.mozBrowserFramesWhitelist", "http://localhost:6666");
 
 // Temporary permission hack for WebSMS
 pref("dom.sms.enabled", true);
 pref("dom.sms.whitelist", "file://,http://localhost:6666");
+
 // Ignore X-Frame-Options headers.
 pref("b2g.ignoreXFrameOptions", true);
 
 // "Preview" landing of bug 710563, which is bogged down in analysis
 // of talos regression.  This is a needed change for higher-framerate
 // CSS animations, and incidentally works around an apparent bug in
 // our handling of requestAnimationFrame() listeners, which are
 // supposed to enable this REPEATING_PRECISE_CAN_SKIP behavior.  The
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -42,28 +42,26 @@ const Cu = Components.utils;
 const CC = Components.Constructor;
 
 const LocalFile = CC('@mozilla.org/file/local;1',
                      'nsILocalFile',
                      'initWithPath');
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
+
 XPCOMUtils.defineLazyGetter(Services, 'env', function() {
   return Cc['@mozilla.org/process/environment;1']
            .getService(Ci.nsIEnvironment);
 });
+
 XPCOMUtils.defineLazyGetter(Services, 'ss', function() {
   return Cc['@mozilla.org/content/style-sheet-service;1']
            .getService(Ci.nsIStyleSheetService);
 });
-XPCOMUtils.defineLazyGetter(Services, 'fm', function() {
-  return Cc['@mozilla.org/focus-manager;1']
-           .getService(Ci.nsIFocusManager);
-});
 
 // In order to use http:// scheme instead of file:// scheme
 // (that is much more restricted) the following code kick-off
 // a local http server listening on http://127.0.0.1:7777 and
 // http://localhost:7777.
 function startupHttpd(baseDir, port) {
   const httpdURL = 'chrome://browser/content/httpd.js';
   let httpd = {};
@@ -71,39 +69,39 @@ function startupHttpd(baseDir, port) {
   let server = new httpd.nsHttpServer();
   server.registerDirectory('/', new LocalFile(baseDir));
   server.registerContentType('appcache', 'text/cache-manifest');
   server.start(port);
 }
 
 // FIXME Bug 707625
 // until we have a proper security model, add some rights to
-// the pre-installed web applications 
+// the pre-installed web applications
 function addPermissions(urls) {
   let permissions = [
     'indexedDB', 'indexedDB-unlimited', 'webapps-manage', 'offline-app'
   ];
   urls.forEach(function(url) {
     let uri = Services.io.newURI(url, null, null);
     let allow = Ci.nsIPermissionManager.ALLOW_ACTION;
-    
+
     permissions.forEach(function(permission) {
       Services.perms.add(uri, permission, allow);
     });
   });
 }
 
 
 var shell = {
   // FIXME/bug 678695: this should be a system setting
   preferredScreenBrightness: 1.0,
 
-  get home() {
-    delete this.home;
-    return this.home = document.getElementById('homescreen');
+  get contentBrowser() {
+    delete this.contentBrowser;
+    return this.contentBrowser = document.getElementById('homescreen');
   },
 
   get homeURL() {
     try {
       let homeSrc = Services.env.get('B2G_HOMESCREEN');
       if (homeSrc)
         return homeSrc;
     } catch (e) {}
@@ -126,17 +124,17 @@ var shell = {
     if (!homeURL) {
       let msg = 'Fatal error during startup: [No homescreen found]';
       return alert(msg);
     }
 
     window.controllers.appendController(this);
     window.addEventListener('keypress', this);
     window.addEventListener('MozApplicationManifest', this);
-    this.home.addEventListener('load', this, true);
+    this.contentBrowser.addEventListener('load', this, true);
 
     try {
       Services.io.offline = false;
 
       let fileScheme = 'file://';
       if (homeURL.substring(0, fileScheme.length) == fileScheme) {
         homeURL = homeURL.replace(fileScheme, '');
 
@@ -151,17 +149,25 @@ var shell = {
         homeURL = homeURL.replace(baseDir, baseHost + ':' + SERVER_PORT);
       }
       addPermissions([homeURL]);
     } catch (e) {
       let msg = 'Fatal error during startup: [' + e + '[' + homeURL + ']';
       return alert(msg);
     }
 
-    let browser = this.home;
+    // Load webapi+apps.js as a frame script
+    let frameScriptUrl = 'chrome://browser/content/webapi.js';
+    try {
+      messageManager.loadFrameScript(frameScriptUrl, true);
+    } catch (e) {
+      dump('Error when loading ' + frameScriptUrl + ' as a frame script: ' + e + '\n');
+    }
+
+    let browser = this.contentBrowser;
     browser.homePage = homeURL;
     browser.goHome();
   },
 
   stop: function shell_stop() {
     window.controllers.removeController(this);
     window.removeEventListener('keypress', this);
     window.removeEventListener('MozApplicationManifest', this);
@@ -182,59 +188,63 @@ var shell = {
 
   isCommandEnabled: function shell_isCommandEnabled(cmd) {
     return true;
   },
 
   doCommand: function shell_doCommand(cmd) {
     switch (cmd) {
       case 'cmd_close':
-        this.home.contentWindow.postMessage('appclose', '*');
+        content.postMessage('appclose', '*');
         break;
     }
   },
 
   handleEvent: function shell_handleEvent(evt) {
     switch (evt.type) {
       case 'keypress':
         switch (evt.keyCode) {
           case evt.DOM_VK_HOME:
-            this.sendEvent(this.home.contentWindow, 'home');
+            this.sendEvent(content, 'home');
             break;
           case evt.DOM_VK_SLEEP:
             this.toggleScreen();
 
             let details = {
               'enabled': screen.mozEnabled
             };
-            this.sendEvent(this.home.contentWindow, 'sleep', details);
+            this.sendEvent(content, 'sleep', details);
             break;
           case evt.DOM_VK_ESCAPE:
             if (evt.defaultPrevented)
               return;
             this.doCommand('cmd_close');
             break;
         }
         break;
       case 'load':
-        this.home.removeEventListener('load', this, true);
+        this.contentBrowser.removeEventListener('load', this, true);
         this.turnScreenOn();
+
+        let chromeWindow = window.QueryInterface(Ci.nsIDOMChromeWindow);
+        chromeWindow.browserDOMWindow = new nsBrowserAccess();
+
         this.sendEvent(window, 'ContentStart');
         break;
       case 'MozApplicationManifest':
         try {
           if (!Services.prefs.getBoolPref('browser.cache.offline.enable'))
             return;
 
           let contentWindow = evt.originalTarget.defaultView;
           let documentElement = contentWindow.document.documentElement;
           if (!documentElement)
             return;
 
-          let manifest = documentElement.getAttribute("manifest");
+          let manifest = documentElement.getAttribute('manifest');
           if (!manifest)
             return;
 
           let documentURI = contentWindow.document.documentURIObject;
           if (!Services.perms.testPermission(documentURI, 'offline-app')) {
             if (Services.prefs.getBoolPref('browser.offline-apps.notify')) {
               // FIXME Bug 710729 - Add a UI for offline cache notifications
               return;
@@ -268,83 +278,52 @@ var shell = {
   },
   turnScreenOff: function shell_turnScreenOff() {
     screen.mozEnabled = false;
     screen.mozBrightness = 0.0;
   },
   turnScreenOn: function shell_turnScreenOn() {
     screen.mozEnabled = true;
     screen.mozBrightness = this.preferredScreenBrightness;
-  },
-};
-
-(function VirtualKeyboardManager() {
-  let activeElement = null;
-  let isKeyboardOpened = false;
-
-  let constructor = {
-    handleEvent: function vkm_handleEvent(evt) {
-      let contentWindow = shell.home.contentWindow.wrappedJSObject;
-
-      switch (evt.type) {
-        case 'ContentStart':
-          contentWindow.navigator.mozKeyboard = new MozKeyboard();
-          break;
-        case 'keypress':
-          if (evt.keyCode != evt.DOM_VK_ESCAPE || !isKeyboardOpened)
-            return;
-
-          shell.sendEvent(contentWindow, 'hideime');
-          isKeyboardOpened = false;
-
-          evt.preventDefault();
-          evt.stopPropagation();
-          break;
-        case 'mousedown':
-          if (evt.target != activeElement || isKeyboardOpened)
-            return;
-
-          let type = activeElement.type;
-          shell.sendEvent(contentWindow, 'showime', { type: type });
-          isKeyboardOpened = true;
-          break;
-      }
-    },
-    observe: function vkm_observe(subject, topic, data) {
-      let contentWindow = shell.home.contentWindow;
-
-      let shouldOpen = parseInt(data);
-      if (shouldOpen && !isKeyboardOpened) {
-        activeElement = Services.fm.focusedElement;
-        if (!activeElement)
-          return;
-
-        let type = activeElement.type;
-        shell.sendEvent(contentWindow, 'showime', { type: type });
-      } else if (!shouldOpen && isKeyboardOpened) {
-        shell.sendEvent(contentWindow, 'hideime');
-      }
-      isKeyboardOpened = shouldOpen;
-    }
-  };
-
-  Services.obs.addObserver(constructor, 'ime-enabled-state-changed', false);
-  ['ContentStart', 'keypress', 'mousedown'].forEach(function vkm_events(type) {
-    window.addEventListener(type, constructor, true);
-  });
-})();
-
-
-function MozKeyboard() {
-}
-
-MozKeyboard.prototype = {
-  sendKey: function mozKeyboardSendKey(keyCode, charCode) {
-    charCode = (charCode == undefined) ? keyCode : charCode;
-
-    var utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIDOMWindowUtils);
-    ['keydown', 'keypress', 'keyup'].forEach(function sendKeyEvents(type) {
-      utils.sendKeyEvent(type, keyCode, charCode, null);
-    });
   }
 };
 
+
+
+function nsBrowserAccess() {
+}
+
+nsBrowserAccess.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow]),
+
+  openURI: function openURI(uri, opener, where, context) {
+    // TODO This should be replaced by an 'open-browser-window' intent
+    let contentWindow = content.wrappedJSObject;
+    if (!('getApplicationManager' in contentWindow))
+      return null;
+
+    let applicationManager = contentWindow.getApplicationManager();
+    if (!applicationManager)
+      return null;
+
+    let url = uri ? uri.spec : 'about:blank';
+    let window = applicationManager.launch(url, where);
+    return window.contentWindow;
+  },
+
+  openURIInFrame: function openURIInFrame(uri, opener, where, context) {
+    throw new Error('Not Implemented');
+  },
+
+  isTabContentWindow: function isTabContentWindow(contentWindow) {
+    return contentWindow == window;
+  }
+};
+
+// Pipe `console` log messages to the nsIConsoleService which writes them
+// to logcat.
+Services.obs.addObserver(function onConsoleAPILogEvent(subject, topic, data) {
+  let message = subject.wrappedJSObject;
+  let prefix = "Content JS " + message.level.toUpperCase() +
+               " at " + message.filename + ":" + message.lineNumber +
+               " in " + (message.functionName || "anonymous") + ": ";
+  Services.console.logStringMessage(prefix + Array.join(message.arguments, " "));
+}, "console-api-log-event", false);
--- a/b2g/chrome/content/touch.js
+++ b/b2g/chrome/content/touch.js
@@ -61,22 +61,22 @@
   // This can be turned on if canPreventMouseEvents is true and the consumer
   // application call evt.preventDefault();
   let preventMouseEvents = false;
 
   let TouchEventHandler = {
     events: ['mousedown', 'mousemove', 'mouseup', 'click', 'unload'],
     start: function teh_start() {
       this.events.forEach((function(evt) {
-        shell.home.addEventListener(evt, this, true);
+        shell.contentBrowser.addEventListener(evt, this, true);
       }).bind(this));
     },
     stop: function teh_stop() {
       this.events.forEach((function(evt) {
-        shell.home.removeEventListener(evt, this, true);
+        shell.contentBrowser.removeEventListener(evt, this, true);
       }).bind(this));
     },
     handleEvent: function teh_handleEvent(evt) {
       if (evt.button || ignoreEvents)
         return;
 
       let eventTarget = this.target;
       let type = '';
@@ -134,22 +134,17 @@
             return;
 
           window.clearTimeout(contextMenuTimeout);
           this.target = null;
           TouchEventHandler.stop();
           return;
 
         case 'click':
-          if (!isNewTouchAction) {
-            debug('click: cancel');
-
-            evt.preventDefault();
-            evt.stopPropagation();
-          } else {
+          if (isNewTouchAction) {
             // Mouse events has been cancelled so dispatch a sequence
             // of events to where touchend has been fired
             if (preventMouseEvents) {
               evt.preventDefault();
               evt.stopPropagation();
 
               let target = evt.target;
               ignoreEvents = true;
new file mode 100644
--- /dev/null
+++ b/b2g/chrome/content/webapi.js
@@ -0,0 +1,1791 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+dump('======================= webapi+apps.js ======================= \n');
+
+'use strict';
+
+let { classes: Cc, interfaces: Ci, utils: Cu }  = Components;
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+Cu.import('resource://gre/modules/Services.jsm');
+
+XPCOMUtils.defineLazyGetter(Services, 'fm', function() {
+  return Cc['@mozilla.org/focus-manager;1']
+           .getService(Ci.nsIFocusManager);
+});
+
+(function() {
+  function generateAPI(window) {
+    let navigator = window.navigator;
+    XPCOMUtils.defineLazyGetter(navigator, 'mozSettings', function() {
+      return new Settings();
+    });
+
+    XPCOMUtils.defineLazyGetter(navigator, 'mozContacts', function() {
+      return ContactsManager;
+    });
+
+    XPCOMUtils.defineLazyGetter(navigator, 'mozApps', function() {
+      let mozApps = {
+        enumerate: function mozAppsEnumerate(callback) {
+          callback(webapps);
+        }
+      };
+      return mozApps;
+    });
+
+    XPCOMUtils.defineLazyGetter(navigator, 'mozKeyboard', function() {
+      return new MozKeyboard();
+    });
+
+    updateApplicationCache(window);
+  };
+
+  let progressListener = {
+    onStateChange: function onStateChange(progress, request,
+                                          flags, status) {
+    },
+
+    onProgressChange: function onProgressChange(progress, request,
+                                                curSelf, maxSelf,
+                                                curTotal, maxTotal) {
+    },
+
+    onLocationChange: function onLocationChange(progress, request,
+                                                locationURI, flags) {
+      if (locationURI.spec.indexOf('/apps/') == -1)
+        return;
+
+      content.addEventListener('appwillopen', function(evt) {
+        let appManager = content.wrappedJSObject.Gaia.AppManager;
+        let topWindow = appManager.foregroundWindow.contentWindow;
+        generateAPI(topWindow);
+      });
+
+      generateAPI(content.wrappedJSObject);
+    },
+
+    onStatusChange: function onStatusChange(progress, request,
+                                            status, message) {
+    },
+
+    onSecurityChange: function onSecurityChange(progress, request,
+                                                state) {
+    },
+
+    QueryInterface: function QueryInterface(aIID) {
+      if (aIID.equals(Ci.nsIWebProgressListener) ||
+          aIID.equals(Ci.nsISupportsWeakReference) ||
+          aIID.equals(Ci.nsISupports)) {
+          return this;
+      }
+
+      throw Components.results.NS_ERROR_NO_INTERFACE;
+    }
+  };
+
+  let flags = Ci.nsIWebProgress.NOTIFY_LOCATION;
+  let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                            .getInterface(Ci.nsIWebProgress);
+  flags = Ci.nsIWebProgress.NOTIFY_ALL;
+  webProgress.addProgressListener(progressListener, flags);
+})();
+
+
+// WebApps - Application Cache
+function updateApplicationCache(window) {
+  try {
+    var cache = window.applicationCache;
+    cache.update();
+
+    cache.addEventListener('updateready', function updateReady(evt) {
+      // XXX Add a nice UI when an update is ready asking if the user
+      // want to reload the application now.
+      cache.swapCache();
+      window.document.location.reload();
+    });
+  } catch (e) {}
+}
+
+// MozKeyboard
+(function VirtualKeyboardManager() {
+  let activeElement = null;
+  let isKeyboardOpened = false;
+  
+  function fireEvent(type, details) {
+    let event = content.document.createEvent('CustomEvent');
+    event.initCustomEvent(type, true, true, details ? details : {});
+    content.dispatchEvent(event);
+  }
+
+  let constructor = {
+    handleEvent: function vkm_handleEvent(evt) {
+      switch (evt.type) {
+        case 'keypress':
+          if (evt.keyCode != evt.DOM_VK_ESCAPE || !isKeyboardOpened)
+            return;
+
+          fireEvent('hideime');
+          isKeyboardOpened = false;
+
+          evt.preventDefault();
+          evt.stopPropagation();
+          break;
+
+        case 'mousedown':
+          if (evt.target != activeElement || isKeyboardOpened)
+            return;
+
+          let type = activeElement.type;
+          fireEvent('showime', { type: type });
+          isKeyboardOpened = true;
+          break;
+      }
+    },
+    observe: function vkm_observe(subject, topic, data) {
+      let shouldOpen = parseInt(data);
+      if (shouldOpen && !isKeyboardOpened) {
+        activeElement = Services.fm.focusedElement;
+        if (!activeElement)
+          return;
+
+        let type = activeElement.type;
+        fireEvent('showime', { type: type });
+      } else if (!shouldOpen && isKeyboardOpened) {
+        fireEvent('hideime');
+      }
+      isKeyboardOpened = shouldOpen;
+    }
+  };
+
+  Services.obs.addObserver(constructor, 'ime-enabled-state-changed', false);
+  ['keypress', 'mousedown'].forEach(function vkm_events(type) {
+    addEventListener(type, constructor, true);
+  });
+})();
+
+
+function MozKeyboard() {
+}
+
+MozKeyboard.prototype = {
+  sendKey: function mozKeyboardSendKey(keyCode, charCode) {
+    charCode = (charCode == undefined) ? keyCode : charCode;
+
+    let utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDOMWindowUtils);
+    ['keydown', 'keypress', 'keyup'].forEach(function sendKey(type) {
+      utils.sendKeyEvent(type, keyCode, charCode, null);
+    });
+  }
+};
+
+// MozSettings - Bug 678695
+function Settings() {
+  content.addEventListener('message', this);
+}
+
+Settings.prototype = {
+  ERROR_SETTING_UNKNOWN: 0x0001,
+
+  _requests: [],
+  handleEvent: function settings_handleEvent(event) {
+    var data = event.data;
+    if (typeof data !== 'string')
+      return;
+
+    var cmd = data.split(':');
+    if (cmd.length < 4 || cmd[0] != 'settings')
+      return;
+
+    var method = cmd[1], id = cmd[2], key = cmd[3], value = cmd[4];
+    var request = this._requests[id];
+    switch (method) {
+      case 'error':
+        request.error = this.ERROR_SETTING_UNKNOWN;
+        this._dispatchEvent(request, request.TYPE_ERROR);
+        break;
+
+      case 'success':
+        request.result = new SettingsMessage(key, value);
+        this._dispatchEvent(request, request.TYPE_SUCCESS);
+        break;
+    }
+  },
+
+  get: function settings_get(key) {
+    var request = new SettingsRequest();
+    var id = this._requests.length;
+    this._requests.push(request);
+
+    var msg = 'settings:get:' + id + ':' + key;
+    content.top.wrappedJSObject.postMessage(msg, '*');
+    return request;
+  },
+
+  set: function settings_set(key, value) {
+    var request = new SettingsRequest();
+    var id = this._requests.length;
+    this._requests.push(request);
+
+    var msg = 'settings:set:' + id + ':' + key + ':' + value;
+    content.top.wrappedJSObject.postMessage(msg, '*');
+    return request;
+  },
+
+  _dispatchEvent: function(target, type) {
+    var evt = content.document.createEvent('CustomEvent');
+    evt.initCustomEvent(type, true, false, null);
+    target.dispatchEvent(evt);
+  }
+};
+
+
+/* ========== nsIDOMMozSettingsRequest ========== */
+function SettingsRequest() {
+  this.readyState = this.STATE_PROCESSING;
+
+  this.error = null;
+  this.onerror = null;
+  // XXX should be an array
+  this._errorCallback = null;
+
+  this.result = null;
+  this.onsuccess = null;
+  // XXX should be an array
+  this._successCallback = null;
+}
+
+SettingsRequest.prototype = {
+  // States of the request
+  STATE_PROCESSING: 'processing',
+  STATE_DONE: 'done',
+
+  // Types of events
+  TYPE_SUCCESS: 'success',
+  TYPE_ERROR: 'error',
+
+  addEventListener: function sr_addEventListener(type, callback) {
+    switch (type) {
+      case this.TYPE_SUCCESS:
+        this._successCallback = callback;
+        break;
+      case this.TYPE_ERROR:
+        this._errorCallback = callback;
+        break;
+    }
+  },
+
+  removeEventListener: function sr_removeEventListener(type, callback) {
+    switch (type) {
+      case this.TYPE_SUCCESS:
+        this._successCallback = null;
+        break;
+      case this.TYPE_ERROR:
+        this._errorCallback = null;
+        break;
+    }
+  },
+
+  dispatchEvent: function sr_dispatchEvent(event) {
+    this.readyState = this.STATE_DONE;
+
+    switch (event.type) {
+      case this.TYPE_SUCCESS:
+        if (this._successCallback)
+          this._successCallback(event);
+
+        if (this.onsuccess)
+          this.onsuccess(event);
+        break;
+      case this.TYPE_ERROR:
+        if (this._errorCallback)
+          this._errorCallback(event);
+
+        if (this.onerror)
+          this.onerror(event);
+        break;
+    }
+  }
+};
+
+/* ========== nsIDOMMozSettingsMessage ========== */
+function SettingsMessage(name, value) {
+  this.name = name;
+  this.value = value;
+}
+
+
+// MozApps - Bug 709015
+var webapps = [
+  { // browser
+    installOrigin: 'http://gaiamobile.org:8888',
+    origin: '../browser',
+    receipt: null,
+    installTime: 1323339869000,
+    manifest: {
+      'name': 'Browser',
+      'description': 'Gaia Web Browser',
+      'launch_path': '/browser.html',
+      'developer': {
+        'name': 'The Gaia Team',
+        'url': 'https://github.com/andreasgal/gaia'
+      },
+      'icons': {
+        '120': '/style/icons/Browser.png'
+      }
+    }
+  },
+  { // camera
+    'installOrigin': 'http://gaiamobile.org:8888',
+    'origin': '../camera',
+    'receipt': null,
+    'installTime': 1323339869000,
+    manifest: {
+      'name': 'Camera',
+      'description': 'Gaia Camera',
+      'launch_path': '/camera.html',
+      'developer': {
+        'name': 'The Gaia Team',
+        'url': 'https://github.com/andreasgal/gaia'
+      },
+      'icons': {
+        '120': '/style/icons/Camera.png'
+      }
+    }
+  },
+  { // dialer
+    'installOrigin': 'http://gaiamobile.org:8888',
+    'origin': '../dialer',
+    'receipt': null,
+    'installTime': 1323339869000,
+    manifest: {
+      'name': 'Dialer',
+      'description': 'Gaia Dialer',
+      'launch_path': '/dialer.html',
+      'developer': {
+        'name': 'The Gaia Team',
+        'url': 'https://github.com/andreasgal/gaia'
+      },
+      'icons': {
+        '120': '/style/icons/Phone.png'
+      }
+    }
+  },
+  { // gallery
+    'installOrigin': 'http://gaiamobile.org:8888',
+    'origin': '../gallery',
+    'receipt': null,
+    'installTime': 1323339869000,
+    manifest: {
+      'name': 'Gallery',
+      'description': 'Gaia Gallery',
+      'launch_path': '/gallery.html',
+      'developer': {
+        'name': 'The Gaia Team',
+        'url': 'https://github.com/andreasgal/gaia'
+      },
+      'icons': {
+        '120': '/style/icons/Gallery.png'
+      }
+    }
+  },
+  { // music
+    'installOrigin': 'http://gaiamobile.org:8888',
+    'origin': '../music',
+    'receipt': null,
+    'installTime': 1323339869000,
+    manifest: {
+      'name': 'Music',
+      'description': 'Gaia Music',
+      'launch_path': '/music.html',
+      'developer': {
+        'name': 'The Gaia Team',
+        'url': 'https://github.com/andreasgal/gaia'
+      },
+      'icons': {
+        '120': '/style/icons/Music.png'
+      }
+    }
+  },
+  { // market
+    'installOrigin': 'http://gaiamobile.org:8888',
+    'origin': '../market',
+    'receipt': null,
+    'installTime': 1323339869000,
+    manifest: {
+      'name': 'Market',
+      'description': 'Market for downloading and installing apps',
+      'launch_path': '/market.html',
+      'developer': {
+        'name': 'The Gaia Team',
+        'url': 'https://github.com/andreasgal/gaia'
+      },
+      'icons': {
+        '120': '/style/icons/Market.png'
+      }
+    }
+  },
+  { // settings
+    'installOrigin': 'http://gaiamobile.org:8888',
+    'origin': '../settings',
+    'receipt': null,
+    'installTime': 1323339869000,
+    manifest: {
+      'name': 'Settings',
+      'description': 'Gaia Settings',
+      'launch_path': '/settings.html',
+      'developer': {
+        'name': 'The Gaia Team',
+        'url': 'https://github.com/andreasgal/gaia'
+      },
+      'icons': {
+        '120': '/style/icons/Settings.png'
+      }
+    }
+  },
+  { // sms
+    'installOrigin': 'http://gaiamobile.org:8888',
+    'origin': '../sms',
+    'receipt': null,
+    'installTime': 1323339869000,
+    manifest: {
+      'name': 'Messages',
+      'description': 'Gaia Messages',
+      'launch_path': '/sms.html',
+      'developer': {
+        'name': 'The Gaia Team',
+        'url': 'https://github.com/andreasgal/gaia'
+      },
+      'icons': {
+        '120': '/style/icons/Messages.png'
+      }
+    }
+  }
+];
+
+
+// Bug 674720 - webContacts
+var contacts = [
+  {
+    id: '3',
+    displayName: 'Coby Newman',
+    name: {
+      familyName: ['Coby'],
+      givenName: ['Newman']
+    },
+    phones: ['1-823-949-7735'],
+    emails: ['posuere.at@hendreritaarcu.com']
+  },
+  {
+    id: '6',
+    displayName: 'Caesar Velasquez',
+    name: {
+      familyName: ['Caesar'],
+      givenName: ['Velasquez']
+    },
+    phones: ['1-355-185-5419'],
+    emails: ['fames@Duis.org']
+  },
+  {
+    id: '9',
+    displayName: 'Hamilton Farrell',
+    name: {
+      familyName: ['Hamilton'],
+      givenName: ['Farrell']
+    },
+    phones: ['1-682-456-9186'],
+    emails: ['sem@Uttinciduntvehicula.com']
+  },
+  {
+    id: '12',
+    displayName: 'Emery Livingston',
+    name: {
+      familyName: ['Emery'],
+      givenName: ['Livingston']
+    },
+    phones: ['1-510-151-9801'],
+    emails: ['orci.luctus.et@massaInteger.com']
+  },
+  {
+    id: '15',
+    displayName: 'Griffith Heath',
+    name: {
+      familyName: ['Griffith'],
+      givenName: ['Heath']
+    },
+    phones: ['1-800-719-3201'],
+    emails: ['dapibus@Inlorem.ca']
+  },
+  {
+    id: '18',
+    displayName: 'Luke Stuart',
+    name: {
+      familyName: ['Luke'],
+      givenName: ['Stuart']
+    },
+    phones: ['1-120-910-1976'],
+    emails: ['congue@nibh.ca']
+  },
+  {
+    id: '21',
+    displayName: 'Brennan Love',
+    name: {
+      familyName: ['Brennan'],
+      givenName: ['Love']
+    },
+    phones: ['1-724-155-2807'],
+    emails: ['interdum.libero.dui@cursusvestibulum.edu']
+  },
+  {
+    id: '24',
+    displayName: 'Lamar Meadows',
+    name: {
+      familyName: ['Lamar'],
+      givenName: ['Meadows']
+    },
+    phones: ['1-976-164-8769'],
+    emails: ['tincidunt@non.com']
+  },
+  {
+    id: '27',
+    displayName: 'Erasmus Flynn',
+    name: {
+      familyName: ['Erasmus'],
+      givenName: ['Flynn']
+    },
+    phones: ['1-488-678-3487'],
+    emails: ['lorem.ut.aliquam@eu.ca']
+  },
+  {
+    id: '30',
+    displayName: 'Aladdin Ellison',
+    name: {
+      familyName: ['Aladdin'],
+      givenName: ['Ellison']
+    },
+    phones: ['1-977-743-6797'],
+    emails: ['sociosqu.ad@sollicitudin.org']
+  },
+  {
+    id: '33',
+    displayName: 'Valentine Rasmussen',
+    name: {
+      familyName: ['Valentine'],
+      givenName: ['Rasmussen']
+    },
+    phones: ['1-265-504-2025'],
+    emails: ['ultrices.iaculis@acsem.edu']
+  },
+  {
+    id: '36',
+    displayName: 'Deacon Murphy',
+    name: {
+      familyName: ['Deacon'],
+      givenName: ['Murphy']
+    },
+    phones: ['1-770-450-1221'],
+    emails: ['varius@erat.edu']
+  },
+  {
+    id: '39',
+    displayName: 'Paul Kennedy',
+    name: {
+      familyName: ['Paul'],
+      givenName: ['Kennedy']
+    },
+    phones: ['1-689-891-3529'],
+    emails: ['ac.arcu@vitae.edu']
+  },
+  {
+    id: '42',
+    displayName: 'Aaron Chase',
+    name: {
+      familyName: ['Aaron'],
+      givenName: ['Chase']
+    },
+    phones: ['1-451-574-7937'],
+    emails: ['tempor.bibendum.Donec@pharetraQuisque.edu']
+  },
+  {
+    id: '45',
+    displayName: 'Geoffrey Dunn',
+    name: {
+      familyName: ['Geoffrey'],
+      givenName: ['Dunn']
+    },
+    phones: ['1-924-387-2395'],
+    emails: ['a.malesuada@tellusPhasellus.com']
+  },
+  {
+    id: '48',
+    displayName: 'Ashton Russo',
+    name: {
+      familyName: ['Ashton'],
+      givenName: ['Russo']
+    },
+    phones: ['1-182-776-5600'],
+    emails: ['Aliquam.vulputate.ullamcorper@faucibusorci.edu']
+  },
+  {
+    id: '51',
+    displayName: 'Owen Noble',
+    name: {
+      familyName: ['Owen'],
+      givenName: ['Noble']
+    },
+    phones: ['1-463-693-1336'],
+    emails: ['et@vulputateveliteu.ca']
+  },
+  {
+    id: '54',
+    displayName: 'Kamal Blake',
+    name: {
+      familyName: ['Kamal'],
+      givenName: ['Blake']
+    },
+    phones: ['1-636-197-1985'],
+    emails: ['tempor@malesuada.edu']
+  },
+  {
+    id: '57',
+    displayName: 'Tyrone Delaney',
+    name: {
+      familyName: ['Tyrone'],
+      givenName: ['Delaney']
+    },
+    phones: ['1-886-920-6283'],
+    emails: ['est@aliquetsemut.com']
+  },
+  {
+    id: '60',
+    displayName: 'Ciaran Sellers',
+    name: {
+      familyName: ['Ciaran'],
+      givenName: ['Sellers']
+    },
+    phones: ['1-315-414-0323'],
+    emails: ['Etiam@Nulla.com']
+  },
+  {
+    id: '63',
+    displayName: 'Bernard Alford',
+    name: {
+      familyName: ['Bernard'],
+      givenName: ['Alford']
+    },
+    phones: ['1-430-958-2651'],
+    emails: ['elementum.lorem.ut@sociisnatoque.edu']
+  },
+  {
+    id: '66',
+    displayName: 'Kamal Cote',
+    name: {
+      familyName: ['Kamal'],
+      givenName: ['Cote']
+    },
+    phones: ['1-666-609-9141'],
+    emails: ['eleifend.egestas@cursus.edu']
+  },
+  {
+    id: '69',
+    displayName: 'Lucius Mckee',
+    name: {
+      familyName: ['Lucius'],
+      givenName: ['Mckee']
+    },
+    phones: ['1-224-590-6780'],
+    emails: ['Fusce.dolor@tellusnon.org']
+  },
+  {
+    id: '72',
+    displayName: 'Dale Coleman',
+    name: {
+      familyName: ['Dale'],
+      givenName: ['Coleman']
+    },
+    phones: ['1-320-245-3036'],
+    emails: ['dapibus.rutrum@ametlorem.org']
+  },
+  {
+    id: '75',
+    displayName: 'Kermit Nguyen',
+    name: {
+      familyName: ['Kermit'],
+      givenName: ['Nguyen']
+    },
+    phones: ['1-247-825-8563'],
+    emails: ['per@risusMorbi.org']
+  },
+  {
+    id: '78',
+    displayName: 'Timon Horton',
+    name: {
+      familyName: ['Timon'],
+      givenName: ['Horton']
+    },
+    phones: ['1-739-233-8981'],
+    emails: ['Etiam@nonummyultriciesornare.ca']
+  },
+  {
+    id: '81',
+    displayName: 'Dale Lamb',
+    name: {
+      familyName: ['Dale'],
+      givenName: ['Lamb']
+    },
+    phones: ['1-640-507-8295'],
+    emails: ['dapibus.id@pedeac.edu']
+  },
+  {
+    id: '84',
+    displayName: 'Owen Acevedo',
+    name: {
+      familyName: ['Owen'],
+      givenName: ['Acevedo']
+    },
+    phones: ['1-403-201-3170'],
+    emails: ['porttitor.tellus.non@dolorFusce.edu']
+  },
+  {
+    id: '87',
+    displayName: 'Richard Mckee',
+    name: {
+      familyName: ['Richard'],
+      givenName: ['Mckee']
+    },
+    phones: ['1-783-513-0684'],
+    emails: ['senectus.et.netus@Vestibulum.com']
+  },
+  {
+    id: '90',
+    displayName: 'Elijah Bass',
+    name: {
+      familyName: ['Elijah'],
+      givenName: ['Bass']
+    },
+    phones: ['1-632-950-0553'],
+    emails: ['erat@sapien.com']
+  },
+  {
+    id: '93',
+    displayName: 'Barrett Wells',
+    name: {
+      familyName: ['Barrett'],
+      givenName: ['Wells']
+    },
+    phones: ['1-112-180-5617'],
+    emails: ['interdum.ligula@varius.edu']
+  },
+  {
+    id: '96',
+    displayName: 'Herman Meyer',
+    name: {
+      familyName: ['Herman'],
+      givenName: ['Meyer']
+    },
+    phones: ['1-296-252-5507'],
+    emails: ['urna@vitaealiquameros.org']
+  },
+  {
+    id: '99',
+    displayName: 'Ashton Hinton',
+    name: {
+      familyName: ['Ashton'],
+      givenName: ['Hinton']
+    },
+    phones: ['1-695-256-8929'],
+    emails: ['lorem@mattisornare.org']
+  },
+  {
+    id: '102',
+    displayName: 'Harrison Marsh',
+    name: {
+      familyName: ['Harrison'],
+      givenName: ['Marsh']
+    },
+    phones: ['1-897-458-1730'],
+    emails: ['pharetra.felis.eget@auctor.com']
+  },
+  {
+    id: '105',
+    displayName: 'Benedict Santana',
+    name: {
+      familyName: ['Benedict'],
+      givenName: ['Santana']
+    },
+    phones: ['1-565-457-4828'],
+    emails: ['amet.metus.Aliquam@Maecenas.org']
+  },
+  {
+    id: '108',
+    displayName: 'David Church',
+    name: {
+      familyName: ['David'],
+      givenName: ['Church']
+    },
+    phones: ['1-179-353-3314'],
+    emails: ['Nullam.enim@Utsagittis.edu']
+  },
+  {
+    id: '111',
+    displayName: 'Colt Wolfe',
+    name: {
+      familyName: ['Colt'],
+      givenName: ['Wolfe']
+    },
+    phones: ['1-587-970-8581'],
+    emails: ['hendrerit.Donec.porttitor@tinciduntaliquam.org']
+  },
+  {
+    id: '114',
+    displayName: 'Carlos Bishop',
+    name: {
+      familyName: ['Carlos'],
+      givenName: ['Bishop']
+    },
+    phones: ['1-963-305-6702'],
+    emails: ['Nam@cursusNunc.org']
+  },
+  {
+    id: '117',
+    displayName: 'Dominic Ware',
+    name: {
+      familyName: ['Dominic'],
+      givenName: ['Ware']
+    },
+    phones: ['1-609-458-5449'],
+    emails: ['Fusce.aliquet@Etiam.ca']
+  },
+  {
+    id: '120',
+    displayName: 'Phillip Whitley',
+    name: {
+      familyName: ['Phillip'],
+      givenName: ['Whitley']
+    },
+    phones: ['1-284-955-1766'],
+    emails: ['per.inceptos.hymenaeos@nequesedsem.ca']
+  },
+  {
+    id: '123',
+    displayName: 'Valentine Sargent',
+    name: {
+      familyName: ['Valentine'],
+      givenName: ['Sargent']
+    },
+    phones: ['1-346-890-6417'],
+    emails: ['nec@dolorFusce.com']
+  },
+  {
+    id: '126',
+    displayName: 'Gabriel Huber',
+    name: {
+      familyName: ['Gabriel'],
+      givenName: ['Huber']
+    },
+    phones: ['1-399-465-0589'],
+    emails: ['pretium.neque@nislsemconsequat.ca']
+  },
+  {
+    id: '129',
+    displayName: 'George Tyler',
+    name: {
+      familyName: ['George'],
+      givenName: ['Tyler']
+    },
+    phones: ['1-739-571-2737'],
+    emails: ['blandit.viverra.Donec@dictum.ca']
+  },
+  {
+    id: '132',
+    displayName: 'Asher Carey',
+    name: {
+      familyName: ['Asher'],
+      givenName: ['Carey']
+    },
+    phones: ['1-477-425-4723'],
+    emails: ['torquent.per.conubia@blanditNamnulla.edu']
+  },
+  {
+    id: '135',
+    displayName: 'Anthony Solomon',
+    name: {
+      familyName: ['Anthony'],
+      givenName: ['Solomon']
+    },
+    phones: ['1-570-753-4296'],
+    emails: ['risus.Nunc@hendreritconsectetuercursus.com']
+  },
+  {
+    id: '138',
+    displayName: 'Griffith Fuller',
+    name: {
+      familyName: ['Griffith'],
+      givenName: ['Fuller']
+    },
+    phones: ['1-779-242-5342'],
+    emails: ['Suspendisse@aliquam.ca']
+  },
+  {
+    id: '141',
+    displayName: 'Beau Brewer',
+    name: {
+      familyName: ['Beau'],
+      givenName: ['Brewer']
+    },
+    phones: ['1-664-184-7334'],
+    emails: ['magna.tellus.faucibus@ultricesposuerecubilia.com']
+  },
+  {
+    id: '144',
+    displayName: 'Jordan Campbell',
+    name: {
+      familyName: ['Jordan'],
+      givenName: ['Campbell']
+    },
+    phones: ['1-593-938-2525'],
+    emails: ['Curae;.Phasellus@Morbiquis.ca']
+  },
+  {
+    id: '147',
+    displayName: 'Cyrus Cabrera',
+    name: {
+      familyName: ['Cyrus'],
+      givenName: ['Cabrera']
+    },
+    phones: ['1-915-748-1349'],
+    emails: ['lorem.tristique@acmetus.edu']
+  },
+  {
+    id: '150',
+    displayName: 'Hamilton Boone',
+    name: {
+      familyName: ['Hamilton'],
+      givenName: ['Boone']
+    },
+    phones: ['1-278-421-9845'],
+    emails: ['non.sapien@quamdignissimpharetra.edu']
+  },
+  {
+    id: '153',
+    displayName: 'Wallace Donovan',
+    name: {
+      familyName: ['Wallace'],
+      givenName: ['Donovan']
+    },
+    phones: ['1-940-175-9334'],
+    emails: ['justo@lacusMaurisnon.org']
+  },
+  {
+    id: '156',
+    displayName: 'Kirk Buckley',
+    name: {
+      familyName: ['Kirk'],
+      givenName: ['Buckley']
+    },
+    phones: ['1-283-177-6304'],
+    emails: ['Cras@Morbinon.edu']
+  },
+  {
+    id: '159',
+    displayName: 'Simon Hall',
+    name: {
+      familyName: ['Simon'],
+      givenName: ['Hall']
+    },
+    phones: ['1-269-202-5174'],
+    emails: ['mus.Proin@dolor.org']
+  },
+  {
+    id: '162',
+    displayName: 'Trevor Rush',
+    name: {
+      familyName: ['Trevor'],
+      givenName: ['Rush']
+    },
+    phones: ['1-865-595-9074'],
+    emails: ['Fusce@Donec.edu']
+  },
+  {
+    id: '165',
+    displayName: 'Todd Mccormick',
+    name: {
+      familyName: ['Todd'],
+      givenName: ['Mccormick']
+    },
+    phones: ['1-398-916-3514'],
+    emails: ['at@ornareelit.org']
+  },
+  {
+    id: '168',
+    displayName: 'Yuli Gay',
+    name: {
+      familyName: ['Yuli'],
+      givenName: ['Gay']
+    },
+    phones: ['1-198-196-4256'],
+    emails: ['Sed.congue.elit@Inornare.edu']
+  },
+  {
+    id: '171',
+    displayName: 'Joseph Frazier',
+    name: {
+      familyName: ['Joseph'],
+      givenName: ['Frazier']
+    },
+    phones: ['1-969-410-7180'],
+    emails: ['faucibus.ut.nulla@massa.org']
+  },
+  {
+    id: '174',
+    displayName: 'Ali Chase',
+    name: {
+      familyName: ['Ali'],
+      givenName: ['Chase']
+    },
+    phones: ['1-598-924-6112'],
+    emails: ['eu.elit@necanteMaecenas.edu']
+  },
+  {
+    id: '177',
+    displayName: 'Guy Simpson',
+    name: {
+      familyName: ['Guy'],
+      givenName: ['Simpson']
+    },
+    phones: ['1-558-377-3714'],
+    emails: ['in@mauriselit.edu']
+  },
+  {
+    id: '180',
+    displayName: 'Ivan Wynn',
+    name: {
+      familyName: ['Ivan'],
+      givenName: ['Wynn']
+    },
+    phones: ['1-274-885-0477'],
+    emails: ['lobortis.quis@Sed.com']
+  },
+  {
+    id: '183',
+    displayName: 'Preston Carpenter',
+    name: {
+      familyName: ['Preston'],
+      givenName: ['Carpenter']
+    },
+    phones: ['1-758-120-5270'],
+    emails: ['elit.Curabitur@vehiculaaliquet.edu']
+  },
+  {
+    id: '186',
+    displayName: 'Demetrius Santos',
+    name: {
+      familyName: ['Demetrius'],
+      givenName: ['Santos']
+    },
+    phones: ['1-913-961-7009'],
+    emails: ['id@magnaPhasellusdolor.com']
+  },
+  {
+    id: '189',
+    displayName: 'Dale Franklin',
+    name: {
+      familyName: ['Dale'],
+      givenName: ['Franklin']
+    },
+    phones: ['1-443-971-0116'],
+    emails: ['velit.Pellentesque@IntegerurnaVivamus.com']
+  },
+  {
+    id: '192',
+    displayName: 'Abraham Randolph',
+    name: {
+      familyName: ['Abraham'],
+      givenName: ['Randolph']
+    },
+    phones: ['1-368-169-0957'],
+    emails: ['egestas@maurisidsapien.com']
+  },
+  {
+    id: '195',
+    displayName: 'Hu Avila',
+    name: {
+      familyName: ['Hu'],
+      givenName: ['Avila']
+    },
+    phones: ['1-311-333-8877'],
+    emails: ['metus@adipiscinglacusUt.com']
+  },
+  {
+    id: '198',
+    displayName: 'Garth Trujillo',
+    name: {
+      familyName: ['Garth'],
+      givenName: ['Trujillo']
+    },
+    phones: ['1-409-494-1231'],
+    emails: ['commodo.hendrerit.Donec@etnunc.ca']
+  },
+  {
+    id: '201',
+    displayName: 'Quamar Buchanan',
+    name: {
+      familyName: ['Quamar'],
+      givenName: ['Buchanan']
+    },
+    phones: ['1-114-992-7225'],
+    emails: ['tellus@consequatpurusMaecenas.ca']
+  },
+  {
+    id: '204',
+    displayName: 'Ulysses Bishop',
+    name: {
+      familyName: ['Ulysses'],
+      givenName: ['Bishop']
+    },
+    phones: ['1-485-518-5941'],
+    emails: ['fermentum.fermentum.arcu@amalesuadaid.com']
+  },
+  {
+    id: '207',
+    displayName: 'Avram Knapp',
+    name: {
+      familyName: ['Avram'],
+      givenName: ['Knapp']
+    },
+    phones: ['1-307-139-5554'],
+    emails: ['est.ac.mattis@ultricesmauris.ca']
+  },
+  {
+    id: '210',
+    displayName: 'Conan Grant',
+    name: {
+      familyName: ['Conan'],
+      givenName: ['Grant']
+    },
+    phones: ['1-331-936-0280'],
+    emails: ['turpis@odio.com']
+  },
+  {
+    id: '213',
+    displayName: 'Chester Kemp',
+    name: {
+      familyName: ['Chester'],
+      givenName: ['Kemp']
+    },
+    phones: ['1-554-119-4848'],
+    emails: ['Aenean.gravida.nunc@eu.org']
+  },
+  {
+    id: '216',
+    displayName: 'Hedley Dudley',
+    name: {
+      familyName: ['Hedley'],
+      givenName: ['Dudley']
+    },
+    phones: ['1-578-607-6287'],
+    emails: ['Nunc@dignissimtemporarcu.ca']
+  },
+  {
+    id: '219',
+    displayName: 'Jermaine Avila',
+    name: {
+      familyName: ['Jermaine'],
+      givenName: ['Avila']
+    },
+    phones: ['1-860-455-2283'],
+    emails: ['accumsan@ametdapibusid.ca']
+  },
+  {
+    id: '222',
+    displayName: 'Kamal Hamilton',
+    name: {
+      familyName: ['Kamal'],
+      givenName: ['Hamilton']
+    },
+    phones: ['1-650-389-0920'],
+    emails: ['Fusce.dolor@nuncsed.ca']
+  },
+  {
+    id: '225',
+    displayName: 'Castor Maxwell',
+    name: {
+      familyName: ['Castor'],
+      givenName: ['Maxwell']
+    },
+    phones: ['1-260-489-7135'],
+    emails: ['diam.lorem@a.ca']
+  },
+  {
+    id: '228',
+    displayName: 'Lyle Burris',
+    name: {
+      familyName: ['Lyle'],
+      givenName: ['Burris']
+    },
+    phones: ['1-250-343-2038'],
+    emails: ['eget.lacus@tempordiamdictum.com']
+  },
+  {
+    id: '231',
+    displayName: 'Merrill Dalton',
+    name: {
+      familyName: ['Merrill'],
+      givenName: ['Dalton']
+    },
+    phones: ['1-851-675-1381'],
+    emails: ['eu.tempor@blanditmattisCras.edu']
+  },
+  {
+    id: '234',
+    displayName: 'Ezekiel Medina',
+    name: {
+      familyName: ['Ezekiel'],
+      givenName: ['Medina']
+    },
+    phones: ['1-389-582-3443'],
+    emails: ['lectus.sit@interdum.ca']
+  },
+  {
+    id: '237',
+    displayName: 'Len Tran',
+    name: {
+      familyName: ['Len'],
+      givenName: ['Tran']
+    },
+    phones: ['1-434-573-6114'],
+    emails: ['turpis.Aliquam.adipiscing@montesnasceturridiculus.com']
+  },
+  {
+    id: '240',
+    displayName: 'Len Dominguez',
+    name: {
+      familyName: ['Len'],
+      givenName: ['Dominguez']
+    },
+    phones: ['1-144-489-7487'],
+    emails: ['augue@Innec.ca']
+  },
+  {
+    id: '243',
+    displayName: 'Paul Lane',
+    name: {
+      familyName: ['Paul'],
+      givenName: ['Lane']
+    },
+    phones: ['1-448-169-4312'],
+    emails: ['lectus.Cum.sociis@dolornonummyac.org']
+  },
+  {
+    id: '246',
+    displayName: 'Eric Horne',
+    name: {
+      familyName: ['Eric'],
+      givenName: ['Horne']
+    },
+    phones: ['1-124-862-6890'],
+    emails: ['commodo.tincidunt.nibh@eleifendnuncrisus.com']
+  },
+  {
+    id: '249',
+    displayName: 'Elton Ellis',
+    name: {
+      familyName: ['Elton'],
+      givenName: ['Ellis']
+    },
+    phones: ['1-492-834-0019'],
+    emails: ['lorem.eu.metus@felis.ca']
+  },
+  {
+    id: '252',
+    displayName: 'Jameson Snyder',
+    name: {
+      familyName: ['Jameson'],
+      givenName: ['Snyder']
+    },
+    phones: ['1-811-590-5893'],
+    emails: ['fermentum@Nuncmaurissapien.org']
+  },
+  {
+    id: '255',
+    displayName: 'Micah Shelton',
+    name: {
+      familyName: ['Micah'],
+      givenName: ['Shelton']
+    },
+    phones: ['1-402-504-4026'],
+    emails: ['Nunc.mauris@malesuada.ca']
+  },
+  {
+    id: '258',
+    displayName: 'Evan Lester',
+    name: {
+      familyName: ['Evan'],
+      givenName: ['Lester']
+    },
+    phones: ['1-535-915-3570'],
+    emails: ['libero@adipiscingfringillaporttitor.org']
+  },
+  {
+    id: '261',
+    displayName: 'Reuben Dalton',
+    name: {
+      familyName: ['Reuben'],
+      givenName: ['Dalton']
+    },
+    phones: ['1-296-598-2504'],
+    emails: ['tincidunt.vehicula.risus@Craseutellus.com']
+  },
+  {
+    id: '264',
+    displayName: 'Beau Baird',
+    name: {
+      familyName: ['Beau'],
+      givenName: ['Baird']
+    },
+    phones: ['1-525-882-9957'],
+    emails: ['urna.suscipit.nonummy@facilisisvitae.com']
+  },
+  {
+    id: '267',
+    displayName: 'Hedley Olsen',
+    name: {
+      familyName: ['Hedley'],
+      givenName: ['Olsen']
+    },
+    phones: ['1-945-295-5863'],
+    emails: ['vulputate.ullamcorper@Vivamusnisi.org']
+  },
+  {
+    id: '270',
+    displayName: 'Oliver Todd',
+    name: {
+      familyName: ['Oliver'],
+      givenName: ['Todd']
+    },
+    phones: ['1-551-447-1296'],
+    emails: ['Donec.egestas@rutrum.edu']
+  },
+  {
+    id: '273',
+    displayName: 'Keegan Mayo',
+    name: {
+      familyName: ['Keegan'],
+      givenName: ['Mayo']
+    },
+    phones: ['1-351-848-2796'],
+    emails: ['ridiculus@Nuncsed.ca']
+  },
+  {
+    id: '276',
+    displayName: 'Wang Cote',
+    name: {
+      familyName: ['Wang'],
+      givenName: ['Cote']
+    },
+    phones: ['1-439-568-2013'],
+    emails: ['Morbi@tinciduntduiaugue.org']
+  },
+  {
+    id: '279',
+    displayName: 'Hyatt Rowe',
+    name: {
+      familyName: ['Hyatt'],
+      givenName: ['Rowe']
+    },
+    phones: ['1-596-765-3807'],
+    emails: ['eu.erat.semper@enimnonnisi.com']
+  },
+  {
+    id: '282',
+    displayName: 'Cade Wyatt',
+    name: {
+      familyName: ['Cade'],
+      givenName: ['Wyatt']
+    },
+    phones: ['1-988-289-5924'],
+    emails: ['erat.nonummy@sedpedeCum.com']
+  },
+  {
+    id: '285',
+    displayName: 'Stephen Vincent',
+    name: {
+      familyName: ['Stephen'],
+      givenName: ['Vincent']
+    },
+    phones: ['1-954-435-1259'],
+    emails: ['nec.euismod@ultricies.ca']
+  },
+  {
+    id: '288',
+    displayName: 'Tobias Cherry',
+    name: {
+      familyName: ['Tobias'],
+      givenName: ['Cherry']
+    },
+    phones: ['1-270-763-1111'],
+    emails: ['Nulla.aliquet@sit.com']
+  },
+  {
+    id: '291',
+    displayName: 'Keane Trevino',
+    name: {
+      familyName: ['Keane'],
+      givenName: ['Trevino']
+    },
+    phones: ['1-794-929-8599'],
+    emails: ['sem.semper.erat@Aliquamnecenim.edu']
+  },
+  {
+    id: '294',
+    displayName: 'Kennedy Cooley',
+    name: {
+      familyName: ['Kennedy'],
+      givenName: ['Cooley']
+    },
+    phones: ['1-725-946-1901'],
+    emails: ['urna.justo@Duismienim.edu']
+  },
+  {
+    id: '297',
+    displayName: 'Lucian Pope',
+    name: {
+      familyName: ['Lucian'],
+      givenName: ['Pope']
+    },
+    phones: ['1-186-946-8356'],
+    emails: ['justo.Proin@dis.com']
+  },
+  {
+    id: '300',
+    displayName: 'Hu Combs',
+    name: {
+      familyName: ['Hu'],
+      givenName: ['Combs']
+    },
+    phones: ['1-398-488-5222'],
+    emails: ['faucibus.lectus@nuncsedpede.com']
+  }
+];
+
+var ContactsManager = {
+  contacts: contacts,
+  find: function contactsManager(fields, successCallback, errorCallback) {
+    var contacts = this.contacts.slice();
+    successCallback(contacts);
+  },
+  create: function contactsCreate(successCallback, errorCallback, contact) {
+    this.contacts.push(contact);
+    successCallback();
+  },
+  delete: function contactsDelete(successCallback, errorCallback, id) {
+    var count = contacts.length;
+    for (var i = 0; i < count; i++) {
+      if (contacts[i].id != id)
+        continue;
+      var oldContact = contacts.slice(i, 1);
+      successCallback(oldContact);
+      return;
+    }
+    errorCallback();
+  }
+};
+
+let { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import('resource://gre/modules/Geometry.jsm');
+Cu.import('resource://gre/modules/Services.jsm');
+
+const ContentPanning = {
+  init: function cp_init() {
+    ['mousedown', 'mouseup', 'mousemove'].forEach(function(type) {
+      addEventListener(type, ContentPanning, true);
+    });
+  },
+
+  handleEvent: function cp_handleEvent(evt) {
+    switch (evt.type) {
+      case 'mousedown':
+        this.onTouchStart(evt);
+        break;
+      case 'mousemove':
+        this.onTouchMove(evt);
+        break;
+      case 'mouseup':
+        this.onTouchEnd(evt);
+        break;
+      case 'click':
+        evt.stopPropagation();
+        evt.preventDefault();
+        evt.target.removeEventListener('click', this, true);
+        break;
+    }
+  },
+
+  position: {
+    origin: new Point(0, 0),
+    current: new Point(0 , 0)
+  },
+
+  onTouchStart: function cp_onTouchStart(evt) {
+    this.dragging = true;
+    KineticPanning.stop();
+
+    this.scrollCallback = this.getPannable(evt.originalTarget);
+    this.position.origin.set(evt.screenX, evt.screenY);
+    this.position.current.set(evt.screenX, evt.screenY);
+    KineticPanning.record(new Point(0, 0));
+  },
+
+  onTouchEnd: function cp_onTouchEnd(evt) {
+    if (!this.dragging)
+      return;
+    this.dragging = false;
+
+    if (this.isPan()) {
+      if (evt.detail) // The event will generate a click
+        evt.target.addEventListener('click', this, true);
+
+      KineticPanning.start(this);
+    }
+  },
+
+  onTouchMove: function cp_onTouchMove(evt) {
+    if (!this.dragging || !this.scrollCallback)
+      return;
+
+    let current = this.position.current;
+    let delta = new Point(evt.screenX - current.x, evt.screenY - current.y);
+    current.set(evt.screenX, evt.screenY);
+
+    if (this.isPan()) {
+      KineticPanning.record(delta);
+      this.scrollCallback(delta.scale(-1));
+    }
+  },
+
+
+  onKineticBegin: function cp_onKineticBegin(evt) {
+  },
+
+  onKineticPan: function cp_onKineticPan(delta) {
+    return !this.scrollCallback(delta);
+  },
+
+  onKineticEnd: function cp_onKineticEnd() {
+    if (!this.dragging)
+      this.scrollCallback = null;
+  },
+
+  isPan: function cp_isPan() {
+    let dpi = content.QueryInterface(Ci.nsIInterfaceRequestor)
+                     .getInterface(Ci.nsIDOMWindowUtils)
+                     .displayDPI;
+
+    let threshold = Services.prefs.getIntPref('ui.dragThresholdX') / 240 * dpi;
+
+    let deltaX = this.position.origin.x - this.position.current.x;
+    let deltaY = this.position.origin.y - this.position.current.y;
+    return (Math.abs(deltaX) > threshold || Math.abs(deltaY) > threshold);
+  },
+
+  getPannable: function cp_getPannable(node) {
+    if (!(node instanceof Ci.nsIDOMHTMLElement) || node.tagName == 'HTML')
+      return null;
+
+    let content = node.ownerDocument.defaultView;
+
+    while (!(node instanceof Ci.nsIDOMHTMLBodyElement)) {
+      let style = content.getComputedStyle(node, null);
+
+      let overflow = [style.getPropertyValue('overflow'),
+                      style.getPropertyValue('overflow-x'),
+                      style.getPropertyValue('overflow-y')];
+
+      let rect = node.getBoundingClientRect();
+      let isAuto = (overflow.indexOf('auto') != -1 &&
+                   (rect.height < node.scrollHeight ||
+                    rect.width < node.scrollWidth));
+
+      let isScroll = (overflow.indexOf('scroll') != -1);
+      if (isScroll || isAuto)
+        return this._generateCallback(node);
+
+      node = node.parentNode;
+    }
+
+    return this._generateCallback(content);
+  },
+
+  _generateCallback: function cp_generateCallback(content) {
+    function scroll(delta) {
+      if (content instanceof Ci.nsIDOMHTMLElement) {
+        let oldX = content.scrollLeft, oldY = content.scrollTop;
+        content.scrollLeft += delta.x;
+        content.scrollTop += delta.y;
+        let newX = content.scrollLeft, newY = content.scrollTop;
+        return (newX != oldX) || (newY != oldY);
+      } else {
+        let oldX = content.scrollX, oldY = content.scrollY;
+        content.scrollBy(delta.x, delta.y);
+        let newX = content.scrollX, newY = content.scrollY;
+        return (newX != oldX) || (newY != oldY);
+      }
+    }
+    return scroll;
+  }
+};
+
+ContentPanning.init();
+
+
+// Min/max velocity of kinetic panning. This is in pixels/millisecond.
+const kMinVelocity = 0.4;
+const kMaxVelocity = 6;
+
+// Constants that affect the "friction" of the scroll pane.
+const kExponentialC = 1400;
+const kPolynomialC = 100 / 1000000;
+
+// How often do we change the position of the scroll pane?
+// Too often and panning may jerk near the end.
+// Too little and panning will be choppy. In milliseconds.
+const kUpdateInterval = 16;
+
+const KineticPanning = {
+  _position: new Point(0, 0),
+  _velocity: new Point(0, 0),
+  _acceleration: new Point(0, 0),
+
+  _target: null,
+  start: function kp_start(target) {
+    this.target = target;
+
+    // Calculate the initial velocity of the movement based on user input
+    let momentums = this.momentums;
+
+    let distance = new Point(0, 0);
+    momentums.forEach(function(momentum) {
+      distance.add(momentum.dx, momentum.dy);
+    });
+
+    let elapsed = momentums[momentums.length - 1].time - momentums[0].time;
+
+    function clampFromZero(x, min, max) {
+      if (x >= 0)
+        return Math.max(min, Math.min(max, x));
+      return Math.min(-min, Math.max(-max, x));
+    }
+
+    let velocityX = clampFromZero(distance.x / elapsed, 0, kMaxVelocity);
+    let velocityY = clampFromZero(distance.y / elapsed, 0, kMaxVelocity);
+
+    let velocity = this._velocity;
+    velocity.set(Math.abs(velocityX) < kMinVelocity ? 0 : velocityX,
+                 Math.abs(velocityY) < kMinVelocity ? 0 : velocityY);
+
+    // Set acceleration vector to opposite signs of velocity
+    function sign(x) {
+      return x ? (x > 0 ? 1 : -1) : 0;
+    }
+
+    this._acceleration.set(velocity.clone().map(sign).scale(-kPolynomialC));
+
+    // Reset the position
+    this._position.set(0, 0);
+
+    this._startAnimation();
+
+    this.target.onKineticBegin();
+  },
+
+  stop: function kp_stop() {
+    if (!this.target)
+      return;
+
+    this.momentums.splice(0);
+
+    this.target.onKineticEnd();
+    this.target = null;
+  },
+
+  momentums: [],
+  record: function kp_record(delta) {
+    // If the panning direction has changed, stop the current activity.
+    if (this.target && ((delta.x * this._velocity.x < 0) ||
+                        (delta.y * this._velocity.y < 0)))
+      this.stop();
+
+    this.momentums.push({ 'time': Date.now(), 'dx' : delta.x, 'dy' : delta.y });
+  },
+
+  _startAnimation: function kp_startAnimation() {
+    let c = kExponentialC;
+    function getNextPosition(position, v, a, t) {
+      // Important traits for this function:
+      //   p(t=0) is 0
+      //   p'(t=0) is v0
+      //
+      // We use exponential to get a smoother stop, but by itself exponential
+      // is too smooth at the end. Adding a polynomial with the appropriate
+      // weight helps to balance
+      position.set(v.x * Math.exp(-t / c) * -c + a.x * t * t + v.x * c,
+                   v.y * Math.exp(-t / c) * -c + a.y * t * t + v.y * c);
+    }
+
+    let startTime = content.mozAnimationStartTime;
+    let elapsedTime = 0, targetedTime = 0, averageTime = 0;
+
+    let velocity = this._velocity;
+    let acceleration = this._acceleration;
+
+    let position = this._position;
+    let nextPosition = new Point(0, 0);
+    let delta = new Point(0, 0);
+
+    let callback = (function(timestamp) {
+      if (!this.target)
+        return;
+
+      // To make animation end fast enough but to keep smoothness, average the
+      // ideal time frame (smooth animation) with the actual time lapse
+      // (end fast enough).
+      // Animation will never take longer than 2 times the ideal length of time.
+      elapsedTime = timestamp - startTime;
+      targetedTime += kUpdateInterval;
+      averageTime = (targetedTime + elapsedTime) / 2;
+
+      // Calculate new position.
+      getNextPosition(nextPosition, velocity, acceleration, averageTime);
+      delta.set(Math.round(nextPosition.x - position.x),
+                Math.round(nextPosition.y - position.y));
+
+      // Test to see if movement is finished for each component.
+      if (delta.x * acceleration.x > 0)
+        delta.x = position.x = velocity.x = acceleration.x = 0;
+
+      if (delta.y * acceleration.y > 0)
+        delta.y = position.y = velocity.y = acceleration.y = 0;
+
+      if (velocity.equals(0, 0) || delta.equals(0, 0)) {
+        this.stop();
+        return;
+      }
+
+      position.add(delta);
+      if (this.target.onKineticPan(delta.scale(-1))) {
+        this.stop();
+        return;
+      }
+
+      content.mozRequestAnimationFrame(callback);
+    }).bind(this);
+
+    content.mozRequestAnimationFrame(callback);
+  }
+};
+
--- a/b2g/chrome/jar.mn
+++ b/b2g/chrome/jar.mn
@@ -4,12 +4,13 @@ chrome.jar:
 % content branding %content/branding/
 % content browser %content/
 
 * content/shell.xul                     (content/shell.xul)
   content/shell.js                      (content/shell.js)
   content/touch.js                      (content/touch.js)
   content/commandUtil.js                (content/commandUtil.js)
   content/httpd.js                      (content/httpd.js)
+  content/webapi.js                     (content/webapi.js)
   content/content.css                   (content/content.css)
 
 % override chrome://global/content/netError.xhtml chrome://browser/content/netError.xhtml
   content/netError.xhtml                (content/netError.xhtml)
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -33,17 +33,18 @@
 # 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 *****
 
 MOZ_APP_BASENAME=B2G
 MOZ_APP_VENDOR=Mozilla
 
-MOZ_APP_VERSION=11.0a1
+MOZ_APP_VERSION=13.0a1
+MOZ_APP_UA_NAME=Firefox
 
 MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
 MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official
 # MOZ_APP_DISPLAYNAME is set by branding/configure.sh
 
 MOZ_SAFE_BROWSING=
 MOZ_SERVICES_SYNC=
 
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1107,15 +1107,15 @@ pref("devtools.editor.component", "orion
 pref("browser.menu.showCharacterEncoding", "chrome://browser/locale/browser.properties");
 
 // Allow using tab-modal prompts when possible.
 pref("prompts.tab_modal.enabled", true);
 // Whether the Panorama should animate going in/out of tabs
 pref("browser.panorama.animate_zoom", true);
 
 // Defines the url to be used for new tabs.
-pref("browser.newtab.url", "about:blank");
+pref("browser.newtab.url", "about:newtab");
 
 // Toggles the content of 'about:newtab'. Shows the grid when enabled.
-pref("browser.newtabpage.enabled", false);
+pref("browser.newtabpage.enabled", true);
 
 // Enable the DOM full-screen API.
 pref("full-screen-api.enabled", true);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5450,17 +5450,17 @@ var TabsInTitlebar = {
       titlebar.style.marginBottom = - Math.min(tabsToolbarRect.top - titlebarTop,
                                                tabsToolbarRect.height) + "px";
 
       document.documentElement.setAttribute("tabsintitlebar", "true");
 
       if (!this._draghandle) {
         let tmp = {};
         Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
-        this._draghandle = new tmp.WindowDraggingElement(tabsToolbar, window);
+        this._draghandle = new tmp.WindowDraggingElement(tabsToolbar);
         this._draghandle.mouseDownCheck = function () {
           return !this._dragBindingAlive && TabsInTitlebar.enabled;
         };
       }
     } else {
       document.documentElement.removeAttribute("tabsintitlebar");
 
       titlebar.style.marginBottom = "";
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -1,16 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const PREF_NEWTAB_ENABLED = "browser.newtabpage.enabled";
 
 Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, true);
 
-Cu.import("resource:///modules/NewTabUtils.jsm");
+let tmp = {};
+Cu.import("resource:///modules/NewTabUtils.jsm", tmp);
+let NewTabUtils = tmp.NewTabUtils;
 
 registerCleanupFunction(function () {
   reset();
 
   while (gBrowser.tabs.length > 1)
     gBrowser.removeTab(gBrowser.tabs[1]);
 
   Services.prefs.clearUserPref(PREF_NEWTAB_ENABLED);
--- a/browser/components/preferences/cookies.js
+++ b/browser/components/preferences/cookies.js
@@ -688,17 +688,20 @@ var gCookiesWindow = {
       }
       this._view._rowCount += rowCountImpact;
       tbo.rowCountChanged(ci, rowCountImpact);
       if (invalidateRow != -1)
         tbo.invalidateRow(invalidateRow);
     }
     else {
       var rangeCount = seln.getRangeCount();
-      for (var i = 0; i < rangeCount; ++i) {
+      // Traverse backwards through selections to avoid messing 
+      // up the indices when they are deleted.
+      // See bug 388079.
+      for (var i = rangeCount - 1; i >= 0; --i) {
         var min = {}; var max = {};
         seln.getRangeAt(i, min, max);
         nextSelected = min.value;
         for (var j = min.value; j <= max.value; ++j) {
           deleteItems.push(this._view._getItemAtIndex(j));
           if (!this._view.hasNextSibling(-1, max.value))
             --nextSelected;
         }
--- a/browser/components/sessionstore/test/Makefile.in
+++ b/browser/components/sessionstore/test/Makefile.in
@@ -155,16 +155,17 @@ include $(topsrcdir)/config/rules.mk
 	browser_659591.js \
 	browser_662812.js \
 	browser_665702-state_session.js \
 	browser_682507.js \
 	browser_687710.js \
 	browser_687710_2.js \
 	browser_694378.js \
 	browser_705597.js \
+	browser_707862.js \
 	$(NULL)
 
 ifneq ($(OS_ARCH),Darwin)
 _BROWSER_TEST_FILES += \
 	browser_597071.js \
 	browser_625016.js \
 	$(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_707862.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let tabState = {
+  entries: [{url: "about:home", children: [{url: "about:mozilla"}]}]
+};
+
+function test() {
+  waitForExplicitFinish();
+
+  let tab = gBrowser.addTab("about:blank");
+  registerCleanupFunction(function () gBrowser.removeTab(tab));
+
+  let browser = tab.linkedBrowser;
+
+  whenBrowserLoaded(browser, function () {
+    ss.setTabState(tab, JSON.stringify(tabState));
+
+    let sessionHistory = browser.sessionHistory;
+    let entry = sessionHistory.getEntryAtIndex(0, false);
+
+    whenChildCount(entry, 1, function () {
+      whenChildCount(entry, 2, function () {
+        whenBrowserLoaded(browser, function () {
+          let sessionHistory = browser.sessionHistory;
+          let entry = sessionHistory.getEntryAtIndex(0, false);
+
+          whenChildCount(entry, 0, finish);
+        });
+
+        // reload the browser to deprecate the subframes
+        browser.reload();
+      });
+
+      // create a dynamic subframe
+      let doc = browser.contentDocument;
+      let iframe = doc.createElement("iframe");
+      iframe.setAttribute("src", "about:mozilla");
+      doc.body.appendChild(iframe);
+    });
+  });
+}
+
+function whenBrowserLoaded(aBrowser, aCallback) {
+  aBrowser.addEventListener("load", function onLoad() {
+    aBrowser.removeEventListener("load", onLoad, true);
+    executeSoon(aCallback);
+  }, true);
+}
+
+function whenChildCount(aEntry, aChildCount, aCallback) {
+  if (aEntry.childCount == aChildCount)
+    aCallback();
+  else
+    executeSoon(function () whenChildCount(aEntry, aChildCount, aCallback));
+}
--- a/browser/components/shell/content/setDesktopBackground.js
+++ b/browser/components/shell/content/setDesktopBackground.js
@@ -63,16 +63,28 @@ var gSetBackground = {
     this._screenWidth = screen.width;
     this._screenHeight = screen.height;
 #ifdef XP_MACOSX
     document.documentElement.getButton("accept").hidden = true;
 #endif
     if (this._screenWidth / this._screenHeight >= 1.6)
       document.getElementById("monitor").setAttribute("aspectratio", "16:10");
 
+#ifdef XP_WIN
+    // hide fill + fit options if <win7 since don't work 
+    var version = Components.classes["@mozilla.org/system-info;1"]
+                  .getService(Ci.nsIPropertyBag2)
+                  .getProperty("version");
+    var isWindows7OrHigher = (parseFloat(version) >= 6.1);
+    if (!isWindows7OrHigher) {
+      document.getElementById("fillPosition").hidden = true;
+      document.getElementById("fitPosition").hidden = true;
+    }
+#endif
+
     // make sure that the correct dimensions will be used
     setTimeout(function(self) {
       self.init(window.arguments[0]);
     }, 0, this);
   },
 
   init: function (aImage)
   {
@@ -193,11 +205,44 @@ var gSetBackground = {
         break;
       case "STRETCH":
         ctx.drawImage(this._image, 0, 0, this._screenWidth, this._screenHeight);
         break;
       case "CENTER":
         var x = (this._screenWidth - this._image.naturalWidth) / 2;
         var y = (this._screenHeight - this._image.naturalHeight) / 2;
         ctx.drawImage(this._image, x, y);
+        break;
+      case "FILL":
+        //Try maxing width first, overflow height
+        var widthRatio = this._screenWidth / this._image.naturalWidth;
+        var width = this._image.naturalWidth * widthRatio;
+        var height = this._image.naturalHeight * widthRatio;
+        if (height < this._screenHeight) {
+          //height less than screen, max height and overflow width
+          var heightRatio = this._screenHeight / this._image.naturalHeight;
+          width = this._image.naturalWidth * heightRatio;
+          height = this._image.naturalHeight * heightRatio;
+        }
+        var x = (this._screenWidth - width) / 2;
+        var y = (this._screenHeight - height) / 2;
+        ctx.drawImage(this._image, x, y, width, height);
+        break;
+      case "FIT":
+        //Try maxing width first, top and bottom borders
+        var widthRatio = this._screenWidth / this._image.naturalWidth;
+        var width = this._image.naturalWidth * widthRatio;
+        var height = this._image.naturalHeight * widthRatio;
+        var x = 0;
+        var y = (this._screenHeight - height) / 2;
+        if (height > this._screenHeight) {
+          //height overflow, maximise height, side borders
+          var heightRatio = this._screenHeight / this._image.naturalHeight;
+          width = this._image.naturalWidth * heightRatio;
+          height = this._image.naturalHeight * heightRatio;
+          x = (this._screenWidth - width) / 2;
+          y = 0;
+        }
+        ctx.drawImage(this._image, x, y, width, height);
+        break;      
     }
   }
 };
--- a/browser/components/shell/content/setDesktopBackground.xul
+++ b/browser/components/shell/content/setDesktopBackground.xul
@@ -73,16 +73,18 @@
       <label value="&position.label;"/>
       <menulist id="menuPosition"
                 label="&position.label;" 
                 oncommand="gSetBackground.updatePosition();">
         <menupopup>
           <menuitem label="&center.label;"  value="CENTER"/>
           <menuitem label="&tile.label;"    value="TILE"/>
           <menuitem label="&stretch.label;" value="STRETCH"/>
+          <menuitem label="&fill.label;"    value="FILL" id="fillPosition"/>
+          <menuitem label="&fit.label;"     value="FIT"  id="fitPosition"/>
         </menupopup>
       </menulist>
       <spacer flex="1"/>
       <label value="&color.label;"/>
       <colorpicker id="desktopColor"
                    type="button" 
                    onchange="gSetBackground.updateColor(this.color);"/> 
     </hbox>
--- a/browser/components/shell/public/nsIShellService.idl
+++ b/browser/components/shell/public/nsIShellService.idl
@@ -76,16 +76,17 @@ interface nsIShellService : nsISupports
 
   /** 
    * Flags for positioning/sizing of the Desktop Background image.
    */
   const long BACKGROUND_TILE      = 1;
   const long BACKGROUND_STRETCH   = 2;
   const long BACKGROUND_CENTER    = 3;
   const long BACKGROUND_FILL      = 4;
+  const long BACKGROUND_FIT       = 5;
 
     /**
      * Sets the desktop background image using either the HTML <IMG> 
      * element supplied or the background image of the element supplied.
      *
      * @param aImageElement Either a HTML <IMG> element or an element with
      *                      a background image from which to source the
      *                      background image. 
--- a/browser/components/shell/src/nsGNOMEShellService.cpp
+++ b/browser/components/shell/src/nsGNOMEShellService.cpp
@@ -422,16 +422,20 @@ nsGNOMEShellService::SetDesktopBackgroun
   if (!container) return rv;
 
   // Set desktop wallpaper filling style
   nsCAutoString options;
   if (aPosition == BACKGROUND_TILE)
     options.Assign("wallpaper");
   else if (aPosition == BACKGROUND_STRETCH)
     options.Assign("stretched");
+  else if (aPosition == BACKGROUND_FILL)
+    options.Assign("zoom");
+  else if (aPosition == BACKGROUND_FIT)
+    options.Assign("scaled");
   else
     options.Assign("centered");
 
   // Write the background file to the home directory.
   nsCAutoString filePath(PR_GetEnv("HOME"));
 
   // get the product brand name from localized strings
   nsString brandName;
--- a/browser/components/shell/src/nsWindowsShellService.cpp
+++ b/browser/components/shell/src/nsWindowsShellService.cpp
@@ -635,16 +635,24 @@ nsWindowsShellService::SetDesktopBackgro
       case BACKGROUND_CENTER:
         style.AssignLiteral("0");
         tile.AssignLiteral("0");
         break;
       case BACKGROUND_STRETCH:
         style.AssignLiteral("2");
         tile.AssignLiteral("0");
         break;
+      case BACKGROUND_FILL:
+        style.AssignLiteral("10");
+        tile.AssignLiteral("0");
+        break;
+      case BACKGROUND_FIT:
+        style.AssignLiteral("6");
+        tile.AssignLiteral("0");
+        break;
     }
 
     rv = regKey->WriteStringValue(NS_LITERAL_STRING("TileWallpaper"), tile);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = regKey->WriteStringValue(NS_LITERAL_STRING("WallpaperStyle"), style);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = regKey->Close();
     NS_ENSURE_SUCCESS(rv, rv);
--- a/browser/components/shell/test/browser_420786.js
+++ b/browser/components/shell/test/browser_420786.js
@@ -47,17 +47,18 @@ function onPageLoad() {
        "Wallpaper file GConf key is correct");
     is(gconf.getString(DG_OPTION_KEY), expectedGConfPosition,
        "Wallpaper position GConf key is correct");
   }
 
   checkWallpaper(Ci.nsIShellService.BACKGROUND_TILE, "wallpaper");
   checkWallpaper(Ci.nsIShellService.BACKGROUND_STRETCH, "stretched");
   checkWallpaper(Ci.nsIShellService.BACKGROUND_CENTER, "centered");
-  checkWallpaper(Ci.nsIShellService.BACKGROUND_FILL, "centered");
+  checkWallpaper(Ci.nsIShellService.BACKGROUND_FILL, "zoom");
+  checkWallpaper(Ci.nsIShellService.BACKGROUND_FIT, "scaled");
 
   // Restore GConf and wallpaper
 
   gconf.setString(DG_IMAGE_KEY, prevImageKey);
   gconf.setString(DG_OPTION_KEY, prevOptionKey);
   gconf.setBool(DG_DRAW_BG_KEY, prevDrawBgKey);
 
   wpFile.remove(false);
--- a/browser/components/thumbnails/test/head.js
+++ b/browser/components/thumbnails/test/head.js
@@ -1,12 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-Cu.import("resource:///modules/PageThumbs.jsm");
+let tmp = {};
+Cu.import("resource:///modules/PageThumbs.jsm", tmp);
+let PageThumbs = tmp.PageThumbs;
+let PageThumbsCache = tmp.PageThumbsCache;
 
 registerCleanupFunction(function () {
   while (gBrowser.tabs.length > 1)
     gBrowser.removeTab(gBrowser.tabs[1]);
 });
 
 let cachedXULDocument;
 
--- a/browser/devtools/tilt/test/browser_tilt_utils05.js
+++ b/browser/devtools/tilt/test/browser_tilt_utils05.js
@@ -1,13 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
-Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
+let tmp = {};
+Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tmp);
+let LayoutHelpers = tmp.LayoutHelpers;
 
 function init(callback) {
   let iframe = gBrowser.ownerDocument.createElement("iframe");
 
   iframe.addEventListener("load", function onLoad() {
     iframe.removeEventListener("load", onLoad, true);
     callback(iframe);
 
--- a/browser/devtools/webconsole/test/browser_gcli_commands.js
+++ b/browser/devtools/webconsole/test/browser_gcli_commands.js
@@ -1,16 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // For more information on GCLI see:
 // - https://github.com/mozilla/gcli/blob/master/docs/index.md
 // - https://wiki.mozilla.org/DevTools/Features/GCLI
 
-Components.utils.import("resource:///modules/gcli.jsm");
+let tmp = {};
+Components.utils.import("resource:///modules/gcli.jsm", tmp);
+let gcli = tmp.gcli;
 
 let hud;
 let gcliterm;
 
 registerCleanupFunction(function() {
   gcliterm = undefined;
   hud = undefined;
   Services.prefs.clearUserPref("devtools.gcli.enable");
--- a/browser/locales/en-US/chrome/browser/setDesktopBackground.dtd
+++ b/browser/locales/en-US/chrome/browser/setDesktopBackground.dtd
@@ -1,9 +1,11 @@
 <!ENTITY position.label             "Position:">
 <!ENTITY tile.label                 "Tile">
 <!ENTITY center.label               "Center">
 <!ENTITY stretch.label              "Stretch">
+<!ENTITY fill.label                 "Fill">
+<!ENTITY fit.label                  "Fit">
 <!ENTITY preview.label              "Preview">
 <!ENTITY color.label                "Color:">
 <!ENTITY setDesktopBackground.title "Set Desktop Background">
 <!ENTITY openDesktopPrefs.label     "Open Desktop Preferences">
 <!ENTITY closeWindow.key            "w">
--- a/build/mobile/devicemanagerADB.py
+++ b/build/mobile/devicemanagerADB.py
@@ -9,18 +9,20 @@ class DeviceManagerADB(DeviceManager):
 
   def __init__(self, host = None, port = 20701, retrylimit = 5, packageName = None):
     self.host = host
     self.port = port
     self.retrylimit = retrylimit
     self.retries = 0
     self._sock = None
     self.useRunAs = False
+    self.haveRoot = False
     self.useZip = False
     self.packageName = None
+    self.tempDir = None
     if packageName == None:
       if os.getenv('USER'):
         packageName = 'org.mozilla.fennec_' + os.getenv('USER')
       else:
         packageName = 'org.mozilla.fennec_'
     self.Init(packageName)
 
   def Init(self, packageName):
@@ -31,41 +33,56 @@ class DeviceManagerADB(DeviceManager):
       self.verifyRunAs(packageName)
     except:
       self.useRunAs = False
       self.packageName = None
     try:
       self.verifyZip()
     except:
       self.useZip = False
-    try:
+
+    def verifyRoot():
       # a test to see if we have root privs
       files = self.listFiles("/data/data")
       if (len(files) == 1):
         if (files[0].find("Permission denied") != -1):
           print "NOT running as root"
           raise Exception("not running as root")
+      self.haveRoot = True
+
+    try:
+      verifyRoot()
     except:
       try:
         self.checkCmd(["root"])
+        # The root command does not fail even if ADB cannot get
+        # root rights (e.g. due to production builds), so we have
+        # to check again ourselves that we have root now.
+        verifyRoot()
       except:
-        print "restarting as root failed"
+        if (self.useRunAs):
+          print "restarting as root failed, but run-as available"
+        else:
+          print "restarting as root failed"
 
   # external function
   # returns:
   #  success: True
   #  failure: False
   def pushFile(self, localname, destname):
     try:
       if (os.name == "nt"):
         destname = destname.replace('\\', '/')
       if (self.useRunAs):
-        remoteTmpFile = self.tmpDir + "/" + os.path.basename(localname)
+        remoteTmpFile = self.getTempDir() + "/" + os.path.basename(localname)
         self.checkCmd(["push", os.path.realpath(localname), remoteTmpFile])
-        self.checkCmdAs(["shell", "cp", remoteTmpFile, destname])
+        if self.useDDCopy:
+          self.checkCmdAs(["shell", "dd", "if=" + remoteTmpFile, "of=" + destname])
+        else:
+          self.checkCmdAs(["shell", "cp", remoteTmpFile, destname])
         self.checkCmd(["shell", "rm", remoteTmpFile])
       else:
         self.checkCmd(["push", os.path.realpath(localname), destname])
       if (self.isDir(destname)):
         destname = destname + "/" + os.path.basename(localname)
       self.chmodDir(destname)
       return True
     except:
@@ -324,17 +341,36 @@ class DeviceManagerADB(DeviceManager):
   # external function
   # returns:
   #  success: output of pullfile, string
   #  failure: None
   def getFile(self, remoteFile, localFile = 'tmpfile_dm_adb'):
     # TODO: add debug flags and allow for printing stdout
     # self.runCmd(["pull", remoteFile, localFile])
     try:
-      self.runCmd(["pull",  remoteFile, localFile]).stdout.read()
+
+      # First attempt to pull file regularly
+      outerr = self.runCmd(["pull",  remoteFile, localFile]).communicate()
+
+      # Now check stderr for errors
+      errl = outerr[1].splitlines()
+      if (len(errl) == 1):
+        if (((errl[0].find("Permission denied") != -1)
+          or (errl[0].find("does not exist") != -1))
+          and self.useRunAs):
+          # If we lack permissions to read but have run-as, then we should try
+          # to copy the file to a world-readable location first before attempting
+          # to pull it again.
+          remoteTmpFile = self.getTempDir() + "/" + os.path.basename(remoteFile)
+          self.checkCmdAs(["shell", "dd", "if=" + remoteFile, "of=" + remoteTmpFile])
+          self.checkCmdAs(["shell", "chmod", "777", remoteTmpFile])
+          self.runCmd(["pull",  remoteTmpFile, localFile]).stdout.read()
+          # Clean up temporary file
+          self.checkCmdAs(["shell", "rm", remoteTmpFile])
+
       f = open(localFile)
       ret = f.read()
       f.close()
       return ret;      
     except:
       return None
 
   # copy directory structure from device (remoteDir) to host (localDir)
@@ -417,16 +453,33 @@ class DeviceManagerADB(DeviceManager):
     root = "/mnt/sdcard"
     if (not self.dirExists(root)):
       root = "/data/local"
     testRoot = root + "/tests"
     if (not self.dirExists(testRoot)):
       self.mkDir(testRoot)
     return testRoot
 
+  # Gets the temporary directory we are using on this device
+  # base on our device root, ensuring also that it exists.
+  #
+  # internal function
+  # returns:
+  #  success: path for temporary directory
+  #  failure: None
+  def getTempDir(self):
+    # Cache result to speed up operations depending
+    # on the temporary directory.
+    if self.tempDir == None:
+      self.tempDir = self.getDeviceRoot() + "/tmp"
+      if (not self.dirExists(self.tempDir)):
+        return self.mkDir(self.tempDir)
+
+    return self.tempDir
+
   # Either we will have /tests/fennec or /tests/firefox but we will never have
   # both.  Return the one that exists
   # TODO: ensure we can support org.mozilla.firefox
   # external function
   # returns:
   #  success: path for app root
   #  failure: None
   def getAppRoot(self, packageName):
@@ -538,26 +591,36 @@ class DeviceManagerADB(DeviceManager):
     if (directive == "process" or directive == "all"):
       ret["process"] = self.runCmd(["shell", "ps"]).stdout.read()
     if (directive == "systime" or directive == "all"):
       ret["systime"] = self.runCmd(["shell", "date"]).stdout.read()
     print ret
     return ret
 
   def runCmd(self, args):
+    # If we are not root but have run-as, and we're trying to execute 
+    # a shell command then using run-as is the best we can do
+    if (not self.haveRoot and self.useRunAs and args[0] == "shell" and args[1] != "run-as"):
+      args.insert(1, "run-as")
+      args.insert(2, self.packageName)
     args.insert(0, "adb")
     return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
   def runCmdAs(self, args):
     if self.useRunAs:
       args.insert(1, "run-as")
       args.insert(2, self.packageName)
     return self.runCmd(args)
 
   def checkCmd(self, args):
+    # If we are not root but have run-as, and we're trying to execute 
+    # a shell command then using run-as is the best we can do
+    if (not self.haveRoot and self.useRunAs and args[0] == "shell" and args[1] != "run-as"):
+      args.insert(1, "run-as")
+      args.insert(2, self.packageName)
     args.insert(0, "adb")
     return subprocess.check_call(args)
 
   def checkCmdAs(self, args):
     if (self.useRunAs):
       args.insert(1, "run-as")
       args.insert(2, self.packageName)
     return self.checkCmd(args)
@@ -586,36 +649,43 @@ class DeviceManagerADB(DeviceManager):
     
   def isCpAvailable(self):
     # Some Android systems may not have a cp command installed,
     # or it may not be executable by the user. 
     data = self.runCmd(["shell", "cp"]).stdout.read()
     if (re.search('Usage', data)):
       return True
     else:
+      data = self.runCmd(["shell", "dd", "-"]).stdout.read()
+      if (re.search('unknown operand', data)):
+        print "'cp' not found, but 'dd' was found as a replacement"
+        self.useDDCopy = True
+        return True
       print "unable to execute 'cp' on device; consider installing busybox from Android Market"
       return False
 
   def verifyRunAs(self, packageName):
     # If a valid package name is available, and certain other
     # conditions are met, devicemanagerADB can execute file operations
     # via the "run-as" command, so that pushed files and directories 
     # are created by the uid associated with the package, more closely
     # echoing conditions encountered by Fennec at run time.
     # Check to see if run-as can be used here, by verifying a 
     # file copy via run-as.
     self.useRunAs = False
     devroot = self.getDeviceRoot()
     if (packageName and self.isCpAvailable() and devroot):
-      self.tmpDir = devroot + "/tmp"
-      if (not self.dirExists(self.tmpDir)):
-        self.mkDir(self.tmpDir)
+      tmpDir = self.getTempDir()
+
       self.checkCmd(["shell", "run-as", packageName, "mkdir", devroot + "/sanity"])
-      self.checkCmd(["push", os.path.abspath(sys.argv[0]), self.tmpDir + "/tmpfile"])
-      self.checkCmd(["shell", "run-as", packageName, "cp", self.tmpDir + "/tmpfile", devroot + "/sanity"])
+      self.checkCmd(["push", os.path.abspath(sys.argv[0]), tmpDir + "/tmpfile"])
+      if self.useDDCopy:
+        self.checkCmd(["shell", "run-as", packageName, "dd", "if=" + tmpDir + "/tmpfile", "of=" + devroot + "/sanity/tmpfile"])
+      else:
+        self.checkCmd(["shell", "run-as", packageName, "cp", tmpDir + "/tmpfile", devroot + "/sanity"])
       if (self.fileExists(devroot + "/sanity/tmpfile")):
         print "will execute commands via run-as " + packageName
         self.packageName = packageName
         self.useRunAs = True
       self.checkCmd(["shell", "rm", devroot + "/tmp/tmpfile"])
       self.checkCmd(["shell", "run-as", packageName, "rm", "-r", devroot + "/sanity"])
       
   def isUnzipAvailable(self):
--- a/build/mobile/robocop/FennecNativeDriver.java.in
+++ b/build/mobile/robocop/FennecNativeDriver.java.in
@@ -129,21 +129,25 @@ public class FennecNativeDriver implemen
   private int geckoTop = 100;
   private int geckoLeft = 0;
   private int geckoHeight= 700;
   private int geckoWidth = 1024;
 
   private void getGeckoInfo() {
     View geckoLayout = activity.findViewById(Integer.decode((String)locators.get("gecko_layout")));
     if (geckoLayout != null) {
-      geckoTop = geckoLayout.getTop();
-      geckoLeft = geckoLayout.getLeft();
+      int[] pos = new int[2];
+      geckoLayout.getLocationOnScreen(pos);
+      geckoTop = pos[1];
+      geckoLeft = pos[0];
       geckoWidth = geckoLayout.getWidth();
       geckoHeight = geckoLayout.getHeight();
       geckoInfo = true;
+    } else {
+      throw new RoboCopException("Unable to find view gecko_layout");
     }
   }
 
   public int getGeckoTop() {
     if(!geckoInfo) {
       getGeckoInfo();
     }
     return geckoTop;
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -155,16 +155,17 @@ MOZ_FEEDS = @MOZ_FEEDS@
 MOZ_TOOLKIT_SEARCH = @MOZ_TOOLKIT_SEARCH@
 MOZ_PLACES = @MOZ_PLACES@
 MOZ_SAFE_BROWSING = @MOZ_SAFE_BROWSING@
 MOZ_URL_CLASSIFIER = @MOZ_URL_CLASSIFIER@
 MOZ_ZIPWRITER = @MOZ_ZIPWRITER@
 MOZ_OGG = @MOZ_OGG@
 MOZ_RAW = @MOZ_RAW@
 MOZ_SYDNEYAUDIO = @MOZ_SYDNEYAUDIO@
+MOZ_CUBEB = @MOZ_CUBEB@
 MOZ_WAVE = @MOZ_WAVE@
 MOZ_MEDIA = @MOZ_MEDIA@
 MOZ_VORBIS = @MOZ_VORBIS@
 MOZ_TREMOR = @MOZ_TREMOR@
 MOZ_NO_THEORA_ASM = @MOZ_NO_THEORA_ASM@
 MOZ_WEBM = @MOZ_WEBM@
 MOZ_VP8_ERROR_CONCEALMENT = @MOZ_VP8_ERROR_CONCEALMENT@
 MOZ_VP8_ENCODER = @MOZ_VP8_ENCODER@
--- a/config/system-headers
+++ b/config/system-headers
@@ -1045,9 +1045,10 @@ vpx/vpx_decoder.h
 vpx/vp8dx.h
 sydneyaudio/sydney_audio.h
 vorbis/codec.h
 theora/theoradec.h
 tremor/ivorbiscodec.h
 ogg/ogg.h
 ogg/os_types.h
 nestegg/nestegg.h
+cubeb/cubeb.h
 #endif
--- a/configure.in
+++ b/configure.in
@@ -455,16 +455,19 @@ case "$target" in
     ANDROID_TOOLCHAIN="${android_toolchain}"
     ANDROID_PLATFORM="${android_platform}"
     ANDROID_SDK="${android_sdk}"
     ANDROID_PLATFORM_TOOLS="${android_platform_tools}"
     ANDROID_VERSION="${android_version}"
     if test -z "$ANDROID_PACKAGE_NAME" ; then
         ANDROID_PACKAGE_NAME='org.mozilla.$(MOZ_APP_NAME)'
     fi
+    if test -z "$MOZ_MOBILE_COMPAT" ; then
+        MOZ_MOBILE_COMPAT='All'
+    fi
 
     AC_DEFINE(ANDROID)
     AC_DEFINE_UNQUOTED(ANDROID_VERSION, $android_version)
     AC_SUBST(ANDROID_VERSION)
     CROSS_COMPILE=1
     MOZ_CHROME_FILE_FORMAT=omni
     ZLIB_DIR=yes
     ;;
@@ -476,16 +479,17 @@ esac
 fi
 
 AC_SUBST(ANDROID_NDK)
 AC_SUBST(ANDROID_TOOLCHAIN)
 AC_SUBST(ANDROID_PLATFORM)
 AC_SUBST(ANDROID_SDK)
 AC_SUBST(ANDROID_PLATFORM_TOOLS)
 AC_SUBST(ANDROID_PACKAGE_NAME)
+AC_SUBST(MOZ_MOBILE_COMPAT)
 AC_SUBST(OBJCOPY)
 
 dnl ========================================================
 dnl Checks for compilers.
 dnl ========================================================
 dnl Set CROSS_COMPILE in the environment when running configure
 dnl to use the cross-compile setup for now
 dnl ========================================================
@@ -4577,16 +4581,17 @@ MOZ_BRANDING_DIRECTORY=
 MOZ_OFFICIAL_BRANDING=
 MOZ_FEEDS=1
 MOZ_INSTALLER=1
 MOZ_JSDEBUGGER=1
 MOZ_AUTH_EXTENSION=1
 MOZ_OGG=1
 MOZ_RAW=
 MOZ_SYDNEYAUDIO=
+MOZ_CUBEB=
 MOZ_VORBIS=
 MOZ_TREMOR=
 MOZ_WAVE=1
 MOZ_MEDIA=
 MOZ_WEBM=1
 MOZ_VP8_ERROR_CONCEALMENT=
 MOZ_VP8_ENCODER=
 VPX_AS=
@@ -5587,16 +5592,17 @@ dnl ====================================
 MOZ_ARG_DISABLE_BOOL(ogg,
 [  --disable-ogg           Disable support for OGG media (Theora video and Vorbis audio)],
     MOZ_OGG=,
     MOZ_OGG=1)
 
 if test -n "$MOZ_OGG"; then
     AC_DEFINE(MOZ_OGG)
     MOZ_SYDNEYAUDIO=1
+    MOZ_CUBEB=1
     MOZ_MEDIA=1
     case "$target_cpu" in
     arm*)
         MOZ_TREMOR=1
     ;;
     *)
         MOZ_VORBIS=1
     ;;
@@ -5704,16 +5710,17 @@ if test -n "$MOZ_WEBM"; then
 fi
 
 AC_SUBST(MOZ_NATIVE_LIBVPX)
 AC_SUBST(MOZ_LIBVPX_INCLUDES)
 AC_SUBST(MOZ_LIBVPX_LIBS)
 
 if test -n "$MOZ_WEBM" -a -z "$MOZ_NATIVE_LIBVPX"; then
     MOZ_SYDNEYAUDIO=1
+    MOZ_CUBEB=1
     MOZ_MEDIA=1
     case "$target_cpu" in
     arm*)
         MOZ_TREMOR=1
     ;;
     *)
         MOZ_VORBIS=1
     ;;
@@ -5815,27 +5822,39 @@ dnl ====================================
 MOZ_ARG_DISABLE_BOOL(wave,
 [  --disable-wave          Disable Wave decoder support],
     MOZ_WAVE=,
     MOZ_WAVE=1)
 
 if test -n "$MOZ_WAVE"; then
     AC_DEFINE(MOZ_WAVE)
     MOZ_SYDNEYAUDIO=1
+    MOZ_CUBEB=1
     MOZ_MEDIA=1
 fi
 
 dnl ========================================================
-dnl = Handle dependent SYDNEYAUDIO and MEDIA defines
+dnl = Handle dependent SYDNEYAUDIO, CUBEB, and MEDIA defines
 dnl ========================================================
 
 if test -n "$MOZ_SYDNEYAUDIO"; then
     AC_DEFINE(MOZ_SYDNEYAUDIO)
 fi
 
+if test -n "$MOZ_CUBEB"; then
+    case "$target" in
+    *-mingw*)
+        AC_DEFINE(MOZ_CUBEB)
+        ;;
+    *)
+        dnl Other targets will be enabled soon.
+        ;;
+    esac
+fi
+
 if test -n "$MOZ_MEDIA"; then
     AC_DEFINE(MOZ_MEDIA)
 fi
 
 if test -n "$MOZ_VORBIS" -a -n "$MOZ_TREMOR"; then
     AC_MSG_ERROR([MOZ_VORBIS and MOZ_TREMOR are mutually exclusive!  The build system should not allow them both to be set, but they are.  Please file a bug at https://bugzilla.mozilla.org/])
 fi
 
@@ -8687,16 +8706,17 @@ AC_SUBST(CXX_VERSION)
 AC_SUBST(MSMANIFEST_TOOL)
 AC_SUBST(NS_ENABLE_TSF)
 AC_SUBST(MOZ_NSS_PATCH)
 AC_SUBST(MOZ_APP_COMPONENT_LIBS)
 AC_SUBST(MOZ_APP_EXTRA_LIBS)
 
 AC_SUBST(MOZ_MEDIA)
 AC_SUBST(MOZ_SYDNEYAUDIO)
+AC_SUBST(MOZ_CUBEB)
 AC_SUBST(MOZ_WAVE)
 AC_SUBST(MOZ_VORBIS)
 AC_SUBST(MOZ_TREMOR)
 AC_SUBST(MOZ_WEBM)
 AC_SUBST(MOZ_VP8_ERROR_CONCEALMENT)
 AC_SUBST(MOZ_VP8_ENCODER)
 AC_SUBST(MOZ_OGG)
 AC_SUBST(MOZ_ALSA_LIBS)
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -1612,16 +1612,18 @@ public:
 
   void SetNeedStyleFlush() {
     mNeedStyleFlush = true;
     if (mDisplayDocument) {
       mDisplayDocument->SetNeedStyleFlush();
     }
   }
 
+  virtual size_t SizeOfStyleSheets(nsMallocSizeOfFun aMallocSizeOf) const = 0;
+
 private:
   PRUint64 mWarnedAbout;
 
 protected:
   ~nsIDocument()
   {
     // XXX The cleanup of mNodeInfoManager (calling DropDocumentReference and
     //     releasing it) happens in the nsDocument destructor. We'd prefer to
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -9135,8 +9135,23 @@ nsDocument::GetMozVisibilityState(nsAStr
   static const char states[][8] = {
     "hidden",
     "visible"
   };
   PR_STATIC_ASSERT(NS_ARRAY_LENGTH(states) == eVisibilityStateCount);
   aState.AssignASCII(states[mVisibilityState]);
   return NS_OK;
 }
+
+static size_t
+SizeOfStyleSheetsElementIncludingThis(nsIStyleSheet* aStyleSheet,
+                                      nsMallocSizeOfFun aMallocSizeOf,
+                                      void* aData)
+{
+  return aStyleSheet->SizeOfIncludingThis(aMallocSizeOf);
+}
+
+/* virtual */ size_t
+nsDocument::SizeOfStyleSheets(nsMallocSizeOfFun aMallocSizeOf) const
+{
+  return mStyleSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
+                                          aMallocSizeOf); 
+}
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -986,16 +986,18 @@ public:
   Element* FullScreenStackTop();
 
   // This method may fire a DOM event; if it does so it will happen
   // synchronously.
   void UpdateVisibilityState();
   // Posts an event to call UpdateVisibilityState
   virtual void PostVisibilityUpdateEvent();
 
+  virtual size_t SizeOfStyleSheets(nsMallocSizeOfFun aMallocSizeOf) const;
+
 protected:
   friend class nsNodeUtils;
 
   // Returns true if a request for DOM full-screen is currently enabled in
   // this document. This returns true if there are no windowed plugins in this
   // doc tree, and if the document is visible, and if the api is not
   // disabled by pref. aIsCallerChrome must contain the return value of
   // nsContentUtils::IsCallerChrome() from the context we're checking.
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -861,18 +861,16 @@ nsFrameScriptExecutor::InitTabChildGloba
 
   JSContext* cx = JS_NewContext(rt, 8192);
   NS_ENSURE_TRUE(cx, false);
 
   mCx = cx;
 
   nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
 
-  JS_SetNativeStackQuota(cx, 128 * sizeof(size_t) * 1024);
-
   JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_PRIVATE_IS_NSISUPPORTS);
   JS_SetVersion(cx, JSVERSION_LATEST);
   JS_SetErrorReporter(cx, ContentScriptErrorReporter);
 
   xpc_LocalizeContext(cx);
 
   JSAutoRequest ar(cx);
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -86,28 +86,28 @@
 
 using namespace mozilla;
 
 #define UTF_8_REPLACEMENT_CHAR    static_cast<PRUnichar>(0xFFFD)
 
 #define TRUE_OR_FAIL_WEBSOCKET(x, ret)                                    \
   PR_BEGIN_MACRO                                                          \
     if (NS_UNLIKELY(!(x))) {                                              \
-       NS_WARNING("ENSURE_TRUE_AND_FAIL_IF_FAILED(" #x ") failed");       \
-       FailConnection();                                                  \
-       return ret;                                                        \
+      NS_WARNING("ENSURE_TRUE_AND_FAIL_IF_FAILED(" #x ") failed");        \
+      FailConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR);          \
+      return ret;                                                         \
     }                                                                     \
   PR_END_MACRO
 
 #define SUCCESS_OR_FAIL_WEBSOCKET(res, ret)                               \
   PR_BEGIN_MACRO                                                          \
     nsresult __rv = res;                                                  \
     if (NS_FAILED(__rv)) {                                                \
       NS_ENSURE_SUCCESS_BODY(res, ret)                                    \
-      FailConnection();                                                   \
+      FailConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR);          \
       return ret;                                                         \
     }                                                                     \
   PR_END_MACRO
 
 nsresult
 nsWebSocket::PrintErrorOnConsole(const char *aBundleURI,
                                  const PRUnichar *aError,
                                  const PRUnichar **aFormatStrings,
@@ -154,44 +154,45 @@ nsWebSocket::PrintErrorOnConsole(const c
   rv = console->LogMessage(errorObject);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 // when this is called the browser side wants no more part of it
 nsresult
-nsWebSocket::CloseConnection()
+nsWebSocket::CloseConnection(PRUint16 aReasonCode,
+                             const nsACString& aReasonString)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   if (mDisconnected)
     return NS_OK;
 
   // Disconnect() can release this object, so we keep a
   // reference until the end of the method
   nsRefPtr<nsWebSocket> kungfuDeathGrip = this;
 
   if (mReadyState == nsIWebSocket::CONNECTING) {
     SetReadyState(nsIWebSocket::CLOSED);
     if (mChannel) {
-      mChannel->Close(mClientReasonCode, mClientReason);
+      mChannel->Close(aReasonCode, aReasonString);
     }
     Disconnect();
     return NS_OK;
   }
 
   SetReadyState(nsIWebSocket::CLOSING);
 
   if (mDisconnected) {
     SetReadyState(nsIWebSocket::CLOSED);
     Disconnect();
     return NS_OK;
   }
 
-  return mChannel->Close(mClientReasonCode, mClientReason);
+  return mChannel->Close(aReasonCode, aReasonString);
 }
 
 nsresult
 nsWebSocket::ConsoleError()
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   nsresult rv;
 
@@ -214,22 +215,23 @@ nsWebSocket::ConsoleError()
     }
   }
   /// todo some specific errors - like for message too large
   return rv;
 }
 
 
 nsresult
-nsWebSocket::FailConnection()
+nsWebSocket::FailConnection(PRUint16 aReasonCode,
+                            const nsACString& aReasonString)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   ConsoleError();
 
-  nsresult rv = CloseConnection();
+  nsresult rv = CloseConnection(aReasonCode, aReasonString);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
   if (NS_FAILED(rv))
     NS_WARNING("Failed to dispatch the error event");
 
   return NS_OK;
 }
@@ -317,17 +319,17 @@ nsWebSocket::OnStart(nsISupports *aConte
 
 NS_IMETHODIMP
 nsWebSocket::OnStop(nsISupports *aContext, nsresult aStatusCode)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   if (mDisconnected)
     return NS_OK;
 
-  mClosedCleanly = NS_SUCCEEDED(aStatusCode);
+  mCloseEventWasClean = NS_SUCCEEDED(aStatusCode);
 
   if (aStatusCode == NS_BASE_STREAM_CLOSED &&
       mReadyState >= nsIWebSocket::CLOSING) {
     // don't generate an error event just because of an unclean close
     aStatusCode = NS_OK;
   }
 
   if (NS_FAILED(aStatusCode)) {
@@ -355,20 +357,26 @@ nsWebSocket::OnAcknowledge(nsISupports *
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWebSocket::OnServerClose(nsISupports *aContext, PRUint16 aCode,
                            const nsACString &aReason)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
-  mServerReasonCode = aCode;
-  CopyUTF8toUTF16(aReason, mServerReason);
+
+  // store code/string for onclose DOM event
+  mCloseEventCode = aCode;
+  CopyUTF8toUTF16(aReason, mCloseEventReason);
 
-  CloseConnection();                              /* reciprocate! */
+  // Send reciprocal Close frame.
+  // 5.5.1: "When sending a Close frame in response, the endpoint typically
+  // echos the status code it received"
+  CloseConnection(aCode, aReason);
+
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsWebSocket::nsIInterfaceRequestor
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
@@ -401,20 +409,19 @@ nsWebSocket::GetInterface(const nsIID &a
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsWebSocket
 ////////////////////////////////////////////////////////////////////////////////
 
 nsWebSocket::nsWebSocket() : mKeepingAlive(false),
                              mCheckMustKeepAlive(true),
                              mTriggeredCloseEvent(false),
-                             mClosedCleanly(false),
                              mDisconnected(false),
-                             mClientReasonCode(0),
-                             mServerReasonCode(nsIWebSocketChannel::CLOSE_ABNORMAL),
+                             mCloseEventWasClean(false),
+                             mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL),
                              mReadyState(nsIWebSocket::CONNECTING),
                              mOutgoingBufferedAmount(0),
                              mBinaryType(WS_BINARY_TYPE_BLOB),
                              mScriptLine(0),
                              mInnerWindowID(0)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   nsLayoutStatics::AddRef();
@@ -618,17 +625,17 @@ class nsAutoCloseWS
 public:
   nsAutoCloseWS(nsWebSocket *aWebSocket)
     : mWebSocket(aWebSocket)
   {}
 
   ~nsAutoCloseWS()
   {
     if (!mWebSocket->mChannel) {
-      mWebSocket->CloseConnection();
+      mWebSocket->CloseConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR);
     }
   }
 private:
   nsRefPtr<nsWebSocket> mWebSocket;
 };
 
 nsresult
 nsWebSocket::EstablishConnection()
@@ -901,19 +908,19 @@ nsWebSocket::SetReadyState(PRUint16 aNew
     mReadyState = aNewReadyState;
     return;
   }
 
   if (aNewReadyState == nsIWebSocket::CLOSED) {
     mReadyState = aNewReadyState;
 
     // The close event must be dispatched asynchronously.
-    rv = NS_DispatchToMainThread(new nsWSCloseEvent(this, mClosedCleanly,
-                                                    mServerReasonCode,
-                                                    mServerReason),
+    rv = NS_DispatchToMainThread(new nsWSCloseEvent(this, mCloseEventWasClean,
+                                                    mCloseEventCode,
+                                                    mCloseEventReason),
                                  NS_DISPATCH_NORMAL);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to dispatch the close event");
       mTriggeredCloseEvent = true;
       UpdateMustKeepAlive();
     }
   }
 }
@@ -1408,55 +1415,53 @@ nsWebSocket::ConvertTextToUTF8(const nsS
 }
 
 NS_IMETHODIMP
 nsWebSocket::Close(PRUint16 code, const nsAString & reason, PRUint8 argc)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
   // the reason code is optional, but if provided it must be in a specific range
+  PRUint16 closeCode = 0;
   if (argc >= 1) {
-    if (code != 1000 && (code < 3000 || code > 4999))
+    if (code != 1000 && (code < 3000 || code > 4999)) {
       return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+    }
+    closeCode = code;
   }
 
-  nsCAutoString utf8Reason;
+  nsCAutoString closeReason;
   if (argc >= 2) {
-    if (ContainsUnpairedSurrogates(reason))
+    if (ContainsUnpairedSurrogates(reason)) {
       return NS_ERROR_DOM_SYNTAX_ERR;
-
-    CopyUTF16toUTF8(reason, utf8Reason);
+    }
+    CopyUTF16toUTF8(reason, closeReason);
 
     // The API requires the UTF-8 string to be 123 or less bytes
-    if (utf8Reason.Length() > 123)
+    if (closeReason.Length() > 123) {
       return NS_ERROR_DOM_SYNTAX_ERR;
+    }
   }
 
-  // Format checks for reason and code both passed, they can now be assigned.
-  if (argc >= 1)
-    mClientReasonCode = code;
-  if (argc >= 2)
-    mClientReason = utf8Reason;
-  
   if (mReadyState == nsIWebSocket::CLOSING ||
       mReadyState == nsIWebSocket::CLOSED) {
     return NS_OK;
   }
 
   if (mReadyState == nsIWebSocket::CONNECTING) {
     // FailConnection() can release the object, so we keep a reference
     // before calling it
     nsRefPtr<nsWebSocket> kungfuDeathGrip = this;
 
-    FailConnection();
+    FailConnection(closeCode, closeReason);
     return NS_OK;
   }
 
   // mReadyState == nsIWebSocket::OPEN
-  CloseConnection();
+  CloseConnection(closeCode, closeReason);
 
   return NS_OK;
 }
 
 /**
  * This Init method should only be called by C++ consumers.
  */
 NS_IMETHODIMP
@@ -1589,18 +1594,17 @@ NS_IMETHODIMP
 nsWebSocket::Cancel(nsresult aStatus)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
 
   if (mDisconnected)
     return NS_OK;
 
   ConsoleError();
-  mClientReasonCode = nsIWebSocketChannel::CLOSE_GOING_AWAY;
-  return CloseConnection();
+  return CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
 }
 
 NS_IMETHODIMP
 nsWebSocket::Suspend()
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
--- a/content/base/src/nsWebSocket.h
+++ b/content/base/src/nsWebSocket.h
@@ -106,19 +106,21 @@ public:
 
   // Determine if preferences allow WebSocket
   static bool PrefEnabled();
 
 protected:
   nsresult ParseURL(const nsString& aURL);
   nsresult EstablishConnection();
 
-  // these three methods when called can release the WebSocket object
-  nsresult FailConnection();
-  nsresult CloseConnection();
+  // These methods when called can release the WebSocket object
+  nsresult FailConnection(PRUint16 reasonCode,
+                          const nsACString& aReasonString = EmptyCString());
+  nsresult CloseConnection(PRUint16 reasonCode,
+                           const nsACString& aReasonString = EmptyCString());
   nsresult Disconnect();
 
   nsresult ConsoleError();
   nsresult PrintErrorOnConsole(const char       *aBundleURI,
                                const PRUnichar  *aError,
                                const PRUnichar **aFormatStrings,
                                PRUint32          aFormatStringsLen);
 
@@ -161,23 +163,22 @@ protected:
   nsString mOriginalURL;
   nsString mEffectiveURL;   // after redirects
   bool mSecure; // if true it is using SSL and the wss scheme,
                         // otherwise it is using the ws scheme with no SSL
 
   bool mKeepingAlive;
   bool mCheckMustKeepAlive;
   bool mTriggeredCloseEvent;
-  bool mClosedCleanly;
   bool mDisconnected;
 
-  nsCString mClientReason;
-  nsString  mServerReason;
-  PRUint16  mClientReasonCode;
-  PRUint16  mServerReasonCode;
+  // Set attributes of DOM 'onclose' message
+  bool      mCloseEventWasClean;
+  nsString  mCloseEventReason;
+  PRUint16  mCloseEventCode;
 
   nsCString mAsciiHost;  // hostname
   PRUint32  mPort;
   nsCString mResource; // [filepath[?query]]
   nsString  mUTF16Origin;
 
   nsCOMPtr<nsIURI> mURI;
   nsCString mRequestedProtocolList;
--- a/content/html/content/src/nsHTMLCanvasElement.cpp
+++ b/content/html/content/src/nsHTMLCanvasElement.cpp
@@ -211,16 +211,38 @@ nsHTMLCanvasElement::ToDataURL(const nsA
   // do a trust check if this is a write-only canvas
   if (mWriteOnly && !nsContentUtils::IsCallerTrustedForRead()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   return ToDataURLImpl(aType, aParams, aDataURL);
 }
 
+// nsHTMLCanvasElement::mozFetchAsStream
+
+NS_IMETHODIMP
+nsHTMLCanvasElement::MozFetchAsStream(nsIInputStreamCallback *aCallback,
+                                      const nsAString& aType)
+{
+  if (!nsContentUtils::IsCallerChrome())
+    return NS_ERROR_FAILURE;
+
+  nsresult rv;
+  bool fellBackToPNG = false;
+  nsCOMPtr<nsIInputStream> inputData;
+
+  rv = ExtractData(aType, EmptyString(), getter_AddRefs(inputData), fellBackToPNG);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIAsyncInputStream> asyncData = do_QueryInterface(inputData, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return aCallback->OnInputStreamReady(asyncData);
+}
+
 nsresult
 nsHTMLCanvasElement::ExtractData(const nsAString& aType,
                                  const nsAString& aOptions,
                                  nsIInputStream** aStream,
                                  bool& aFellBackToPNG)
 {
   // note that if we don't have a current context, the spec says we're
   // supposed to just return transparent black pixels of the canvas
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -2082,20 +2082,16 @@ nsresult nsHTMLMediaElement::InitializeD
 
   return FinishDecoderSetup(decoder);
 }
 
 nsresult nsHTMLMediaElement::FinishDecoderSetup(nsMediaDecoder* aDecoder)
 {
   NS_ASSERTION(mLoadingSrc, "mLoadingSrc set up");
 
-  nsCAutoString src;
-  GetCurrentSpec(src);
-  printf("*** nsHTMLElement::FinishDecoderSetup() mDecoder=%p stream=%p src=%s\n",
-         aDecoder, aDecoder->GetStream(), src.get());
   mDecoder = aDecoder;
   AddMediaElementToURITable();
 
   // Force a same-origin check before allowing events for this media resource.
   mMediaSecurityVerified = false;
 
   // The new stream has not been suspended by us.
   mPausedForInactiveDocument = false;
@@ -2485,25 +2481,17 @@ ImageContainer* nsHTMLMediaElement::GetI
   if (mPrintSurface)
     return nsnull;
 
   // Only video frames need an image container.
   nsCOMPtr<nsIDOMHTMLVideoElement> video = do_QueryObject(this);
   if (!video)
     return nsnull;
 
-  nsRefPtr<LayerManager> manager =
-    nsContentUtils::PersistentLayerManagerForDocument(OwnerDoc());
-  if (!manager)
-    return nsnull;
-
-  mImageContainer = manager->CreateImageContainer();
-  if (manager->IsCompositingCheap()) {
-    mImageContainer->SetDelayedConversion(true);
-  }
+  mImageContainer = LayerManager::CreateImageContainer();
   return mImageContainer;
 }
 
 nsresult nsHTMLMediaElement::DispatchAudioAvailableEvent(float* aFrameBuffer,
                                                          PRUint32 aFrameBufferLength,
                                                          float aTime)
 {
   // Auto manage the memory for the frame buffer. If we fail and return
--- a/content/media/nsAudioStream.cpp
+++ b/content/media/nsAudioStream.cpp
@@ -55,16 +55,21 @@ using namespace mozilla::dom;
 #include "mozilla/Mutex.h"
 extern "C" {
 #include "sydneyaudio/sydney_audio.h"
 }
 #include "mozilla/TimeStamp.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Preferences.h"
 
+#if defined(MOZ_CUBEB)
+#include "nsAutoRef.h"
+#include "cubeb/cubeb.h"
+#endif
+
 using namespace mozilla;
 
 #if defined(XP_MACOSX)
 #define SA_PER_STREAM_VOLUME 1
 #endif
 
 // Android's audio backend is not available in content processes, so audio must
 // be remoted to the parent chrome process.
@@ -73,16 +78,20 @@ using namespace mozilla;
 #endif
 
 using mozilla::TimeStamp;
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* gAudioStreamLog = nsnull;
 #endif
 
+#if defined(MOZ_CUBEB)
+static cubeb* gCubebContext;
+#endif
+
 static const PRUint32 FAKE_BUFFER_SIZE = 176400;
 
 // Number of milliseconds per second.
 static const PRInt64 MS_PER_S = 1000;
 
 class nsNativeAudioStream : public nsAudioStream
 {
  public:
@@ -165,19 +174,19 @@ class AudioInitEvent : public nsRunnable
   {
     mOwner = owner;
   }
 
   NS_IMETHOD Run()
   {
     ContentChild * cpc = ContentChild::GetSingleton();
     NS_ASSERTION(cpc, "Content Protocol is NULL!");
-    mOwner->mAudioChild =  static_cast<AudioChild*> (cpc->SendPAudioConstructor(mOwner->mChannels,
-                                                                                mOwner->mRate,
-                                                                                mOwner->mFormat));
+    mOwner->mAudioChild =  static_cast<AudioChild*>(cpc->SendPAudioConstructor(mOwner->mChannels,
+                                                                               mOwner->mRate,
+                                                                               mOwner->mFormat));
     return NS_OK;
   }
 
   nsRefPtr<nsRemotedAudioStream> mOwner;
 };
 
 class AudioWriteEvent : public nsRunnable
 {
@@ -311,75 +320,101 @@ class AudioShutdownEvent : public nsRunn
       mAudioChild->SendShutdown();
     return NS_OK;
   }
 
   nsRefPtr<AudioChild> mAudioChild;
 };
 #endif
 
-static mozilla::Mutex* gVolumeScaleLock = nsnull;
+#define PREF_VOLUME_SCALE "media.volume_scale"
+#define PREF_USE_CUBEB "media.use_cubeb"
 
+static mozilla::Mutex* gAudioPrefsLock = nsnull;
 static double gVolumeScale = 1.0;
+static bool gUseCubeb = false;
 
-static int VolumeScaleChanged(const char* aPref, void *aClosure) {
-  nsAdoptingString value = Preferences::GetString("media.volume_scale");
-  mozilla::MutexAutoLock lock(*gVolumeScaleLock);
-  if (value.IsEmpty()) {
-    gVolumeScale = 1.0;
-  } else {
-    NS_ConvertUTF16toUTF8 utf8(value);
-    gVolumeScale = NS_MAX<double>(0, PR_strtod(utf8.get(), nsnull));
+static int PrefChanged(const char* aPref, void* aClosure)
+{
+  if (strcmp(aPref, PREF_VOLUME_SCALE) == 0) {
+    nsAdoptingString value = Preferences::GetString(aPref);
+    mozilla::MutexAutoLock lock(*gAudioPrefsLock);
+    if (value.IsEmpty()) {
+      gVolumeScale = 1.0;
+    } else {
+      NS_ConvertUTF16toUTF8 utf8(value);
+      gVolumeScale = NS_MAX<double>(0, PR_strtod(utf8.get(), nsnull));
+    }
+  } else if (strcmp(aPref, PREF_USE_CUBEB) == 0) {
+    bool value = Preferences::GetBool(aPref, true);
+    mozilla::MutexAutoLock lock(*gAudioPrefsLock);
+    gUseCubeb = value;
   }
   return 0;
 }
 
-static double GetVolumeScale() {
-  mozilla::MutexAutoLock lock(*gVolumeScaleLock);
+static double GetVolumeScale()
+{
+  mozilla::MutexAutoLock lock(*gAudioPrefsLock);
   return gVolumeScale;
 }
 
+#if defined(MOZ_CUBEB)
+static bool GetUseCubeb()
+{
+  mozilla::MutexAutoLock lock(*gAudioPrefsLock);
+  return gUseCubeb;
+}
+#endif
+
 void nsAudioStream::InitLibrary()
 {
 #ifdef PR_LOGGING
   gAudioStreamLog = PR_NewLogModule("nsAudioStream");
 #endif
-  gVolumeScaleLock = new mozilla::Mutex("nsAudioStream::gVolumeScaleLock");
-  VolumeScaleChanged(nsnull, nsnull);
-  Preferences::RegisterCallback(VolumeScaleChanged, "media.volume_scale");
+  gAudioPrefsLock = new mozilla::Mutex("nsAudioStream::gAudioPrefsLock");
+  PrefChanged(PREF_VOLUME_SCALE, nsnull);
+  Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
+#if defined(MOZ_CUBEB)
+  PrefChanged(PREF_USE_CUBEB, nsnull);
+  Preferences::RegisterCallback(PrefChanged, PREF_USE_CUBEB);
+  if (cubeb_init(&gCubebContext, "nsAudioStream") != 0) {
+    NS_WARNING("cubeb_init failed");
+  }
+#endif
 }
 
 void nsAudioStream::ShutdownLibrary()
 {
-  Preferences::UnregisterCallback(VolumeScaleChanged, "media.volume_scale");
-  delete gVolumeScaleLock;
-  gVolumeScaleLock = nsnull;
+  Preferences::UnregisterCallback(PrefChanged, PREF_VOLUME_SCALE);
+#if defined(MOZ_CUBEB)
+  Preferences::UnregisterCallback(PrefChanged, PREF_USE_CUBEB);
+#endif
+  delete gAudioPrefsLock;
+  gAudioPrefsLock = nsnull;
+
+#if defined(MOZ_CUBEB)
+  if (gCubebContext) {
+    cubeb_destroy(gCubebContext);
+    gCubebContext = nsnull;
+  }
+#endif
 }
 
 nsIThread *
 nsAudioStream::GetThread()
 {
   if (!mAudioPlaybackThread) {
     NS_NewThread(getter_AddRefs(mAudioPlaybackThread),
                  nsnull,
                  MEDIA_THREAD_STACK_SIZE);
   }
   return mAudioPlaybackThread;
 }
 
-nsAudioStream* nsAudioStream::AllocateStream()
-{
-#if defined(REMOTE_AUDIO)
-  if (XRE_GetProcessType() == GeckoProcessType_Content) {
-    return new nsRemotedAudioStream();
-  }
-#endif
-  return new nsNativeAudioStream();
-}
-
 class AsyncShutdownPlaybackThread : public nsRunnable
 {
 public:
   AsyncShutdownPlaybackThread(nsIThread* aThread) : mThread(aThread) {}
   NS_IMETHODIMP Run() { return mThread->Shutdown(); }
 private:
   nsCOMPtr<nsIThread> mThread;
 };
@@ -765,8 +800,403 @@ nsRemotedAudioStream::GetPositionInFrame
 
 bool
 nsRemotedAudioStream::IsPaused()
 {
   return mPaused;
 }
 #endif
 
+#if defined(MOZ_CUBEB)
+template <>
+class nsAutoRefTraits<cubeb_stream> : public nsPointerRefTraits<cubeb_stream>
+{
+public:
+  static void Release(cubeb_stream* aStream) { cubeb_stream_destroy(aStream); }
+};
+
+class nsBufferedAudioStream : public nsAudioStream
+{
+ public:
+  NS_DECL_ISUPPORTS
+
+  nsBufferedAudioStream();
+  ~nsBufferedAudioStream();
+
+  nsresult Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat);
+  void Shutdown();
+  nsresult Write(const void* aBuf, PRUint32 aFrames);
+  PRUint32 Available();
+  void SetVolume(double aVolume);
+  void Drain();
+  void Pause();
+  void Resume();
+  PRInt64 GetPosition();
+  PRInt64 GetPositionInFrames();
+  bool IsPaused();
+  PRInt32 GetMinWriteSize();
+
+private:
+  static long DataCallback_S(cubeb_stream*, void* aThis, void* aBuffer, long aFrames)
+  {
+    return static_cast<nsBufferedAudioStream*>(aThis)->DataCallback(aBuffer, aFrames);
+  }
+
+  static int StateCallback_S(cubeb_stream*, void* aThis, cubeb_state aState)
+  {
+    return static_cast<nsBufferedAudioStream*>(aThis)->StateCallback(aState);
+  }
+
+  long DataCallback(void* aBuffer, long aFrames);
+  int StateCallback(cubeb_state aState);
+
+  // Shared implementation of underflow adjusted position calculation.
+  // Caller must own the monitor.
+  PRInt64 GetPositionInFramesUnlocked();
+
+  // The monitor is held to protect all access to member variables.  Write()
+  // waits while mBuffer is full; DataCallback() notifies as it consumes
+  // data from mBuffer.  Drain() waits while mState is DRAINING;
+  // StateCallback() notifies when mState is DRAINED.
+  Monitor mMonitor;
+
+  // Sum of silent frames written when DataCallback requests more frames
+  // than are available in mBuffer.
+  PRUint64 mLostFrames;
+
+  // Temporary audio buffer.  Filled by Write() and consumed by
+  // DataCallback().  Once mBufferLimit is reached, Write() blocks until
+  // sufficient space becomes available in mBuffer.  The buffer and buffer
+  // limit deal in bytes, not frames.
+  nsTArray<PRUint8> mBuffer;
+  PRUint32 mBufferLimit;
+
+  // Software volume level.  Applied during the servicing of DataCallback().
+  double mVolume;
+
+  // Owning reference to a cubeb_stream.  cubeb_stream_destroy is called by
+  // nsAutoRef's destructor.
+  nsAutoRef<cubeb_stream> mCubebStream;
+
+  PRInt32 mRate;
+  PRInt32 mChannels;
+  SampleFormat mFormat;
+  PRUint32 mBytesPerFrame;
+
+  enum StreamState {
+    INITIALIZED, // Initialized, playback has not begun.
+    STARTED,     // Started by a call to Write() (iff INITIALIZED) or Resume().
+    STOPPED,     // Stopped by a call to Pause().
+    DRAINING,    // Drain requested.  DataCallback will indicate end of stream
+                 // once the remaining contents of mBuffer are requested by
+                 // cubeb, after which StateCallback will indicate drain
+                 // completion.
+    DRAINED      // StateCallback has indicated that the drain is complete.
+  };
+
+  StreamState mState;
+
+  // Arbitrary default stream latency.  The higher this value, the longer stream
+  // volume changes will take to become audible.
+  static const unsigned int DEFAULT_LATENCY_MS = 100;
+};
+#endif
+
+nsAudioStream* nsAudioStream::AllocateStream()
+{
+#if defined(REMOTE_AUDIO)
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    return new nsRemotedAudioStream();
+  }
+#endif
+#if defined(MOZ_CUBEB)
+  if (GetUseCubeb()) {
+    return new nsBufferedAudioStream();
+  }
+#endif
+  return new nsNativeAudioStream();
+}
+
+#if defined(MOZ_CUBEB)
+nsBufferedAudioStream::nsBufferedAudioStream()
+  : mMonitor("nsBufferedAudioStream"), mLostFrames(0), mVolume(1.0), mRate(0), mChannels(0),
+    mBytesPerFrame(0), mState(INITIALIZED)
+{
+}
+
+nsBufferedAudioStream::~nsBufferedAudioStream()
+{
+  Shutdown();
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS0(nsBufferedAudioStream)
+
+nsresult
+nsBufferedAudioStream::Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat)
+{
+  if (!gCubebContext || aNumChannels < 0 || aRate < 0) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mRate = aRate;
+  mChannels = aNumChannels;
+  mFormat = aFormat;
+
+  cubeb_stream_params params;
+  params.rate = aRate;
+  params.channels = aNumChannels;
+  switch (aFormat) {
+  case FORMAT_S16_LE:
+    params.format = CUBEB_SAMPLE_S16LE;
+    mBytesPerFrame = sizeof(short) * aNumChannels;
+    break;
+  case FORMAT_FLOAT32:
+    params.format = CUBEB_SAMPLE_FLOAT32LE;
+    mBytesPerFrame = sizeof(float) * aNumChannels;
+    break;
+  default:
+    return NS_ERROR_FAILURE;
+  }
+
+  {
+    cubeb_stream* stream;
+    if (cubeb_stream_init(gCubebContext, &stream, "nsBufferedAudioStream", params,
+                          DEFAULT_LATENCY_MS, DataCallback_S, StateCallback_S, this) == CUBEB_OK) {
+      mCubebStream.own(stream);
+    }
+  }
+
+  if (!mCubebStream) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Limit mBuffer to one second of audio.  This value is arbitrary, and was
+  // selected based on the observed behaviour of the existing nsAudioStream
+  // implementations.
+  mBufferLimit = aRate * mBytesPerFrame;
+  NS_ABORT_IF_FALSE(mBufferLimit % mBytesPerFrame == 0, "Must buffer complete frames");
+
+  // Pre-allocate the buffer.  nsTArray::RemoveElementsAt shrinks the buffer
+  // only if its length reaches zero, so allocator thrashing should be
+  // minimal.
+  mBuffer.SetCapacity(mBufferLimit);
+
+  return NS_OK;
+}
+
+void
+nsBufferedAudioStream::Shutdown()
+{
+  if (mCubebStream) {
+    cubeb_stream_stop(mCubebStream);
+    mCubebStream.reset();
+  }
+}
+
+nsresult
+nsBufferedAudioStream::Write(const void* aBuf, PRUint32 aFrames)
+{
+  MonitorAutoLock mon(mMonitor);
+  if (!mCubebStream) {
+    return NS_ERROR_FAILURE;
+  }
+  NS_ASSERTION(mState == INITIALIZED || mState == STARTED, "Stream write in unexpected state.");
+
+  const PRUint8* src = static_cast<const PRUint8*>(aBuf);
+  PRUint32 bytesToCopy = aFrames * mBytesPerFrame;
+
+  while (bytesToCopy > 0) {
+    NS_ABORT_IF_FALSE(mBuffer.Length() <= mBufferLimit, "Buffer invariant violated.");
+
+    PRUint32 available = NS_MIN(bytesToCopy, mBufferLimit - mBuffer.Length());
+    NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0, "Must copy complete frames.");
+
+    mBuffer.AppendElements(src, available);
+    src += available;
+    bytesToCopy -= available;
+
+    if (mState != STARTED && cubeb_stream_start(mCubebStream) == CUBEB_OK) {
+      mState = STARTED;
+    }
+
+    if (bytesToCopy > 0) {
+      mon.Wait();
+    }
+  }
+
+  return NS_OK;
+}
+
+PRUint32
+nsBufferedAudioStream::Available()
+{
+  MonitorAutoLock mon(mMonitor);
+  NS_ABORT_IF_FALSE(mBuffer.Length() <= mBufferLimit, "Buffer invariant violated.");
+  NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Buffer invariant violated.");
+  return (mBufferLimit - mBuffer.Length()) / mBytesPerFrame;
+}
+
+PRInt32 nsBufferedAudioStream::GetMinWriteSize()
+{
+  return 1;
+}
+
+void
+nsBufferedAudioStream::SetVolume(double aVolume)
+{
+  MonitorAutoLock mon(mMonitor);
+  NS_ABORT_IF_FALSE(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume");
+  mVolume = aVolume;
+}
+
+void
+nsBufferedAudioStream::Drain()
+{
+  MonitorAutoLock mon(mMonitor);
+  if (mState != STARTED) {
+    return;
+  }
+  mState = DRAINING;
+  while (mState != DRAINED) {
+    mon.Wait();
+  }
+}
+
+void
+nsBufferedAudioStream::Pause()
+{
+  MonitorAutoLock mon(mMonitor);
+  if (!mCubebStream || mState != STARTED) {
+    return;
+  }
+
+  if (cubeb_stream_stop(mCubebStream) == CUBEB_OK) {
+    mState = STOPPED;
+  }
+}
+
+void
+nsBufferedAudioStream::Resume()
+{
+  MonitorAutoLock mon(mMonitor);
+  if (!mCubebStream || mState != STOPPED) {
+    return;
+  }
+
+  if (cubeb_stream_start(mCubebStream) == CUBEB_OK) {
+    mState = STARTED;
+  }
+}
+
+PRInt64 nsBufferedAudioStream::GetPosition()
+{
+  MonitorAutoLock mon(mMonitor);
+  PRInt64 frames = GetPositionInFramesUnlocked();
+  if (frames >= 0) {
+    return USECS_PER_S * frames / mRate;
+  }
+  return -1;
+}
+
+PRInt64
+nsBufferedAudioStream::GetPositionInFrames()
+{
+  MonitorAutoLock mon(mMonitor);
+  return GetPositionInFramesUnlocked();
+}
+
+PRInt64
+nsBufferedAudioStream::GetPositionInFramesUnlocked()
+{
+  mMonitor.AssertCurrentThreadOwns();
+
+  if (!mCubebStream) {
+    return -1;
+  }
+
+  uint64_t position = 0;
+  if (cubeb_stream_get_position(mCubebStream, &position) != CUBEB_OK) {
+    return -1;
+  }
+
+  // Adjust the reported position by the number of silent frames written
+  // during stream underruns.
+  PRInt64 adjustedPosition = 0;
+  if (position >= mLostFrames) {
+    adjustedPosition = position - mLostFrames;
+  }
+  return adjustedPosition;
+}
+
+bool
+nsBufferedAudioStream::IsPaused()
+{
+  MonitorAutoLock mon(mMonitor);
+  return mState == STOPPED;
+}
+
+template<typename T>
+void
+SampleCopy(void* aDst, const PRUint8* aSrc, PRUint32 aSamples, double aVolume)
+{
+  const T* src = reinterpret_cast<const T*>(aSrc);
+  double scaled_volume = GetVolumeScale() * aVolume;
+  T* dst = static_cast<T*>(aDst);
+  for (PRUint32 i = 0; i < aSamples; ++i) {
+    dst[i] = T(src[i] * scaled_volume);
+  }
+}
+
+long
+nsBufferedAudioStream::DataCallback(void* aBuffer, long aFrames)
+{
+  MonitorAutoLock mon(mMonitor);
+  PRUint32 bytesWanted = aFrames * mBytesPerFrame;
+
+  // Adjust bytesWanted to fit what is available in mBuffer.
+  PRUint32 available = NS_MIN(bytesWanted, mBuffer.Length());
+  NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0, "Must copy complete frames");
+
+  // Copy each sample from mBuffer to aBuffer, adjusting the volume during the copy.
+  PRUint32 samplesToCopy = available / mBytesPerFrame * mChannels;
+  switch (mFormat) {
+  case FORMAT_S16_LE:
+    SampleCopy<PRInt16>(aBuffer, mBuffer.Elements(), samplesToCopy, mVolume);
+    break;
+  case FORMAT_FLOAT32:
+    SampleCopy<float>(aBuffer, mBuffer.Elements(), samplesToCopy, mVolume);
+    break;
+  default:
+    return -1;
+  }
+
+  // Remove copied data from the temporary audio buffer.
+  mBuffer.RemoveElementsAt(0, available);
+  NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Must copy complete frames");
+
+  // Notify any blocked Write() call that more space is available in mBuffer.
+  mon.NotifyAll();
+
+  // Calculate remaining bytes requested by caller.  If the stream is not
+  // draining an underrun has occurred, so fill the remaining buffer with
+  // silence.
+  bytesWanted -= available;
+  if (mState != DRAINING) {
+    memset(static_cast<PRUint8*>(aBuffer) + available, 0, bytesWanted);
+    mLostFrames += bytesWanted / mBytesPerFrame;
+    bytesWanted = 0;
+  }
+
+  return aFrames - (bytesWanted / mBytesPerFrame);
+}
+
+int
+nsBufferedAudioStream::StateCallback(cubeb_state aState)
+{
+  if (aState == CUBEB_STATE_DRAINED) {
+    MonitorAutoLock mon(mMonitor);
+    mState = DRAINED;
+    mon.NotifyAll();
+  }
+  return CUBEB_OK;
+}
+#endif
+
--- a/content/smil/nsSMILTimedElement.cpp
+++ b/content/smil/nsSMILTimedElement.cpp
@@ -228,17 +228,17 @@ const nsSMILMilestone nsSMILTimedElement
 // The thresholds at which point we start filtering intervals and instance times
 // indiscriminately.
 // See FilterIntervals and FilterInstanceTimes.
 const PRUint8 nsSMILTimedElement::sMaxNumIntervals = 20;
 const PRUint8 nsSMILTimedElement::sMaxNumInstanceTimes = 100;
 
 // Detect if we arrive in some sort of undetected recursive syncbase dependency
 // relationship
-const PRUint16 nsSMILTimedElement::sMaxUpdateIntervalRecursionDepth = 20;
+const PRUint8 nsSMILTimedElement::sMaxUpdateIntervalRecursionDepth = 20;
 
 //----------------------------------------------------------------------
 // Ctor, dtor
 
 nsSMILTimedElement::nsSMILTimedElement()
 :
   mAnimationElement(nsnull),
   mFillMode(FILL_REMOVE),
@@ -247,16 +247,17 @@ nsSMILTimedElement::nsSMILTimedElement()
   mClient(nsnull),
   mCurrentInterval(nsnull),
   mCurrentRepeatIteration(0),
   mPrevRegisteredMilestone(sMaxMilestone),
   mElementState(STATE_STARTUP),
   mSeekState(SEEK_NOT_SEEKING),
   mDeferIntervalUpdates(false),
   mDoDeferredUpdate(false),
+  mDeleteCount(0),
   mUpdateIntervalRecursionDepth(0)
 {
   mSimpleDur.SetIndefinite();
   mMin.SetMillis(0L);
   mMax.SetIndefinite();
   mTimeDependents.Init();
 }
 
@@ -1955,22 +1956,39 @@ nsSMILTimedElement::UpdateCurrentInterva
   // current interval and notify all our time dependents of the change.
   //
   // The disadvantage of deferring resolving the interval is that DOM calls to
   // to getStartTime will throw an INVALID_STATE_ERR exception until the
   // document timeline begins since the start time has not yet been resolved.
   if (mElementState == STATE_STARTUP)
     return;
 
+  // Although SMIL gives rules for detecting cycles in change notifications,
+  // some configurations can lead to create-delete-create-delete-etc. cycles
+  // which SMIL does not consider.
+  //
+  // In order to provide consistent behavior in such cases, we detect two
+  // deletes in a row and then refuse to create any further intervals. That is,
+  // we say the configuration is invalid.
+  if (mDeleteCount > 1) {
+    // When we update the delete count we also set the state to post active, so
+    // if we're not post active here then something other than
+    // UpdateCurrentInterval has updated the element state in between and all
+    // bets are off.
+    NS_ABORT_IF_FALSE(mElementState == STATE_POSTACTIVE,
+      "Expected to be in post-active state after performing double delete");
+    return;
+  }
+
   // Check that we aren't stuck in infinite recursion updating some syncbase
   // dependencies. Generally such situations should be detected in advance and
   // the chain broken in a sensible and predictable manner, so if we're hitting
   // this assertion we need to work out how to detect the case that's causing
   // it. In release builds, just bail out before we overflow the stack.
-  AutoRestore<PRUint16> depthRestorer(mUpdateIntervalRecursionDepth);
+  AutoRestore<PRUint8> depthRestorer(mUpdateIntervalRecursionDepth);
   if (++mUpdateIntervalRecursionDepth > sMaxUpdateIntervalRecursionDepth) {
     NS_ABORT_IF_FALSE(false,
         "Update current interval recursion depth exceeded threshold");
     return;
   }
 
   // If the interval is active the begin time is fixed.
   const nsSMILInstanceTime* beginTime = mElementState == STATE_ACTIVE
@@ -2021,16 +2039,18 @@ nsSMILTimedElement::UpdateCurrentInterva
       {
         mCurrentInterval->SetEnd(*mCurrentInterval->Begin());
         NotifyChangedInterval(mCurrentInterval, false, true);
       }
       // The transition to the postactive state will take place on the next
       // sample (along with firing end events, clearing intervals etc.)
       RegisterMilestone();
     } else if (mElementState == STATE_WAITING) {
+      AutoRestore<PRUint8> deleteCountRestorer(mDeleteCount);
+      ++mDeleteCount;
       mElementState = STATE_POSTACTIVE;
       ResetCurrentInterval();
     }
   }
 }
 
 void
 nsSMILTimedElement::SampleSimpleTime(nsSMILTime aActiveTime)
--- a/content/smil/nsSMILTimedElement.h
+++ b/content/smil/nsSMILTimedElement.h
@@ -620,18 +620,18 @@ protected:
     SEEK_BACKWARD_FROM_ACTIVE,
     SEEK_BACKWARD_FROM_INACTIVE
   };
   nsSMILSeekState                 mSeekState;
 
   // Used to batch updates to the timing model
   class AutoIntervalUpdateBatcher;
   bool mDeferIntervalUpdates;
-  bool mDoDeferredUpdate; // Set if an update to the current interval
-                                  // was requested while mDeferIntervalUpdates
-                                  // was set
+  bool mDoDeferredUpdate; // Set if an update to the current interval was
+                          // requested while mDeferIntervalUpdates was set
 
   // Recursion depth checking
-  PRUint16              mUpdateIntervalRecursionDepth;
-  static const PRUint16 sMaxUpdateIntervalRecursionDepth;
+  PRUint8              mDeleteCount;
+  PRUint8              mUpdateIntervalRecursionDepth;
+  static const PRUint8 sMaxUpdateIntervalRecursionDepth;
 };
 
 #endif // NS_SMILTIMEDELEMENT_H_
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -4922,17 +4922,17 @@ Convolve3x3(const PRUint8 *index, PRInt3
 }
 
 static void
 GenerateNormal(float *N, const PRUint8 *data, PRInt32 stride,
                PRInt32 surfaceWidth, PRInt32 surfaceHeight,
                PRInt32 x, PRInt32 y, float surfaceScale)
 {
   // See this for source of constants:
-  //   http://www.w3.org/TR/SVG11/filters.html#feDiffuseLighting
+  //   http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement
   static const PRInt8 Kx[3][3][3][3] =
     { { { {  0,  0,  0}, { 0, -2,  2}, { 0, -1,  1} },
         { {  0,  0,  0}, {-2,  0,  2}, {-1,  0,  1} },
         { {  0,  0,  0}, {-2,  2,  0}, {-1,  1,  0} } },
       { { {  0, -1,  1}, { 0, -2,  2}, { 0, -1,  1} },
         { { -1,  0,  1}, {-2,  0,  2}, {-1,  0,  1} },
         { { -1,  1,  0}, {-2,  2,  0}, {-1,  1,  0} } },
       { { {  0, -1,  1}, { 0, -2,  2}, { 0,  0,  0} },
--- a/docshell/shistory/src/nsSHEntry.cpp
+++ b/docshell/shistory/src/nsSHEntry.cpp
@@ -650,18 +650,26 @@ nsSHEntry::RemoveChild(nsISHEntry * aChi
   if (dynamic) {
     childRemoved = mChildren.RemoveObject(aChild);
   } else {
     PRInt32 index = mChildren.IndexOfObject(aChild);
     if (index >= 0) {
       childRemoved = mChildren.ReplaceObjectAt(nsnull, index);
     }
   }
-  if (childRemoved)
+  if (childRemoved) {
     aChild->SetParent(nsnull);
+
+    // reduce the child count, i.e. remove empty children at the end
+    for (PRInt32 i = mChildren.Count() - 1; i >= 0 && !mChildren[i]; --i) {
+      if (!mChildren.RemoveObjectAt(i)) {
+        break;
+      }
+    }
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHEntry::GetChildAt(PRInt32 aIndex, nsISHEntry ** aResult)
 {
   if (aIndex >= 0 && aIndex < mChildren.Count()) {
     *aResult = mChildren[aIndex];
new file mode 100644
--- /dev/null
+++ b/dom/base/crashtests/693811-1.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script>
+
+var collection = document.images;
+var other = document.embeds;
+var options = document.createElement("select").options;
+collection.toString;
+options.selectedIndex;
+Object.getPrototypeOf(collection).item = {};
+other.toString;
+collection.toString;
+options.selectedIndex;
+options.toString;
+</script>
new file mode 100644
--- /dev/null
+++ b/dom/base/crashtests/693811-2.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<script>
+
+var collection = document.images;
+var other = document.embeds;
+var options = document.createElement("select").options;
+collection.toString;
+options.selectedIndex;
+Object.defineProperty(Object.getPrototypeOf(collection),
+                      "item",
+                      { value: {}, enumerable: true, configurable: true });
+other.toString;
+collection.toString;
+options.selectedIndex;
+options.toString;
+</script>
new file mode 100644
--- /dev/null
+++ b/dom/base/crashtests/693811-3.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+document.createElement("p").constructor = function(){};
+</script>
--- a/dom/base/crashtests/crashtests.list
+++ b/dom/base/crashtests/crashtests.list
@@ -25,12 +25,15 @@ asserts(1-2) load 504224.html # bug 5640
 load 603531.html
 load 601247.html
 load 609560-1.xhtml
 load 612018-1.html
 load 637116.html
 load 666869.html
 load 675621-1.html
 load 693894.html
+load 693811-1.html
+load 693811-2.html
+load 693811-3.html
 load 695867.html
 load 697643.html
 load 706283-1.html
 load 708405-1.html
--- a/dom/base/nsDOMMemoryReporter.cpp
+++ b/dom/base/nsDOMMemoryReporter.cpp
@@ -83,24 +83,32 @@ AppendWindowURI(nsGlobalWindow *aWindow,
   // (such as about:memory) have to undo this change.
   spec.ReplaceChar('/', '\\');
 
   aStr += spec;
 
   return true;
 }
 
+struct WindowTotals
+{
+  WindowTotals() : mDom(0), mStyleSheets(0) {}
+  size_t mDom;
+  size_t mStyleSheets;
+};
+
+NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(WindowStyleSheetsMallocSizeOf,
+                                     "window/style-sheets")
+
 static void
-CollectWindowMemoryUsage(nsGlobalWindow *aWindow,
-                         nsIMemoryMultiReporterCallback *aCb,
-                         nsISupports *aClosure)
+CollectWindowReports(nsGlobalWindow *aWindow,
+                     WindowTotals *aWindowTotals,
+                     nsIMemoryMultiReporterCallback *aCb,
+                     nsISupports *aClosure)
 {
-  NS_NAMED_LITERAL_CSTRING(kWindowDesc,
-                           "Memory used by a window and the DOM within it.");
-
   // DOM window objects fall into one of three categories:
   // - "active" windows are currently either displayed in an active
   //   tab, or a child of such a window.
   // - "cached" windows are in the fastback cache.
   // - "other" windows are closed (or navigated away from w/o being
   //   cached) yet held alive by either a website or our code. The
   //   latter case may be a memory leak, but not necessarily.
   //
@@ -114,17 +122,17 @@ CollectWindowMemoryUsage(nsGlobalWindow 
   // For outer windows we simply group them all together and just show
   // the combined count and amount of memory used, which is generally
   // a constant amount per window (since all the actual data lives in
   // the inner window).
   //
   // The path we give to the reporter callback for inner windows are
   // as follows:
   //
-  //   explicit/dom/window-objects/<category>/top=<top-outer-id> (inner=<top-inner-id>)/inner-window(id=<id>, uri=<uri>)
+  //   explicit/dom+style/window-objects/<category>/top=<top-outer-id> (inner=<top-inner-id>)/inner-window(id=<id>, uri=<uri>)
   //
   // Where:
   // - <category> is active, cached, or other, as described above.
   // - <top-outer-id> is the window id (nsPIDOMWindow::WindowID()) of
   //   the top outer window (i.e. tab, or top level chrome window).
   // - <top-inner-id> is the window id of the top window's inner
   //   window.
   // - <id> is the window id of the inner window in question.
@@ -133,72 +141,92 @@ CollectWindowMemoryUsage(nsGlobalWindow 
   // Exposing the window ids is done to get logical grouping in
   // about:memory, and also for debuggability since one can get to the
   // nsGlobalWindow for a window id by calling the static method
   // nsGlobalWindow::GetInnerWindowWithId(id) (or
   // GetOuterWindowWithId(id) in a debugger.
   //
   // For outer windows we simply use:
   // 
-  //   explicit/dom/window-objects/<category>/outer-windows
+  //   explicit/dom+style/window-objects/<category>/outer-windows
   //
   // Which gives us simple counts of how many outer windows (and their
   // combined sizes) per category.
 
-  nsCAutoString str("explicit/dom/window-objects/");
+  nsCAutoString windowPath("explicit/dom+style/window-objects/");
 
   nsIDocShell *docShell = aWindow->GetDocShell();
 
   nsGlobalWindow *top = aWindow->GetTop();
-  PRInt64 windowSize = aWindow->SizeOf();
+  PRInt64 windowDOMSize = aWindow->SizeOf();
+  PRInt64 styleSheetsSize = aWindow->SizeOfStyleSheets(WindowStyleSheetsMallocSizeOf);
 
   if (docShell && aWindow->IsFrozen()) {
-    str += NS_LITERAL_CSTRING("cached/");
+    windowPath += NS_LITERAL_CSTRING("cached/");
   } else if (docShell) {
-    str += NS_LITERAL_CSTRING("active/");
+    windowPath += NS_LITERAL_CSTRING("active/");
   } else {
-    str += NS_LITERAL_CSTRING("other/");
+    windowPath += NS_LITERAL_CSTRING("other/");
   }
 
   if (aWindow->IsInnerWindow()) {
-    str += NS_LITERAL_CSTRING("top=");
+    windowPath += NS_LITERAL_CSTRING("top=");
 
     if (top) {
-      str.AppendInt(top->WindowID());
+      windowPath.AppendInt(top->WindowID());
 
       nsGlobalWindow *topInner = top->GetCurrentInnerWindowInternal();
       if (topInner) {
-        str += NS_LITERAL_CSTRING(" (inner=");
-        str.AppendInt(topInner->WindowID());
-        str += NS_LITERAL_CSTRING(")");
+        windowPath += NS_LITERAL_CSTRING(" (inner=");
+        windowPath.AppendInt(topInner->WindowID());
+        windowPath += NS_LITERAL_CSTRING(")");
       }
     } else {
-      str += NS_LITERAL_CSTRING("none");
+      windowPath += NS_LITERAL_CSTRING("none");
     }
 
-    str += NS_LITERAL_CSTRING("/inner-window(id=");
-    str.AppendInt(aWindow->WindowID());
-    str += NS_LITERAL_CSTRING(", uri=");
+    windowPath += NS_LITERAL_CSTRING("/inner-window(id=");
+    windowPath.AppendInt(aWindow->WindowID());
+    windowPath += NS_LITERAL_CSTRING(", uri=");
 
-    if (!AppendWindowURI(aWindow, str)) {
-      str += NS_LITERAL_CSTRING("[system]");
+    if (!AppendWindowURI(aWindow, windowPath)) {
+      windowPath += NS_LITERAL_CSTRING("[system]");
     }
 
-    str += NS_LITERAL_CSTRING(")");
+    windowPath += NS_LITERAL_CSTRING(")");
   } else {
     // Combine all outer windows per section (active/cached/other) as
     // they basically never contain anything of interest, and are
     // always pretty much the same size.
 
-    str += NS_LITERAL_CSTRING("outer-windows");
+    windowPath += NS_LITERAL_CSTRING("outer-windows");
   }
 
-  aCb->Callback(EmptyCString(), str, nsIMemoryReporter::KIND_HEAP,
-                nsIMemoryReporter::UNITS_BYTES, windowSize, kWindowDesc,
-                aClosure);
+  if (windowDOMSize > 0) {
+    nsCAutoString domPath(windowPath);
+    domPath += "/dom";
+    NS_NAMED_LITERAL_CSTRING(kWindowDesc,
+                             "Memory used by a window and the DOM within it.");
+    aCb->Callback(EmptyCString(), domPath, nsIMemoryReporter::KIND_HEAP,
+                  nsIMemoryReporter::UNITS_BYTES, windowDOMSize, kWindowDesc,
+                  aClosure);
+    aWindowTotals->mDom += windowDOMSize;
+  }
+
+  if (styleSheetsSize > 0) {
+    nsCAutoString styleSheetsPath(windowPath);
+    styleSheetsPath += "/style-sheets";
+    NS_NAMED_LITERAL_CSTRING(kStyleSheetsDesc,
+                             "Memory used by style sheets within a window.");
+    aCb->Callback(EmptyCString(), styleSheetsPath,
+                  nsIMemoryReporter::KIND_HEAP,
+                  nsIMemoryReporter::UNITS_BYTES, styleSheetsSize,
+                  kStyleSheetsDesc, aClosure);
+    aWindowTotals->mStyleSheets += styleSheetsSize;
+  }
 }
 
 typedef nsTArray< nsRefPtr<nsGlobalWindow> > WindowArray;
 
 static
 PLDHashOperator
 GetWindows(const PRUint64& aId, nsGlobalWindow*& aWindow, void* aClosure)
 {
@@ -218,20 +246,37 @@ nsDOMMemoryMultiReporter::CollectReports
   // Hold on to every window in memory so that window objects can't be
   // destroyed while we're calling the memory reporter callback.
   WindowArray windows;
   windowsById->Enumerate(GetWindows, &windows);
 
   // Collect window memory usage.
   nsRefPtr<nsGlobalWindow> *w = windows.Elements();
   nsRefPtr<nsGlobalWindow> *end = w + windows.Length();
+  WindowTotals windowTotals;
   for (; w != end; ++w) {
-    CollectWindowMemoryUsage(*w, aCb, aClosure);
+    CollectWindowReports(*w, &windowTotals, aCb, aClosure);
   }
 
+  NS_NAMED_LITERAL_CSTRING(kDomTotalWindowsDesc,
+    "Memory used for the DOM within windows.  This is the sum of all windows' "
+    "'dom' numbers.");
+  aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING("dom-total-window"),
+                nsIMemoryReporter::KIND_OTHER,
+                nsIMemoryReporter::UNITS_BYTES, windowTotals.mDom,
+                kDomTotalWindowsDesc, aClosure);
+
+  NS_NAMED_LITERAL_CSTRING(kLayoutTotalWindowStyleSheetsDesc,
+    "Memory used for style sheets within windows.  This is the sum of all windows' "
+    "'style-sheets' numbers.");
+  aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING("style-sheets-total-window"),
+                nsIMemoryReporter::KIND_OTHER,
+                nsIMemoryReporter::UNITS_BYTES, windowTotals.mStyleSheets,
+                kLayoutTotalWindowStyleSheetsDesc, aClosure);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMMemoryMultiReporter::GetExplicitNonHeap(PRInt64* aAmount)
 {
   // This reporter only measures heap memory.
   *aAmount = 0;
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -812,17 +812,17 @@ nsDOMWindowUtils::GarbageCollect(nsICycl
   SAMPLE_LABEL("GC", "GarbageCollect");
   // Always permit this in debug builds.
 #ifndef DEBUG
   if (!IsUniversalXPConnectCapable()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 #endif
 
-  nsJSContext::GarbageCollectNow(js::gcreason::DOM_UTILS);
+  nsJSContext::GarbageCollectNow(js::gcreason::DOM_UTILS, nsGCNormal, true);
   nsJSContext::CycleCollectNow(aListener, aExtraForgetSkippableCalls);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::CycleCollect(nsICycleCollectorListener *aListener,
                                PRInt32 aExtraForgetSkippableCalls)
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -10291,16 +10291,26 @@ nsGlobalWindow::SizeOf() const
     }
   }
 
   size += mNavigator ? mNavigator->SizeOf() : 0;
 
   return size;
 }
 
+size_t
+nsGlobalWindow::SizeOfStyleSheets(nsMallocSizeOfFun aMallocSizeOf) const
+{
+  size_t n = 0;
+  if (IsInnerWindow() && mDoc) {
+    n += mDoc->SizeOfStyleSheets(aMallocSizeOf);
+  }
+  return n;
+}
+
 // nsGlobalChromeWindow implementation
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGlobalChromeWindow,
                                                   nsGlobalWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mBrowserDOMWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mMessageManager)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -571,16 +571,17 @@ public:
 
   static bool HasPerformanceSupport();
 
   static WindowByIdTable* GetWindowsTable() {
     return sWindowsById;
   }
 
   PRInt64 SizeOf() const;
+  size_t SizeOfStyleSheets(nsMallocSizeOfFun aMallocSizeOf) const;
 
   void UnmarkGrayTimers();
 private:
   // Enable updates for the accelerometer.
   void EnableDeviceMotionUpdates();
 
   // Disables updates for the accelerometer.
   void DisableDeviceMotionUpdates();
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -130,29 +130,32 @@ static PRLogModuleInfo* gJSDiagnostics;
 #define NS_GC_DELAY                 4000 // ms
 
 #define NS_SHRINK_GC_BUFFERS_DELAY  4000 // ms
 
 // The amount of time we wait from the first request to GC to actually
 // doing the first GC.
 #define NS_FIRST_GC_DELAY           10000 // ms
 
+#define NS_FULL_GC_DELAY            60000 // ms
+
 // The amount of time we wait between a request to CC (after GC ran)
 // and doing the actual CC.
 #define NS_CC_DELAY                 5000 // ms
 
 #define NS_CC_SKIPPABLE_DELAY       250 // ms
 
 #define NS_CC_FORCED                (5 * 60 * PR_USEC_PER_SEC) // 5 min
 
 #define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT
 
 // if you add statics here, add them to the list in nsJSRuntime::Startup
 
 static nsITimer *sGCTimer;
+static nsITimer *sFullGCTimer;
 static nsITimer *sShrinkGCBuffersTimer;
 static nsITimer *sCCTimer;
 
 static PRTime sLastCCEndTime;
 
 static bool sGCHasRun;
 
 // The number of currently pending document loads. This count isn't
@@ -161,26 +164,33 @@ static bool sGCHasRun;
 // all cases. This counter also gets reset if we end up GC'ing while
 // we're waiting for a slow page to load. IOW, this count may be 0
 // even when there are pending loads.
 static PRUint32 sPendingLoadCount;
 static bool sLoadingInProgress;
 
 static PRUint32 sCCollectedWaitingForGC;
 static bool sPostGCEventsToConsole;
+static bool sDisableExplicitCompartmentGC;
 static PRUint32 sCCTimerFireCount = 0;
 static PRUint32 sMinForgetSkippableTime = PR_UINT32_MAX;
 static PRUint32 sMaxForgetSkippableTime = 0;
 static PRUint32 sTotalForgetSkippableTime = 0;
 static PRUint32 sRemovedPurples = 0;
 static PRUint32 sForgetSkippableBeforeCC = 0;
 static PRUint32 sPreviousSuspectedCount = 0;
+static PRUint32 sCompartmentGCCount = 0;
+static nsJSContext* sTopEvaluator = nsnull;
+static bool sContextDeleted = false;
+static bool sPreviousWasChromeCompGC = false;
 
 static bool sCleanupSinceLastGC = true;
 
+PRUint32 nsJSContext::sGlobalGCEpoch = 0;
+
 nsScriptNameSpaceManager *gNameSpaceManager;
 
 static nsIJSRuntimeService *sRuntimeService;
 JSRuntime *nsJSRuntime::sRuntime;
 
 static const char kJSRuntimeServiceContractID[] =
   "@mozilla.org/js/xpc/RuntimeService;1";
 
@@ -211,17 +221,18 @@ public:
 
 NS_IMPL_ISUPPORTS1(nsMemoryPressureObserver, nsIObserver)
 
 NS_IMETHODIMP
 nsMemoryPressureObserver::Observe(nsISupports* aSubject, const char* aTopic,
                                   const PRUnichar* aData)
 {
   if (sGCOnMemoryPressure) {
-    nsJSContext::GarbageCollectNow(js::gcreason::MEM_PRESSURE, nsGCShrinking);
+    nsJSContext::GarbageCollectNow(js::gcreason::MEM_PRESSURE, nsGCShrinking,
+                                   true);
     nsJSContext::CycleCollectNow();
   }
   return NS_OK;
 }
 
 class nsRootedJSValueArray {
 public:
   explicit nsRootedJSValueArray(JSContext *cx) : avr(cx, vals.Length(), vals.Elements()) {}
@@ -933,25 +944,29 @@ static const char js_zeal_compartment_st
 static const char js_methodjit_content_str[]  = JS_OPTIONS_DOT_STR "methodjit.content";
 static const char js_methodjit_chrome_str[]   = JS_OPTIONS_DOT_STR "methodjit.chrome";
 static const char js_methodjit_always_str[]   = JS_OPTIONS_DOT_STR "methodjit_always";
 static const char js_typeinfer_str[]          = JS_OPTIONS_DOT_STR "typeinference";
 static const char js_pccounts_content_str[]   = JS_OPTIONS_DOT_STR "pccounts.content";
 static const char js_pccounts_chrome_str[]    = JS_OPTIONS_DOT_STR "pccounts.chrome";
 static const char js_jit_hardening_str[]      = JS_OPTIONS_DOT_STR "jit_hardening";
 static const char js_memlog_option_str[] = JS_OPTIONS_DOT_STR "mem.log";
+static const char js_disable_explicit_compartment_gc[] =
+  JS_OPTIONS_DOT_STR "disable_explicit_compartment_gc";
 
 int
 nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
 {
   nsJSContext *context = reinterpret_cast<nsJSContext *>(data);
   PRUint32 oldDefaultJSOptions = context->mDefaultJSOptions;
   PRUint32 newDefaultJSOptions = oldDefaultJSOptions;
 
   sPostGCEventsToConsole = Preferences::GetBool(js_memlog_option_str);
+  sDisableExplicitCompartmentGC =
+    Preferences::GetBool(js_disable_explicit_compartment_gc);
 
   bool strict = Preferences::GetBool(js_strict_option_str);
   if (strict)
     newDefaultJSOptions |= JSOPTION_STRICT;
   else
     newDefaultJSOptions &= ~JSOPTION_STRICT;
 
   nsIScriptGlobalObject *global = context->GetGlobalObject();
@@ -1043,16 +1058,19 @@ nsJSContext::JSOptionChangedCallback(con
     ::JS_SetGCZeal(context->mContext, (PRUint8)zeal, frequency, compartment);
 #endif
 
   return 0;
 }
 
 nsJSContext::nsJSContext(JSRuntime *aRuntime)
   : mGCOnDestruction(true),
+    mChromeComp(false),
+    mGlobalGCEpoch(0),
+    mEvaluationCount(0),
     mExecuteDepth(0)
 {
 
   ++sContextCount;
 
   mDefaultJSOptions = JSOPTION_PRIVATE_IS_NSISUPPORTS;
 
   mContext = ::JS_NewContext(aRuntime, gStackSize);
@@ -1093,16 +1111,19 @@ nsJSContext::~nsJSContext()
   // parameters, but don't execute the functions (see bug 622326).
   delete mTerminations;
 
   mGlobalObjectRef = nsnull;
 
   DestroyJSContext();
 
   --sContextCount;
+  if (sTopEvaluator == this) {
+    sTopEvaluator = nsnull;
+  }
 
   if (!sContextCount && sDidShutdown) {
     // The last context is being deleted, and we're already in the
     // process of shutting down, release the JS runtime service, and
     // the security manager.
 
     NS_IF_RELEASE(sRuntimeService);
     NS_IF_RELEASE(sSecurityManager);
@@ -1119,16 +1140,17 @@ nsJSContext::DestroyJSContext()
   // Clear our entry in the JSContext, bugzilla bug 66413
   ::JS_SetContextPrivate(mContext, nsnull);
 
   // Unregister our "javascript.options.*" pref-changed callback.
   Preferences::UnregisterCallback(JSOptionChangedCallback,
                                   js_options_dot_str, this);
 
   if (mGCOnDestruction) {
+    sContextDeleted = true;
     PokeGC(js::gcreason::NSJSCONTEXT_DESTROY);
   }
         
   // Let xpconnect destroy the JSContext when it thinks the time is right.
   nsIXPConnect *xpc = nsContentUtils::XPConnect();
   if (xpc) {
     xpc->ReleaseJSContext(mContext, true);
   } else {
@@ -2232,16 +2254,17 @@ nsresult
 nsJSContext::CreateNativeGlobalForInner(
                                 nsIScriptGlobalObject *aNewInner,
                                 bool aIsChrome,
                                 nsIPrincipal *aPrincipal,
                                 JSObject** aNativeGlobal, nsISupports **aHolder)
 {
   nsIXPConnect *xpc = nsContentUtils::XPConnect();
   PRUint32 flags = aIsChrome? nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT : 0;
+  mChromeComp = aIsChrome;
 
   nsCOMPtr<nsIPrincipal> systemPrincipal;
   if (aIsChrome) {
     nsIScriptSecurityManager *ssm = nsContentUtils::GetSecurityManager();
     ssm->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
   }
 
   nsRefPtr<nsIXPConnectJSObjectHolder> jsholder;
@@ -3154,16 +3177,22 @@ nsJSContext::ScriptEvaluated(bool aTermi
   JS_MaybeGC(mContext);
 
   // Be careful to not reset the operation callback if some outer script is
   // still running. This would allow a script to bypass the slow script check
   // simply by invoking nested scripts (e.g., through a plugin).
   if (aTerminated && mExecuteDepth == 0 && !JS_IsRunning(mContext)) {
     mOperationCallbackTime = 0;
     mModalStateTime = 0;
+
+    IncreaseEvaluationCount(this);
+    if (EvaluationCount(sTopEvaluator) < EvaluationCount(this) &&
+        (!mChromeComp || !sPreviousWasChromeCompGC)) {
+      sTopEvaluator = this;
+    }
   }
 }
 
 nsresult
 nsJSContext::SetTerminationFunction(nsScriptTerminationFunc aFunc,
                                     nsISupports* aRef)
 {
   NS_PRECONDITION(GetExecutingScript(), "should be executing script");
@@ -3226,38 +3255,84 @@ nsJSContext::SetGCOnDestruction(bool aGC
 NS_IMETHODIMP
 nsJSContext::ScriptExecuted()
 {
   ScriptEvaluated(!::JS_IsRunning(mContext));
 
   return NS_OK;
 }
 
+void
+FullGCTimerFired(nsITimer* aTimer, void* aClosure)
+{
+  NS_RELEASE(sFullGCTimer);
+
+  uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
+  nsJSContext::GarbageCollectNow(static_cast<js::gcreason::Reason>(reason),
+                                 nsGCNormal, true);
+}
+
 //static
 void
-nsJSContext::GarbageCollectNow(js::gcreason::Reason reason, PRUint32 gckind)
+nsJSContext::GarbageCollectNow(js::gcreason::Reason aReason, PRUint32 aGckind,
+                               bool aGlobal)
 {
   NS_TIME_FUNCTION_MIN(1.0);
   SAMPLE_LABEL("GC", "GarbageCollectNow");
 
   KillGCTimer();
   KillShrinkGCBuffersTimer();
 
   // Reset sPendingLoadCount in case the timer that fired was a
   // timer we scheduled due to a normal GC timer firing while
   // documents were loading. If this happens we're waiting for a
   // document that is taking a long time to load, and we effectively
   // ignore the fact that the currently loading documents are still
   // loading and move on as if they weren't.
   sPendingLoadCount = 0;
   sLoadingInProgress = false;
 
-  if (nsContentUtils::XPConnect()) {
-    nsContentUtils::XPConnect()->GarbageCollect(reason, gckind);
+  if (!nsContentUtils::XPConnect()) {
+    return;
   }
+  
+  // Use compartment GC when we're not asked to do a shrinking GC nor
+  // global GC and compartment GC has been called less than 10 times after
+  // the previous global GC. If a top level browsing context has been
+  // deleted, we do a global GC.
+  if (!sDisableExplicitCompartmentGC &&
+      aGckind != nsGCShrinking && !aGlobal &&
+      EvaluationCount(sTopEvaluator) > 0 &&
+      !sContextDeleted && sCompartmentGCCount < 10) {
+    nsJSContext* top = sTopEvaluator;
+    sTopEvaluator = nsnull;
+    ResetEvaluationCount(top);
+    JSContext* cx = top->GetNativeContext();
+    if (cx) {
+      JSObject* global = top->GetNativeGlobal();
+      if (global) {
+        if (!sFullGCTimer) {
+          CallCreateInstance("@mozilla.org/timer;1", &sFullGCTimer);
+        }
+        if (sFullGCTimer) {
+          sFullGCTimer->Cancel();
+          js::gcreason::Reason reason = js::gcreason::FULL_GC_TIMER;
+          sFullGCTimer->InitWithFuncCallback(FullGCTimerFired,
+                                             reinterpret_cast<void *>(reason),
+                                             NS_FULL_GC_DELAY,
+                                             nsITimer::TYPE_ONE_SHOT);
+        }
+        JSCompartment* comp = js::GetObjectCompartment(global);
+        js::CompartmentGCForReason(cx, comp, aReason);
+        return;
+      }
+    }
+  }
+
+  nsContentUtils::XPConnect()->GarbageCollect(aReason, aGckind);
 }
 
 //static
 void
 nsJSContext::ShrinkGCBuffersNow()
 {
   NS_TIME_FUNCTION_MIN(1.0);
   SAMPLE_LABEL("GC", "ShrinkGCBuffersNow");
@@ -3351,17 +3426,18 @@ nsJSContext::CycleCollectNow(nsICycleCol
 
 // static
 void
 GCTimerFired(nsITimer *aTimer, void *aClosure)
 {
   NS_RELEASE(sGCTimer);
 
   uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
-  nsJSContext::GarbageCollectNow(static_cast<js::gcreason::Reason>(reason), nsGCNormal);
+  nsJSContext::GarbageCollectNow(static_cast<js::gcreason::Reason>(reason),
+                                 nsGCNormal, false);
 }
 
 void
 ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure)
 {
   NS_RELEASE(sShrinkGCBuffersTimer);
 
   nsJSContext::ShrinkGCBuffersNow();
@@ -3519,16 +3595,26 @@ nsJSContext::KillGCTimer()
 {
   if (sGCTimer) {
     sGCTimer->Cancel();
 
     NS_RELEASE(sGCTimer);
   }
 }
 
+void
+nsJSContext::KillFullGCTimer()
+{
+  if (sFullGCTimer) {
+    sFullGCTimer->Cancel();
+
+    NS_RELEASE(sFullGCTimer);
+  }
+}
+
 //static
 void
 nsJSContext::KillShrinkGCBuffersTimer()
 {
   if (sShrinkGCBuffersTimer) {
     sShrinkGCBuffersTimer->Cancel();
 
     NS_RELEASE(sShrinkGCBuffersTimer);
@@ -3547,18 +3633,18 @@ nsJSContext::KillCCTimer()
 }
 
 void
 nsJSContext::GC(js::gcreason::Reason aReason)
 {
   PokeGC(aReason);
 }
 
-static void
-DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status)
+void
+nsJSContext::DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status)
 {
   NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
 
   if (sPostGCEventsToConsole) {
     PRTime now = PR_Now();
     PRTime delta = 0;
     if (sFirstCollectionTime) {
       delta = now - sFirstCollectionTime;
@@ -3574,16 +3660,29 @@ DOMGCFinishedCallback(JSRuntime *rt, JSC
     if (cs) {
       cs->LogStringMessage(msg.get());
     }
   }
 
   sCCollectedWaitingForGC = 0;
   sCleanupSinceLastGC = false;
 
+  if (!comp) {
+    sPreviousWasChromeCompGC = false;
+    sContextDeleted = false;
+    sCompartmentGCCount = 0;
+    ++sGlobalGCEpoch;
+    KillFullGCTimer();
+  } else {
+    // Only every other compartment GC is allowed to collect a chrome
+    // compartment. Otherwise we'd collect chrome compartment all the time.
+    sPreviousWasChromeCompGC = xpc::AccessCheck::isChrome(comp);
+    ++sCompartmentGCCount;
+  }
+
   if (sGCTimer) {
     // If we were waiting for a GC to happen, kill the timer.
     nsJSContext::KillGCTimer();
 
     // If this is a compartment GC, restart it. We still want
     // a full GC to happen. Compartment GCs usually happen as a
     // result of last-ditch or MaybeGC. In both cases its
     // probably a time of heavy activity and we want to delay
@@ -3694,23 +3793,24 @@ nsJSRuntime::ParseVersion(const nsString
     return NS_OK;
 }
 
 //static
 void
 nsJSRuntime::Startup()
 {
   // initialize all our statics, so that we can restart XPCOM
-  sGCTimer = sCCTimer = nsnull;
+  sGCTimer = sFullGCTimer = sCCTimer = nsnull;
   sGCHasRun = false;
   sLastCCEndTime = 0;
   sPendingLoadCount = 0;
   sLoadingInProgress = false;
   sCCollectedWaitingForGC = 0;
   sPostGCEventsToConsole = false;
+  sDisableExplicitCompartmentGC = false;
   gNameSpaceManager = nsnull;
   sRuntimeService = nsnull;
   sRuntime = nsnull;
   sIsInitialized = false;
   sDidShutdown = false;
   sContextCount = 0;
   sSecurityManager = nsnull;
 }
@@ -3857,17 +3957,17 @@ nsJSRuntime::Init()
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = sRuntimeService->GetRuntime(&sRuntime);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Let's make sure that our main thread is the same as the xpcom main thread.
   NS_ASSERTION(NS_IsMainThread(), "bad");
 
-  ::JS_SetGCFinishedCallback(sRuntime, DOMGCFinishedCallback);
+  ::JS_SetGCFinishedCallback(sRuntime, nsJSContext::DOMGCFinishedCallback);
 
   JSSecurityCallbacks *callbacks = JS_GetRuntimeSecurityCallbacks(sRuntime);
   NS_ASSERTION(callbacks, "SecMan should have set security callbacks!");
 
   callbacks->findObjectPrincipals = ObjectPrincipalFinder;
 
   // Set up the structured clone callbacks.
   static JSStructuredCloneCallbacks cloneCallbacks = {
@@ -3942,16 +4042,17 @@ nsJSRuntime::GetNameSpaceManager()
   return gNameSpaceManager;
 }
 
 /* static */
 void
 nsJSRuntime::Shutdown()
 {
   nsJSContext::KillGCTimer();
+  nsJSContext::KillFullGCTimer();
   nsJSContext::KillShrinkGCBuffersTimer();
   nsJSContext::KillCCTimer();
 
   NS_IF_RELEASE(gNameSpaceManager);
 
   if (!sContextCount) {
     // We're being shutdown, and there are no more contexts
     // alive, release the JS runtime service and the security manager.
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -176,25 +176,28 @@ public:
   virtual void EnterModalState();
   virtual void LeaveModalState();
 
   NS_DECL_NSIXPCSCRIPTNOTIFY
 
   static void LoadStart();
   static void LoadEnd();
 
-  static void GarbageCollectNow(js::gcreason::Reason reason, PRUint32 gckind = nsGCNormal);
+  static void GarbageCollectNow(js::gcreason::Reason aReason,
+                                PRUint32 aGckind,
+                                bool aGlobal);
   static void ShrinkGCBuffersNow();
   // If aExtraForgetSkippableCalls is -1, forgetSkippable won't be
   // called even if the previous collection was GC.
   static void CycleCollectNow(nsICycleCollectorListener *aListener = nsnull,
                               PRInt32 aExtraForgetSkippableCalls = 0);
 
   static void PokeGC(js::gcreason::Reason aReason);
   static void KillGCTimer();
+  static void KillFullGCTimer();
 
   static void PokeShrinkGCBuffers();
   static void KillShrinkGCBuffersTimer();
 
   static void MaybePokeCC();
   static void KillCCTimer();
 
   virtual void GC(js::gcreason::Reason aReason);
@@ -203,16 +206,39 @@ public:
 
   nsIScriptGlobalObject* GetCachedGlobalObject()
   {
     // Verify that we have a global so that this
     // does always return a null when GetGlobalObject() is null.
     JSObject* global = JS_GetGlobalObject(mContext);
     return global ? mGlobalObjectRef.get() : nsnull;
   }
+  
+  static PRUint32 EvaluationCount(nsJSContext* aCx)
+  {
+    return aCx && aCx->mGlobalGCEpoch == sGlobalGCEpoch ?
+      aCx->mEvaluationCount : 0;
+  }
+
+  static void IncreaseEvaluationCount(nsJSContext* aCx)
+  {
+    if (aCx->mGlobalGCEpoch != sGlobalGCEpoch) {
+      aCx->mEvaluationCount = 0;
+      aCx->mGlobalGCEpoch = sGlobalGCEpoch;
+    }
+    ++(aCx->mEvaluationCount);
+  }
+  
+  static void ResetEvaluationCount(nsJSContext* aCx)
+  {
+    aCx->mEvaluationCount = 0;
+  }
+  
+  static void DOMGCFinishedCallback(JSRuntime* aRt, JSCompartment* aComp,
+                                    const char* aStatus);
 protected:
   nsresult InitializeExternalClasses();
 
   // Helper to convert xpcom datatypes to jsvals.
   nsresult ConvertSupportsTojsvals(nsISupports *aArgs,
                                    JSObject *aScope,
                                    PRUint32 *aArgc,
                                    jsval **aArgv,
@@ -291,28 +317,32 @@ protected:
   
   TerminationFuncClosure* mTerminations;
 
 private:
   bool mIsInitialized;
   bool mScriptsEnabled;
   bool mGCOnDestruction;
   bool mProcessingScriptTag;
-
+  bool mChromeComp;
+  PRUint32 mGlobalGCEpoch;
+  PRUint32 mEvaluationCount;
   PRUint32 mExecuteDepth;
   PRUint32 mDefaultJSOptions;
   PRTime mOperationCallbackTime;
 
   PRTime mModalStateTime;
   PRUint32 mModalStateDepth;
 
   // mGlobalObjectRef ensures that the outer window stays alive as long as the
   // context does. It is eventually collected by the cycle collector.
   nsCOMPtr<nsIScriptGlobalObject> mGlobalObjectRef;
 
+  static PRUint32 sGlobalGCEpoch;
+
   static int JSOptionChangedCallback(const char *pref, void *data);
 
   static JSBool DOMOperationCallback(JSContext *cx);
 };
 
 class nsIJSRuntimeService;
 
 class nsJSRuntime : public nsIScriptRuntime
--- a/dom/interfaces/html/nsIDOMHTMLCanvasElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLCanvasElement.idl
@@ -49,16 +49,17 @@
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#graphics
  *
  * @status UNDER_DEVELOPMENT
  */
 
 interface nsIDOMFile;
 interface nsIVariant;
+interface nsIInputStreamCallback;
 
 [scriptable, uuid(8cddbc86-f384-40ac-835b-fe3e00630cad)]
 interface nsIDOMHTMLCanvasElement : nsIDOMHTMLElement
 {
   attribute unsigned long width;
   attribute unsigned long height;
   attribute boolean mozOpaque;
 
@@ -77,10 +78,15 @@ interface nsIDOMHTMLCanvasElement : nsID
   // mozGetAsFile(name);              -- defaults to image/png
   // mozGetAsFile(name, type);        -- uses given type
   [optional_argc] nsIDOMFile mozGetAsFile(in DOMString name,
                                           [optional] in DOMString type);
 
   // A Mozilla-only extension to get a canvas context backed by double-buffered
   // shared memory. Only privileged callers can call this.
   nsISupports MozGetIPCContext(in DOMString contextId);
+
+  // A Mozilla-only extension that returns the canvas' image data as a data
+  // stream in the desired image format.
+  void mozFetchAsStream(in nsIInputStreamCallback callback,
+                                        [optional] in DOMString type);
 };
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -795,24 +795,24 @@ ContentChild::GetIndexedDBPath()
     }
 
     return *gIndexedDBPath;
 }
 
 bool
 ContentChild::RecvGarbageCollect()
 {
-    nsJSContext::GarbageCollectNow(js::gcreason::DOM_IPC);
+    nsJSContext::GarbageCollectNow(js::gcreason::DOM_IPC, nsGCNormal, true);
     return true;
 }
 
 bool
 ContentChild::RecvCycleCollect()
 {
-    nsJSContext::GarbageCollectNow(js::gcreason::DOM_IPC);
+    nsJSContext::GarbageCollectNow(js::gcreason::DOM_IPC, nsGCNormal, true);
     nsJSContext::CycleCollectNow();
     return true;
 }
 
 bool
 ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID)
 {
     mAppInfo.version.Assign(version);
--- a/dom/plugins/base/Makefile.in
+++ b/dom/plugins/base/Makefile.in
@@ -133,16 +133,17 @@ endif
 endif
 
 ifdef MOZ_JAVA_COMPOSITOR
 DEFINES += -DMOZ_JAVA_COMPOSITOR
 endif
 
 LOCAL_INCLUDES += \
   -DSK_BUILD_FOR_ANDROID_NDK \
+  -I$(topsrcdir)/widget/android \
   -I$(topsrcdir)/xpcom/base/ \
   -I$(topsrcdir)/gfx/skia/include/core \
   -I$(topsrcdir)/gfx/skia/include/config \
   $(MOZ_CAIRO_CFLAGS) \
   $(NULL)
 
 include $(topsrcdir)/dom/dom-config.mk
 include $(topsrcdir)/config/config.mk
--- a/dom/plugins/base/android/ANPAudio.cpp
+++ b/dom/plugins/base/android/ANPAudio.cpp
@@ -301,17 +301,16 @@ void
 anp_audio_start(ANPAudioTrack* s)
 {
   if (s == NULL || s->output_unit == NULL) {
     return;
   }
   
   if (s->keepGoing) {
     // we are already playing.  Ignore.
-    LOG("anp_audio_start called twice!");
     return;
   }
 
   JNIEnv *jenv = GetJNIForThread();
   if (!jenv)
     return;
 
   jenv->CallVoidMethod(s->output_unit, at.play);
@@ -354,17 +353,35 @@ anp_audio_stop(ANPAudioTrack* s)
 }
 
 bool
 anp_audio_isStopped(ANPAudioTrack* s)
 {
   return s->isStopped;
 }
 
-void InitAudioTrackInterface(ANPAudioTrackInterfaceV0 *i) {
+uint32_t
+anp_audio_trackLatency(ANPAudioTrack* s) {
+  // Bug 721835
+  NOT_IMPLEMENTED();
+  return 1;
+}
+
+void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i) {
   _assert(i->inSize == sizeof(*i));
   ASSIGN(i, newTrack);
   ASSIGN(i, deleteTrack);
   ASSIGN(i, start);
   ASSIGN(i, pause);
   ASSIGN(i, stop);
   ASSIGN(i, isStopped);
 }
+
+void InitAudioTrackInterfaceV1(ANPAudioTrackInterfaceV1 *i) {
+  _assert(i->inSize == sizeof(*i));
+  ASSIGN(i, newTrack);
+  ASSIGN(i, deleteTrack);
+  ASSIGN(i, start);
+  ASSIGN(i, pause);
+  ASSIGN(i, stop);
+  ASSIGN(i, isStopped);
+  ASSIGN(i, trackLatency);
+}
--- a/dom/plugins/base/android/ANPBase.h
+++ b/dom/plugins/base/android/ANPBase.h
@@ -31,37 +31,46 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#include <stdlib.h>
 #include "android_npapi.h"
-#include <stdlib.h>
 #include "nsAutoPtr.h"
 #include "nsISupportsImpl.h"
 
 #define NOT_IMPLEMENTED_FATAL() do {                                    \
     __android_log_print(ANDROID_LOG_ERROR, "GeckoPlugins",              \
                         "%s not implemented %s, %d",                    \
                         __PRETTY_FUNCTION__, __FILE__, __LINE__);       \
     abort();                                                            \
   } while(0)
 
 #define NOT_IMPLEMENTED()                                               \
     __android_log_print(ANDROID_LOG_ERROR, "GeckoPlugins",              \
                         "!!!!!!!!!!!!!!  %s not implemented %s, %d",    \
                         __PRETTY_FUNCTION__, __FILE__, __LINE__);       \
 
-void InitAudioTrackInterface(ANPAudioTrackInterfaceV0 *i);
+void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i);
+void InitAudioTrackInterfaceV1(ANPAudioTrackInterfaceV1* i);
 void InitBitmapInterface(ANPBitmapInterfaceV0 *i);
 void InitCanvasInterface(ANPCanvasInterfaceV0 *i);
 void InitEventInterface(ANPEventInterfaceV0 *i);
 void InitLogInterface(ANPLogInterfaceV0 *i);
 void InitMatrixInterface(ANPMatrixInterfaceV0 *i);
 void InitPaintInterface(ANPPaintInterfaceV0 *i);
 void InitPathInterface(ANPPathInterfaceV0 *i);
 void InitSurfaceInterface(ANPSurfaceInterfaceV0 *i);
 void InitSystemInterface(ANPSystemInterfaceV0 *i);
+void InitSystemInterfaceV1(ANPSystemInterfaceV1 *i);
+void InitSystemInterfaceV2(ANPSystemInterfaceV2 *i);
 void InitTypeFaceInterface(ANPTypefaceInterfaceV0 *i);
 void InitWindowInterface(ANPWindowInterfaceV0 *i);
+void InitWindowInterfaceV1(ANPWindowInterfaceV1 *i);
+void InitWindowInterfaceV2(ANPWindowInterfaceV2 *i);
+void InitVideoInterfaceV0(ANPVideoInterfaceV0 *i);
+void InitVideoInterfaceV1(ANPVideoInterfaceV1 *i);
+void InitOpenGLInterface(ANPOpenGLInterfaceV0 *i);
+void InitNativeWindowInterface(ANPNativeWindowInterfaceV0 *i);
new file mode 100644
--- /dev/null
+++ b/dom/plugins/base/android/ANPNativeWindow.cpp
@@ -0,0 +1,85 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Android NPAPI support code
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   James Willcox <jwillcox@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the 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 ***** */
+
+// must include config.h first for webkit to fiddle with new/delete
+#include <android/log.h>
+#include "AndroidBridge.h"
+#include "AndroidMediaLayer.h"
+#include "ANPBase.h"
+#include "nsIPluginInstanceOwner.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsNPAPIPluginInstance.h"
+#include "gfxRect.h"
+
+using namespace mozilla;
+using namespace mozilla;
+
+#define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
+#define ASSIGN(obj, name)   (obj)->name = anp_native_window_##name
+
+static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) {
+  nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+
+  nsPluginInstanceOwner* owner;
+  if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) {
+    return NULL;
+  }
+
+
+  ANPNativeWindow window = owner->Layer()->GetNativeWindowForContent();
+  owner->Invalidate();
+
+  return window;
+}
+
+static void anp_native_window_invertPluginContent(NPP instance, bool isContentInverted) {
+  nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+
+  nsPluginInstanceOwner* owner;
+  if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) {
+    return;
+  }
+
+  owner->Layer()->SetInverted(isContentInverted);
+}
+
+
+void InitNativeWindowInterface(ANPNativeWindowInterfaceV0* i) {
+    ASSIGN(i, acquireNativeWindow);
+    ASSIGN(i, invertPluginContent);
+}
new file mode 100644
--- /dev/null
+++ b/dom/plugins/base/android/ANPOpenGL.cpp
@@ -0,0 +1,65 @@
+/* The Original Code is Android NPAPI support code
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   James Willcox <jwillcox@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the 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 <dlfcn.h>
+#include <android/log.h>
+#include "AndroidBridge.h"
+#include "ANPBase.h"
+#include "GLContextProvider.h"
+#include "nsNPAPIPluginInstance.h"
+
+#define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
+#define ASSIGN(obj, name)   (obj)->name = anp_opengl_##name
+
+using namespace mozilla;
+using namespace mozilla::gl;
+
+static ANPEGLContext anp_opengl_acquireContext(NPP inst) {
+    // Bug 687267
+    NOT_IMPLEMENTED();
+    return NULL;
+}
+
+static ANPTextureInfo anp_opengl_lockTexture(NPP instance) {
+    ANPTextureInfo info = { 0, 0, 0, 0 };
+    NOT_IMPLEMENTED();
+    return info;
+}
+
+static void anp_opengl_releaseTexture(NPP instance, const ANPTextureInfo* info) {
+    NOT_IMPLEMENTED();
+}
+
+static void anp_opengl_invertPluginContent(NPP instance, bool isContentInverted) {
+    NOT_IMPLEMENTED();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void InitOpenGLInterface(ANPOpenGLInterfaceV0* i) {
+    ASSIGN(i, acquireContext);
+    ASSIGN(i, lockTexture);
+    ASSIGN(i, releaseTexture);
+    ASSIGN(i, invertPluginContent);
+}
--- a/dom/plugins/base/android/ANPSystem.cpp
+++ b/dom/plugins/base/android/ANPSystem.cpp
@@ -57,16 +57,22 @@ anp_system_getApplicationDataDirectory()
   if (!dir) {
     dir = getenv("ANDROID_PLUGIN_DATADIR");
   }
 
   LOG("getApplicationDataDirectory return %s", dir);
   return dir;
 }
 
+const char*
+anp_system_getApplicationDataDirectory(NPP instance)
+{
+  return anp_system_getApplicationDataDirectory();
+}
+
 jclass anp_system_loadJavaClass(NPP instance, const char* className)
 {
   LOG("%s", __PRETTY_FUNCTION__);
 
   JNIEnv* env = GetJNIForThread();
   if (!env)
     return nsnull;
 
@@ -83,13 +89,32 @@ jclass anp_system_loadJavaClass(NPP inst
   lib->GetLibraryPath(libName);
 
   jstring jclassName = env->NewStringUTF(className);
   jstring jlibName = env->NewStringUTF(libName.get());
   jobject obj = env->CallStaticObjectMethod(cls, method, jclassName, jlibName);
   return reinterpret_cast<jclass>(obj);
 }
 
+void anp_system_setPowerState(NPP instance, ANPPowerState powerState)
+{
+  NOT_IMPLEMENTED();
+}
+
 void InitSystemInterface(ANPSystemInterfaceV0 *i) {
   _assert(i->inSize == sizeof(*i));
   ASSIGN(i, getApplicationDataDirectory);
   ASSIGN(i, loadJavaClass);
 }
+
+void InitSystemInterfaceV1(ANPSystemInterfaceV1 *i) {
+  _assert(i->inSize == sizeof(*i));
+  ASSIGN(i, getApplicationDataDirectory);
+  ASSIGN(i, loadJavaClass);
+  ASSIGN(i, setPowerState);
+}
+
+void InitSystemInterfaceV2(ANPSystemInterfaceV2 *i) {
+  _assert(i->inSize == sizeof(*i));
+  ASSIGN(i, getApplicationDataDirectory);
+  ASSIGN(i, loadJavaClass);
+  ASSIGN(i, setPowerState);
+}
new file mode 100644
--- /dev/null
+++ b/dom/plugins/base/android/ANPVideo.cpp
@@ -0,0 +1,109 @@
+/* The Original Code is Android NPAPI support code
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   James Willcox <jwillcox@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the 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 <android/log.h>
+#include "ANPBase.h"
+#include "AndroidMediaLayer.h"
+#include "nsIPluginInstanceOwner.h"
+#include "nsPluginInstanceOwner.h"
+#include "nsNPAPIPluginInstance.h"
+#include "gfxRect.h"
+
+#define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
+#define ASSIGN(obj, name)   (obj)->name = anp_video_##name
+
+using namespace mozilla;
+
+static AndroidMediaLayer* GetLayerForInstance(NPP instance) {
+  nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+
+  nsPluginInstanceOwner* owner;
+  if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) {
+    return NULL;
+  }
+
+  return owner->Layer();
+}
+
+static void Invalidate(NPP instance) {
+  nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+
+  nsPluginInstanceOwner* owner;
+  if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner)))
+    return;
+
+  owner->Invalidate();
+}
+
+static ANPNativeWindow anp_video_acquireNativeWindow(NPP instance) {
+  AndroidMediaLayer* layer = GetLayerForInstance(instance);
+  if (!layer)
+    return NULL;
+
+  return layer->RequestNativeWindowForVideo();
+}
+
+static void anp_video_setWindowDimensions(NPP instance, const ANPNativeWindow window,
+        const ANPRectF* dimensions) {
+  AndroidMediaLayer* layer = GetLayerForInstance(instance);
+  if (!layer)
+    return;
+
+  gfxRect rect(dimensions->left, dimensions->top,
+               dimensions->right - dimensions->left,
+               dimensions->bottom - dimensions->top);
+
+  layer->SetNativeWindowDimensions(window, rect);
+  Invalidate(instance);
+}
+
+
+static void anp_video_releaseNativeWindow(NPP instance, ANPNativeWindow window) {
+  AndroidMediaLayer* layer = GetLayerForInstance(instance);
+  if (!layer)
+    return;
+
+  layer->ReleaseNativeWindowForVideo(window);
+  Invalidate(instance);
+}
+
+static void anp_video_setFramerateCallback(NPP instance, const ANPNativeWindow window, ANPVideoFrameCallbackProc callback) {
+  // Bug 722682
+  NOT_IMPLEMENTED();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void InitVideoInterfaceV0(ANPVideoInterfaceV0* i) {
+    ASSIGN(i, acquireNativeWindow);
+    ASSIGN(i, setWindowDimensions);
+    ASSIGN(i, releaseNativeWindow);
+}
+
+void InitVideoInterfaceV1(ANPVideoInterfaceV1* i) {
+    ASSIGN(i, acquireNativeWindow);
+    ASSIGN(i, setWindowDimensions);
+    ASSIGN(i, releaseNativeWindow);
+    ASSIGN(i, setFramerateCallback);
+}
--- a/dom/plugins/base/android/ANPWindow.cpp
+++ b/dom/plugins/base/android/ANPWindow.cpp
@@ -34,20 +34,26 @@
  * 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 "assert.h"
 #include "ANPBase.h"
 #include <android/log.h>
+#include "AndroidBridge.h"
+#include "nsNPAPIPluginInstance.h"
+#include "nsIPluginInstanceOwner.h"
+#include "nsPluginInstanceOwner.h"
 
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
 #define ASSIGN(obj, name)   (obj)->name = anp_window_##name
 
+using namespace mozilla;
+
 void
 anp_window_setVisibleRects(NPP instance, const ANPRectI rects[], int32_t count)
 {
   NOT_IMPLEMENTED();
 }
 
 void
 anp_window_clearVisibleRects(NPP instance)
@@ -74,18 +80,67 @@ anp_window_exitFullScreen(NPP instance)
 }
 
 void
 anp_window_requestCenterFitZoom(NPP instance)
 {
   NOT_IMPLEMENTED();
 }
 
+ANPRectI
+anp_window_visibleRect(NPP instance)
+{
+  ANPRectI rect = { 0, 0, 0, 0 };
+
+  nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
+
+  nsPluginInstanceOwner* owner;
+  if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) {
+    return rect;
+  }
+
+  nsIntRect visibleRect = owner->GetVisibleRect();
+  rect.left = visibleRect.x;
+  rect.top = visibleRect.y;
+  rect.right = visibleRect.x + visibleRect.width;
+  rect.bottom = visibleRect.y + visibleRect.height;
+
+  return rect;
+}
+
+void anp_window_requestFullScreenOrientation(NPP instance, ANPScreenOrientation orientation)
+{
+  NOT_IMPLEMENTED();
+}
+
 void InitWindowInterface(ANPWindowInterfaceV0 *i) {
   _assert(i->inSize == sizeof(*i));
   ASSIGN(i, setVisibleRects);
   ASSIGN(i, clearVisibleRects);
   ASSIGN(i, showKeyboard);
   ASSIGN(i, requestFullScreen);
   ASSIGN(i, exitFullScreen);
   ASSIGN(i, requestCenterFitZoom);
 }
 
+void InitWindowInterfaceV1(ANPWindowInterfaceV1 *i) {
+  _assert(i->inSize == sizeof(*i));
+  ASSIGN(i, setVisibleRects);
+  ASSIGN(i, clearVisibleRects);
+  ASSIGN(i, showKeyboard);
+  ASSIGN(i, requestFullScreen);
+  ASSIGN(i, exitFullScreen);
+  ASSIGN(i, requestCenterFitZoom);
+  ASSIGN(i, visibleRect);
+}
+
+void InitWindowInterfaceV2(ANPWindowInterfaceV2 *i) {
+  _assert(i->inSize == sizeof(*i));
+  ASSIGN(i, setVisibleRects);
+  ASSIGN(i, clearVisibleRects);
+  ASSIGN(i, showKeyboard);
+  ASSIGN(i, requestFullScreen);
+  ASSIGN(i, exitFullScreen);
+  ASSIGN(i, requestCenterFitZoom);
+  ASSIGN(i, visibleRect);
+  ASSIGN(i, requestFullScreenOrientation);
+}
+
--- a/dom/plugins/base/android/Makefile.in
+++ b/dom/plugins/base/android/Makefile.in
@@ -58,21 +58,26 @@ EXPORTS = \
 
 CPPSRCS += ANPAudio.cpp    \
            ANPEvent.cpp    \
            ANPMatrix.cpp   \
            ANPSystem.cpp   \
            ANPWindow.cpp   \
            ANPBitmap.cpp   \
            ANPLog.cpp      \
+           ANPNativeWindow.cpp \
            ANPSurface.cpp  \
+           ANPVideo.cpp    \
+           ANPOpenGL.cpp   \
            $(NULL)
 
 LOCAL_INCLUDES += \
+  -I$(topsrcdir)/widget/android \
   -I$(topsrcdir)/dom/plugins/base \
   -I$(topsrcdir)/dom/plugins/base/android/include \
+  -I$(topsrcdir)/gfx/gl \
   $(MOZ_CAIRO_CFLAGS) \
   $(NULL)
 
 DEFINES += -DMOZ_APP_NAME='"$(MOZ_APP_NAME)"'
 
 include $(topsrcdir)/config/rules.mk
 
--- a/dom/plugins/base/android/android_npapi.h
+++ b/dom/plugins/base/android/android_npapi.h
@@ -31,18 +31,19 @@
     To minimize what native libraries the plugin links against, some
     functionality is provided via function-ptrs (e.g. time, sound)
  */
 
 #ifndef android_npapi_H
 #define android_npapi_H
 
 #include <stdint.h>
+#include <jni.h>
 #include "npapi.h"
-#include <jni.h>
+#include "GLDefs.h"
 
 ///////////////////////////////////////////////////////////////////////////////
 // General types
 
 enum ANPBitmapFormats {
     kUnknown_ANPBitmapFormat    = 0,
     kRGBA_8888_ANPBitmapFormat  = 1,
     kRGB_565_ANPBitmapFormat    = 2
@@ -115,16 +116,26 @@ typedef uint32_t ANPMatrixFlag;
 #define kPathInterfaceV0_ANPGetValue        ((NPNVariable)1005)
 #define kTypefaceInterfaceV0_ANPGetValue    ((NPNVariable)1006)
 #define kWindowInterfaceV0_ANPGetValue      ((NPNVariable)1007)
 #define kBitmapInterfaceV0_ANPGetValue      ((NPNVariable)1008)
 #define kSurfaceInterfaceV0_ANPGetValue     ((NPNVariable)1009)
 #define kSystemInterfaceV0_ANPGetValue      ((NPNVariable)1010)
 #define kEventInterfaceV0_ANPGetValue       ((NPNVariable)1011)
 
+#define kAudioTrackInterfaceV1_ANPGetValue  ((NPNVariable)1012)
+#define kOpenGLInterfaceV0_ANPGetValue      ((NPNVariable)1013)
+#define kWindowInterfaceV1_ANPGetValue      ((NPNVariable)1014)
+#define kVideoInterfaceV0_ANPGetValue       ((NPNVariable)1015)
+#define kSystemInterfaceV1_ANPGetValue      ((NPNVariable)1016)
+#define kSystemInterfaceV2_ANPGetValue      ((NPNVariable)1017)
+#define kWindowInterfaceV2_ANPGetValue      ((NPNVariable)1018)
+#define kNativeWindowInterfaceV0_ANPGetValue ((NPNVariable)1019)
+#define kVideoInterfaceV1_ANPGetValue       ((NPNVariable)1020)
+
 /** queries for the drawing models supported on this device.
 
     NPN_GetValue(inst, kSupportedDrawingModel_ANPGetValue, uint32_t* bits)
  */
 #define kSupportedDrawingModel_ANPGetValue  ((NPNVariable)2000)
 
 /** queries for the context (android.content.Context) of the plugin. If no
     instance is specified the application's context is returned. If the instance
@@ -175,16 +186,17 @@ enum ANPDrawingModels {
         ANPSurfaceInterface.  This interface can be used to manipulate Java
         objects that extend Surface.class by allowing them to access the
         surface's underlying bitmap in native code.  For instance, if a raster
         surface is used the plugin can lock, draw directly into the bitmap, and
         unlock the surface in native code without making JNI calls to the Java
         surface object.
      */
     kSurface_ANPDrawingModel = 1 << 1,
+    kOpenGL_ANPDrawingModel  = 1 << 2,
 };
 typedef int32_t ANPDrawingModel;
 
 /** Request to receive/disable events. If the pointer is NULL then all flags will
     be disabled. Otherwise, the event type will be enabled iff its corresponding
     bit in the EventFlags bit field is set.
 
     NPN_SetValue(inst, ANPAcceptEvents, (void*)EventFlags)
@@ -673,16 +685,35 @@ struct ANPWindowInterfaceV0 : ANPInterfa
         the plugin's full screen view will be discarded by the view system.
      */
     void    (*exitFullScreen)(NPP instance);
     /** Called when a plugin wishes to be zoomed and centered in the current view.
      */
     void    (*requestCenterFitZoom)(NPP instance);
 };
 
+struct ANPWindowInterfaceV1 : ANPWindowInterfaceV0 {
+    /** Returns a rectangle representing the visible area of the plugin on
+        screen. The coordinates are relative to the size of the plugin in the
+        document and therefore will never be negative or exceed the plugin's size.
+     */
+    ANPRectI (*visibleRect)(NPP instance);
+};
+
+typedef int32_t ANPScreenOrientation;
+
+struct ANPWindowInterfaceV2 : ANPWindowInterfaceV1 {
+    /** Called when the plugin wants to specify a particular screen orientation
+        when entering into full screen mode. The orientation must be set prior
+        to entering into full screen.  After entering full screen any subsequent
+        changes will be updated the next time the plugin goes full screen.
+     */
+    void (*requestFullScreenOrientation)(NPP instance, ANPScreenOrientation orientation);
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 
 enum ANPSampleFormats {
     kUnknown_ANPSamleFormat     = 0,
     kPCM16Bit_ANPSampleFormat   = 1,
     kPCM8Bit_ANPSampleFormat    = 2
 };
 typedef int32_t ANPSampleFormat;
@@ -757,16 +788,22 @@ struct ANPAudioTrackInterfaceV0 : ANPInt
     void (*pause)(ANPAudioTrack*);
     void (*stop)(ANPAudioTrack*);
     /** Returns true if the track is not playing (e.g. pause or stop was called,
         or start was never called.
      */
     bool (*isStopped)(ANPAudioTrack*);
 };
 
+struct ANPAudioTrackInterfaceV1 : ANPAudioTrackInterfaceV0 {
+    /** Returns the track's latency in milliseconds. */
+    uint32_t (*trackLatency)(ANPAudioTrack*);
+};
+
+
 ///////////////////////////////////////////////////////////////////////////////
 // DEFINITION OF VALUES PASSED THROUGH NPP_HandleEvent
 
 enum ANPEventTypes {
     kNull_ANPEventType          = 0,
     kKey_ANPEventType           = 1,
     /** Mouse events are triggered by either clicking with the navigational pad
         or by tapping the touchscreen (if the kDown_ANPTouchAction is handled by
@@ -917,22 +954,26 @@ struct ANPEvent {
         } lifecycle;
         struct {
             ANPDrawingModel model;
             // relative to (0,0) in top-left of your plugin
             ANPRectI        clip;
             // use based on the value in model
             union {
                 ANPBitmap   bitmap;
+                struct {
+                    int32_t width;
+                    int32_t height;
+                } surfaceSize;
             } data;
         } draw;
-        int32_t     other[8];
     } data;
 };
 
+
 struct ANPEventInterfaceV0 : ANPInterface {
     /** Post a copy of the specified event to the plugin. The event will be
         delivered to the plugin in its main thread (the thread that receives
         other ANPEvents). If, after posting before delivery, the NPP instance
         is torn down, the event will be discarded.
      */
     void (*postEvent)(NPP inst, const ANPEvent* event);
 };
@@ -971,9 +1012,122 @@ struct ANPSurfaceInterfaceV0 : ANPInterf
   void (*unlock)(JNIEnv* env, jobject surface);
 };
 
 typedef int32_t   int32;
 typedef uint32_t uint32;
 typedef int16_t   int16;
 typedef uint16_t uint16;
 
+/**
+ * TODO should we not use EGL and GL data types for ABI safety?
+ */
+struct ANPTextureInfo {
+    GLuint      textureId;
+    uint32_t    width;
+    uint32_t    height;
+    GLenum      internalFormat;
+};
+
+typedef void* ANPEGLContext;
+
+struct ANPOpenGLInterfaceV0 : ANPInterface {
+    ANPEGLContext (*acquireContext)(NPP instance);
+
+    ANPTextureInfo (*lockTexture)(NPP instance);
+
+    void (*releaseTexture)(NPP instance, const ANPTextureInfo*);
+
+    /**
+     * Invert the contents of the plugin on the y-axis.
+     * default is to not be inverted (i.e. use OpenGL coordinates)
+     */
+    void (*invertPluginContent)(NPP instance, bool isContentInverted);
+};
+
+enum ANPPowerStates {
+    kDefault_ANPPowerState  = 0,
+    kScreenOn_ANPPowerState = 1
+};
+typedef int32_t ANPPowerState;
+
+struct ANPSystemInterfaceV1 : ANPSystemInterfaceV0 {
+    void (*setPowerState)(NPP instance, ANPPowerState powerState);
+};
+
+struct ANPSystemInterfaceV2 : ANPInterface {
+    /** Return the path name for the current Application's plugin data directory,
+        or NULL if not supported. This directory will change depending on whether
+        or not the plugin is found within an incognito tab.
+     */
+    const char* (*getApplicationDataDirectory)(NPP instance);
+
+    // redeclaration of existing features
+    jclass (*loadJavaClass)(NPP instance, const char* className);
+    void (*setPowerState)(NPP instance, ANPPowerState powerState);
+};
+
+typedef void* ANPNativeWindow;
+
+struct ANPVideoInterfaceV0 : ANPInterface {
+
+    /**
+     * Constructs a new native window to be used for rendering video content.
+     *
+     * Subsequent calls will produce new windows, but may also return NULL after
+     * n attempts if the browser has reached it's limit. Further, if the browser
+     * is unable to acquire the window quickly it may also return NULL in order
+     * to not prevent the plugin from executing. A subsequent call will then
+     * return the window if it is avaiable.
+     *
+     * NOTE: The hardware may fail if you try to decode more than the allowable
+     * number of videos supported on that device.
+     */
+    ANPNativeWindow (*acquireNativeWindow)(NPP instance);
+
+    /**
+     * Sets the rectangle that specifies where the video content is to be drawn.
+     * The dimensions are in document space. Further, if the rect is NULL the
+     * browser will not attempt to draw the window, therefore do not set the
+     * dimensions until you queue the first buffer in the window.
+     */
+    void (*setWindowDimensions)(NPP instance, const ANPNativeWindow window, const ANPRectF* dimensions);
+
+    /**
+     */
+    void (*releaseNativeWindow)(NPP instance, ANPNativeWindow window);
+};
+
+/** Called to notify the plugin that a video frame has been composited by the
+*  browser for display.  This will be called in a separate thread and as such
+*  you cannot call releaseNativeWindow from the callback.
+*
+*  The timestamp is in nanoseconds, and is monotonically increasing.
+*/
+typedef void (*ANPVideoFrameCallbackProc)(ANPNativeWindow* window, int64_t timestamp);
+
+struct ANPVideoInterfaceV1 : ANPVideoInterfaceV0 {
+    /** Set a callback to be notified when an ANPNativeWindow is composited by
+     *  the browser.
+     */
+    void (*setFramerateCallback)(NPP instance, const ANPNativeWindow window, ANPVideoFrameCallbackProc);
+};
+
+struct ANPNativeWindowInterfaceV0 : ANPInterface {
+    /**
+     * Constructs a new native window to be used for rendering plugin content.
+     *
+     * Subsequent calls will return the original constructed window. Further, if
+     * the browser is unable to acquire the window quickly it may return NULL in
+     * order to not block the plugin indefinitely. A subsequent call will then
+     * return the window if it is available.
+     */
+    ANPNativeWindow (*acquireNativeWindow)(NPP instance);
+
+    /**
+     * Invert the contents of the plugin on the y-axis.
+     * default is to not be inverted (e.g. use OpenGL coordinates)
+     */
+    void (*invertPluginContent)(NPP instance, bool isContentInverted);
+};
+
+
 #endif
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -127,19 +127,20 @@ using mozilla::plugins::PluginModulePare
 #include "mozilla/X11Util.h"
 #endif
 
 #ifdef XP_WIN
 #include <windows.h>
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
+#include <android/log.h>
+#include "android_npapi.h"
 #include "ANPBase.h"
 #include "AndroidBridge.h"
-#include <android/log.h>
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
 #endif
 
 using namespace mozilla;
 using namespace mozilla::plugins::parent;
 
 // We should make this const...
 static NPNetscapeFuncs sBrowserFuncs = {
@@ -2343,32 +2344,31 @@ NPError NP_CALLBACK
       ANPWindowInterfaceV0 *i = (ANPWindowInterfaceV0 *) result;
       InitWindowInterface(i);
       return NPERR_NO_ERROR;
     }
 
     case kAudioTrackInterfaceV0_ANPGetValue: {
       LOG("get audio interface");
       ANPAudioTrackInterfaceV0 *i = (ANPAudioTrackInterfaceV0 *) result;
-      InitAudioTrackInterface(i);
+      InitAudioTrackInterfaceV0(i);
       return NPERR_NO_ERROR;
     }
 
     case kEventInterfaceV0_ANPGetValue: {
       LOG("get event interface");
       ANPEventInterfaceV0 *i = (ANPEventInterfaceV0 *) result;
       InitEventInterface(i);
       return NPERR_NO_ERROR;
     }
 
     case kSystemInterfaceV0_ANPGetValue: {
       LOG("get system interface");
       ANPSystemInterfaceV0* i = reinterpret_cast<ANPSystemInterfaceV0*>(result);
       InitSystemInterface(i);
-      LOG("done system interface");
       return NPERR_NO_ERROR;
     }
 
     case kSurfaceInterfaceV0_ANPGetValue: {
       LOG("get surface interface");
       ANPSurfaceInterfaceV0 *i = (ANPSurfaceInterfaceV0 *) result;
       InitSurfaceInterface(i);
       return NPERR_NO_ERROR;
@@ -2390,16 +2390,81 @@ NPError NP_CALLBACK
       jclass cls     = env->FindClass("org/mozilla/gecko/GeckoApp");
       jfieldID field = env->GetStaticFieldID(cls, "mAppContext",
                                              "Lorg/mozilla/gecko/GeckoApp;");
       jobject ret = env->GetStaticObjectField(cls, field);
       int32_t* i  = reinterpret_cast<int32_t*>(result);
       *i = reinterpret_cast<int32_t>(ret);
       return NPERR_NO_ERROR;
     }
+
+    case kAudioTrackInterfaceV1_ANPGetValue: {
+      LOG("get audio interface v1");
+      ANPAudioTrackInterfaceV1 *i = (ANPAudioTrackInterfaceV1 *) result;
+      InitAudioTrackInterfaceV1(i);
+      return NPERR_NO_ERROR;
+    }
+
+    case kNativeWindowInterfaceV0_ANPGetValue: {
+      LOG("get native window interface v0");
+      ANPNativeWindowInterfaceV0* i = (ANPNativeWindowInterfaceV0 *) result;
+      InitNativeWindowInterface(i);
+      return NPERR_NO_ERROR;
+    }
+
+    case kOpenGLInterfaceV0_ANPGetValue: {
+      LOG("get openGL interface");
+      ANPOpenGLInterfaceV0 *i = (ANPOpenGLInterfaceV0*) result;
+      InitOpenGLInterface(i);
+      return NPERR_NO_ERROR;
+    }
+
+    case kWindowInterfaceV1_ANPGetValue: {
+      LOG("get Window interface V1");
+      ANPWindowInterfaceV1 *i = (ANPWindowInterfaceV1 *) result;
+      InitWindowInterfaceV1(i);
+      return NPERR_NO_ERROR;
+    }
+
+    case kWindowInterfaceV2_ANPGetValue: {
+      LOG("get Window interface V2");
+      ANPWindowInterfaceV2 *i = (ANPWindowInterfaceV2 *) result;
+      InitWindowInterfaceV2(i);
+      return NPERR_NO_ERROR;
+    }
+
+    case kVideoInterfaceV0_ANPGetValue: {
+      LOG("get video interface");
+      ANPVideoInterfaceV0 *i = (ANPVideoInterfaceV0*) result;
+      InitVideoInterfaceV0(i);
+      return NPERR_NO_ERROR;
+    }
+
+    case kVideoInterfaceV1_ANPGetValue: {
+      LOG("get video interface");
+      ANPVideoInterfaceV1 *i = (ANPVideoInterfaceV1*) result;
+      InitVideoInterfaceV1(i);
+      return NPERR_NO_ERROR;
+    }
+
+
+    case kSystemInterfaceV1_ANPGetValue: {
+      LOG("get system interface v1");
+      ANPSystemInterfaceV1* i = reinterpret_cast<ANPSystemInterfaceV1*>(result);
+      InitSystemInterfaceV1(i);
+      return NPERR_NO_ERROR;
+    }
+
+    case kSystemInterfaceV2_ANPGetValue: {
+      LOG("get system interface v2");
+      ANPSystemInterfaceV2* i = reinterpret_cast<ANPSystemInterfaceV2*>(result);
+      InitSystemInterfaceV2(i);
+      return NPERR_NO_ERROR;
+    }
+
 #endif
 
   // we no longer hand out any XPCOM objects
   case NPNVDOMElement:
     // fall through
   case NPNVDOMWindow:
     // fall through
   case NPNVserviceManager:
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -145,16 +145,18 @@ public:
 #ifdef XP_MACOSX
   void SetDrawingModel(NPDrawingModel aModel);
   void SetEventModel(NPEventModel aModel);
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
   PRUint32 GetANPDrawingModel() { return mANPDrawingModel; }
   void SetANPDrawingModel(PRUint32 aModel);
+
+  // This stuff is for kSurface_ANPDrawingModel
   void* GetJavaSurface();
   void SetJavaSurface(void* aSurface);
   void RequestJavaSurface();
 #endif
 
   nsresult NewStreamListener(const char* aURL, void* notifyData,
                              nsIPluginStreamListener** listener);
 
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -117,16 +117,17 @@ static NS_DEFINE_CID(kAppShellCID, NS_AP
 #include <gtk/gtk.h>
 #include "gfxXlibNativeRenderer.h"
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "ANPBase.h"
 #include "android_npapi.h"
 #include "AndroidBridge.h"
+#include "AndroidMediaLayer.h"
 using namespace mozilla::dom;
 
 #include <android/log.h>
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
 #endif
 
 using namespace mozilla;
 
@@ -257,33 +258,24 @@ nsPluginInstanceOwner::EndUpdateBackgrou
 
 bool
 nsPluginInstanceOwner::UseAsyncRendering()
 {
 #ifdef XP_MACOSX
   if (mUseAsyncRendering) {
     return true;
   }
-
-  nsRefPtr<ImageContainer> container;
-  if (mObjectFrame) {
-    container = mObjectFrame->GetImageContainer();
-  }
 #endif
 
   bool useAsyncRendering;
   bool result = (mInstance &&
           NS_SUCCEEDED(mInstance->UseAsyncPainting(&useAsyncRendering)) &&
-          useAsyncRendering &&
-#ifdef XP_MACOSX
-          container &&
-          container->GetBackendType() == 
-                  LayerManager::LAYERS_OPENGL
-#else
-          (!mPluginWindow ||
+          useAsyncRendering
+#ifndef XP_MACOSX
+          && (!mPluginWindow ||
            mPluginWindow->type == NPWindowTypeDrawable)
 #endif
           );
 
 #ifdef XP_MACOSX
   if (result) {
     mUseAsyncRendering = true;
   }
@@ -343,18 +335,19 @@ nsPluginInstanceOwner::nsPluginInstanceO
   mEventModel = NPEventModelCocoa;
 #endif
   mUseAsyncRendering = false;
 #endif
 
   mWaitingForPaint = false;
 
 #ifdef MOZ_WIDGET_ANDROID
-  mPluginViewAdded = false;
-  mLastPluginRect = gfxRect(0, 0, 0, 0);
+  mOnScreen = false;
+  mInverted = false;
+  mLayer = new AndroidMediaLayer();
 #endif
 }
 
 nsPluginInstanceOwner::~nsPluginInstanceOwner()
 {
   PRInt32 cnt;
 
   if (mWaitingForPaint) {
@@ -395,16 +388,23 @@ nsPluginInstanceOwner::~nsPluginInstance
   if (mTagText) {
     NS_Free(mTagText);
     mTagText = nsnull;
   }
 
   PLUG_DeletePluginNativeWindow(mPluginWindow);
   mPluginWindow = nsnull;
 
+#ifdef MOZ_WIDGET_ANDROID
+  if (mLayer) {
+    delete mLayer;
+    mLayer = nsnull;
+  }
+#endif
+
   if (mInstance) {
     mInstance->InvalidateOwner();
   }
 }
 
 NS_IMPL_ISUPPORTS3(nsPluginInstanceOwner,
                    nsIPluginInstanceOwner,
                    nsIPluginTagInfo,
@@ -1678,29 +1678,61 @@ void nsPluginInstanceOwner::ScrollPositi
       }
       pluginWidget->EndDrawPlugin();
     }
   }
 #endif
 }
 
 #ifdef MOZ_WIDGET_ANDROID
+
+void nsPluginInstanceOwner::SendSize(int width, int height)
+{
+  if (!mInstance)
+    return;
+
+  PRInt32 model = mInstance->GetANPDrawingModel();
+
+  if (model != kOpenGL_ANPDrawingModel)
+    return;
+
+  ANPEvent event;
+  event.inSize = sizeof(ANPEvent);
+  event.eventType = kDraw_ANPEventType;
+  event.data.draw.model = kOpenGL_ANPDrawingModel;
+  event.data.draw.data.surfaceSize.width = width;
+  event.data.draw.data.surfaceSize.height = height;
+
+  mInstance->HandleEvent(&event, nsnull);
+}
+
+void nsPluginInstanceOwner::SendOnScreenEvent(bool onScreen)
+{
+  if (!mInstance)
+    return;
+
+  if ((onScreen && !mOnScreen) || (!onScreen && mOnScreen)) {
+    ANPEvent event;
+    event.inSize = sizeof(ANPEvent);
+    event.eventType = kLifecycle_ANPEventType;
+    event.data.lifecycle.action = onScreen ? kOnScreen_ANPLifecycleAction : kOffScreen_ANPLifecycleAction;
+    mInstance->HandleEvent(&event, nsnull);
+
+    mOnScreen = onScreen;
+  }
+}
+
 bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect)
 {
   void* javaSurface = mInstance->GetJavaSurface();
   if (!javaSurface) {
     mInstance->RequestJavaSurface();
     return false;
   }
 
-  if (aRect.IsEqualEdges(mLastPluginRect)) {
-    // Already added and in position, no work to do
-    return true;
-  }
-
   JNIEnv* env = GetJNIForThread();
   if (!env)
     return false;
 
   AndroidBridge::AutoLocalJNIFrame frame(env, 1);
 
   jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell");
 
@@ -1733,58 +1765,52 @@ bool nsPluginInstanceOwner::AddPluginVie
                             method,
                             javaSurface,
                             aRect.x,
                             aRect.y,
                             aRect.width,
                             aRect.height);
 #endif
 
-  if (!mPluginViewAdded) {
-    ANPEvent event;
-    event.inSize = sizeof(ANPEvent);
-    event.eventType = kLifecycle_ANPEventType;
-    event.data.lifecycle.action = kOnScreen_ANPLifecycleAction;
-    mInstance->HandleEvent(&event, nsnull);
-
-    mPluginViewAdded = true;
-  }
+  SendOnScreenEvent(true);
 
   return true;
 }
 
 void nsPluginInstanceOwner::RemovePluginView()
 {
-  if (!mInstance || !mObjectFrame | !mPluginViewAdded)
+  if (!mInstance || !mObjectFrame | !mOnScreen)
     return;
 
-  mPluginViewAdded = false;
-
   void* surface = mInstance->GetJavaSurface();
   if (!surface)
     return;
 
   JNIEnv* env = GetJNIForThread();
   if (!env)
     return;
 
   AndroidBridge::AutoLocalJNIFrame frame(env, 1);
 
   jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell");
   jmethodID method = env->GetStaticMethodID(cls,
                                             "removePluginView",
                                             "(Landroid/view/View;)V");
   env->CallStaticVoidMethod(cls, method, surface);
-
-    ANPEvent event;
-    event.inSize = sizeof(ANPEvent);
-    event.eventType = kLifecycle_ANPEventType;
-    event.data.lifecycle.action = kOffScreen_ANPLifecycleAction;
-    mInstance->HandleEvent(&event, nsnull);
+  SendOnScreenEvent(false);
 }
+
+void nsPluginInstanceOwner::Invalidate() {
+  NPRect rect;
+  rect.left = rect.top = 0;
+  rect.right = mPluginWindow->width;
+  rect.bottom = mPluginWindow->height;
+  InvalidateRect(&rect);
+}
+
 #endif
 
 nsresult nsPluginInstanceOwner::DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent)
 {
 #ifdef MOZ_WIDGET_ANDROID
   {
     ANPEvent event;
     event.inSize = sizeof(ANPEvent);
@@ -2856,25 +2882,31 @@ void nsPluginInstanceOwner::Paint(gfxCon
 {
   if (!mInstance || !mObjectFrame)
     return;
 
   PRInt32 model = mInstance->GetANPDrawingModel();
 
   if (model == kSurface_ANPDrawingModel) {
     if (!AddPluginView(aFrameRect)) {
-      NPRect rect;
-      rect.left = rect.top = 0;
-      rect.right = aFrameRect.width;
-      rect.bottom = aFrameRect.height;
-      InvalidateRect(&rect);
+      Invalidate();
     }
     return;
   }
 
+  if (model == kOpenGL_ANPDrawingModel) {
+    // FIXME: this is gross
+    float zoomLevel = aFrameRect.width / (float)mPluginWindow->width;
+    mLayer->UpdatePosition(aFrameRect, zoomLevel);
+
+    SendOnScreenEvent(true);
+    SendSize((int)aFrameRect.width, (int)aFrameRect.height);
+    return;
+  }
+
   if (model != kBitmap_ANPDrawingModel)
     return;
 
 #ifdef ANP_BITMAP_DRAWING_MODEL
   static nsRefPtr<gfxImageSurface> pluginSurface;
 
   if (pluginSurface == nsnull ||
       aFrameRect.width  != pluginSurface->Width() ||
@@ -3561,18 +3593,26 @@ void nsPluginInstanceOwner::UpdateWindow
   mPluginWindow->clipRect.top = 0;
 
   if (mPluginWindowVisible && mPluginDocumentActiveState) {
     mPluginWindow->clipRect.right = mPluginWindow->width;
     mPluginWindow->clipRect.bottom = mPluginWindow->height;
   } else {
     mPluginWindow->clipRect.right = 0;
     mPluginWindow->clipRect.bottom = 0;
-#ifdef MOZ_WIDGET_ANDROID
-    RemovePluginView();
+#if 0 //MOZ_WIDGET_ANDROID
+    if (mInstance) {
+      PRInt32 model = mInstance->GetANPDrawingModel();
+
+      if (model == kSurface_ANPDrawingModel) {
+        RemovePluginView();
+      } else if (model == kOpenGL_ANPDrawingModel) {
+        HidePluginLayer();
+      }
+    }
 #endif
   }
 
   if (!aSetWindow)
     return;
 
   if (mPluginWindow->x               != oldWindow.x               ||
       mPluginWindow->y               != oldWindow.y               ||
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -89,16 +89,22 @@ class gfxXlibSurface;
 #endif
 
 #ifdef XP_OS2
 #define INCL_PM
 #define INCL_GPI
 #include <os2.h>
 #endif
 
+#ifdef MOZ_WIDGET_ANDROID
+namespace mozilla {
+  class AndroidMediaLayer;
+}
+#endif
+
 // X.h defines KeyPress
 #ifdef KeyPress
 #undef KeyPress
 #endif
 
 class nsPluginInstanceOwner : public nsIPluginInstanceOwner,
                               public nsIPluginTagInfo,
                               public nsIDOMEventListener,
@@ -281,33 +287,60 @@ public:
   // Methods to update the background image we send to async plugins.
   // The eventual target of these operations is PluginInstanceParent,
   // but it takes several hops to get there.
   void SetBackgroundUnknown();
   already_AddRefed<gfxContext> BeginUpdateBackground(const nsIntRect& aRect);
   void EndUpdateBackground(gfxContext* aContext, const nsIntRect& aRect);
   
   bool UseAsyncRendering();
+
+#ifdef MOZ_WIDGET_ANDROID
+  nsIntRect GetVisibleRect() {
+    return nsIntRect(0, 0, mPluginWindow->width, mPluginWindow->height);
+  }
+
+  void SetInverted(bool aInverted) {
+    mInverted = aInverted;
+  }
+
+  bool Inverted() {
+    return mInverted;
+  }
+
+  mozilla::AndroidMediaLayer* Layer() {
+    return mLayer;
+  }
+
+  void Invalidate();
+#endif
   
 private:
   
   // return FALSE if LayerSurface dirty (newly created and don't have valid plugin content yet)
   bool IsUpToDate()
   {
     nsIntSize size;
     return NS_SUCCEEDED(mInstance->GetImageSize(&size)) &&
     size == nsIntSize(mPluginWindow->width, mPluginWindow->height);
   }
   
   void FixUpURLS(const nsString &name, nsAString &value);
-#ifdef ANDROID
+#ifdef MOZ_WIDGET_ANDROID
+  void SendSize(int width, int height);
+  void SendOnScreenEvent(bool onScreen);
+
   bool AddPluginView(const gfxRect& aRect);
   void RemovePluginView();
-  bool mPluginViewAdded;
-  gfxRect mLastPluginRect;
+
+  bool mOnScreen;
+  bool mInverted;
+
+  // For kOpenGL_ANPDrawingModel
+  mozilla::AndroidMediaLayer *mLayer;
 #endif 
  
   nsPluginNativeWindow       *mPluginWindow;
   nsRefPtr<nsNPAPIPluginInstance> mInstance;
   nsObjectFrame              *mObjectFrame;
   nsIContent                 *mContent; // WEAK, content owns us
   nsCString                   mDocumentBase;
   char                       *mTagText;
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -642,19 +642,16 @@ PluginInstanceParent::GetImage(ImageCont
     if (!mFrontSurface)
 #endif
         return NS_ERROR_NOT_AVAILABLE;
 
     Image::Format format = Image::CAIRO_SURFACE;
 #ifdef XP_MACOSX
     if (ioSurface) {
         format = Image::MAC_IO_SURFACE;
-        if (!aContainer->Manager()) {
-            return NS_ERROR_FAILURE;
-        }
     }
 #endif
 
     nsRefPtr<Image> image;
     image = aContainer->CreateImage(&format, 1);
     if (!image) {
         return NS_ERROR_FAILURE;
     }
--- a/dom/system/b2g/Makefile.in
+++ b/dom/system/b2g/Makefile.in
@@ -78,11 +78,12 @@ LOCAL_INCLUDES = \
 EXTRA_COMPONENTS = \
   RadioInterfaceLayer.manifest \
   RadioInterfaceLayer.js \
   $(NULL)
 
 EXTRA_JS_MODULES = \
   ril_consts.js \
   ril_worker.js \
+  systemlibs.js \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/dom/system/b2g/RadioInterfaceLayer.js
+++ b/dom/system/b2g/RadioInterfaceLayer.js
@@ -42,17 +42,17 @@
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 var RIL = {};
 Cu.import("resource://gre/modules/ril_consts.js", RIL);
 
-const DEBUG = true; // set to false to suppress debug messages
+const DEBUG = false; // set to true to see debug messages
 
 const RADIOINTERFACELAYER_CID =
   Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}");
 const DATACALLINFO_CID =
   Components.ID("{ef474cd9-94f7-4c05-a31b-29b9de8a10d2}");
 
 const nsIAudioManager = Ci.nsIAudioManager;
 const nsIRadioInterfaceLayer = Ci.nsIRadioInterfaceLayer;
@@ -236,17 +236,19 @@ RadioInterfaceLayer.prototype = {
 
   /**
    * Handle call state changes by updating our current state and the audio
    * system.
    */
   handleCallStateChange: function handleCallStateChange(call) {
     debug("handleCallStateChange: " + JSON.stringify(call));
     call.state = convertRILCallState(call.state);
-    if (call.state == nsIRadioInterfaceLayer.CALL_STATE_CONNECTED) {
+    if (call.state == nsIRadioInterfaceLayer.CALL_STATE_DIALING ||
+        call.state == nsIRadioInterfaceLayer.CALL_STATE_RINGING ||
+        call.state == nsIRadioInterfaceLayer.CALL_STATE_CONNECTED) {
       // This is now the active call.
       this._activeCall = call;
     }
     this.updateCallAudioState();
     this._deliverCallback("callStateChanged",
                           [call.callIndex, call.state, call.number]);
   },
 
--- a/dom/system/b2g/ril_worker.js
+++ b/dom/system/b2g/ril_worker.js
@@ -57,26 +57,28 @@
  *
  * Note: The code below is purposely lean on abstractions to be as lean in
  * terms of object allocations. As a result, it may look more like C than
  * JavaScript, and that's intended.
  */
 
 "use strict";
 
-importScripts("ril_consts.js");
+importScripts("ril_consts.js", "systemlibs.js");
 
 let DEBUG = false;
 
 const INT32_MAX   = 2147483647;
 const UINT8_SIZE  = 1;
 const UINT16_SIZE = 2;
 const UINT32_SIZE = 4;
 const PARCEL_SIZE_SIZE = UINT32_SIZE;
 
+let RILQUIRKS_CALLSTATE_EXTRA_UINT32 = false;
+
 /**
  * This object contains helpers buffering incoming data & deconstructing it
  * into parcels as well as buffering outgoing data & constructing parcels.
  * For that it maintains two buffers and corresponding uint8 views, indexes.
  *
  * The incoming buffer is a circular buffer where we store incoming data.
  * As soon as a complete parcel is received, it is processed right away, so
  * the buffer only needs to be large enough to hold one parcel.
@@ -427,43 +429,46 @@ let Buf = {
       this.readIncoming -= this.currentParcelSize;
       this.currentParcelSize = 0;
     }
   },
 
   /**
    * Process one parcel.
    */
-
   processParcel: function processParcel() {
     let response_type = this.readUint32();
     let length = this.readIncoming - UINT32_SIZE;
 
     let request_type;
     if (response_type == RESPONSE_TYPE_SOLICITED) {
       let token = this.readUint32();
       let error = this.readUint32();
       length -= 2 * UINT32_SIZE;
       request_type = this.tokenRequestMap[token];
       if (error) {
         //TODO
-        debug("Received error " + error + " for solicited parcel type " +
-              request_type);
+        if (DEBUG) {
+          debug("Received error " + error + " for solicited parcel type " +
+                request_type);
+        }
         return;
       }
-      debug("Solicited response for request type " + request_type +
-            ", token " + token);
+      if (DEBUG) {
+        debug("Solicited response for request type " + request_type +
+              ", token " + token);
+      }
       delete this.tokenRequestMap[token];
       this.lastSolicitedToken = token;
     } else if (response_type == RESPONSE_TYPE_UNSOLICITED) {
       request_type = this.readUint32();
       length -= UINT32_SIZE;
-      debug("Unsolicited response for request type " + request_type);
+      if (DEBUG) debug("Unsolicited response for request type " + request_type);
     } else {
-      debug("Unknown response type: " + response_type);
+      if (DEBUG) debug("Unknown response type: " + response_type);
       return;
     }
 
     RIL.handleParcel(request_type, length);
   },
 
   /**
    * Start a new outgoing parcel.
@@ -478,30 +483,29 @@ let Buf = {
     let token = this.token;
     this.writeUint32(token);
     this.tokenRequestMap[token] = type;
     this.token++;
     return token;
   },
 
   /**
-   * Communication with the RIL IPC thread.
+   * Communicate with the RIL IPC thread.
    */
-
   sendParcel: function sendParcel() {
     // Compute the size of the parcel and write it to the front of the parcel
     // where we left room for it. Note that he parcel size does not include
     // the size itself.
     let parcelSize = this.outgoingIndex - PARCEL_SIZE_SIZE;
     this.writeParcelSize(parcelSize);
 
     // This assumes that postRILMessage will make a copy of the ArrayBufferView
     // right away!
     let parcel = this.outgoingBytes.subarray(0, this.outgoingIndex);
-    debug("Outgoing parcel: " + Array.slice(parcel));
+    if (DEBUG) debug("Outgoing parcel: " + Array.slice(parcel));
     postRILMessage(parcel);
     this.outgoingIndex = PARCEL_SIZE_SIZE;
   },
 
   simpleRequest: function simpleRequest(type) {
     this.newParcel(type);
     this.sendParcel();
   }
@@ -512,16 +516,35 @@ let Buf = {
  * Provide a high-level API representing the RIL's capabilities. This is
  * where parcels are sent and received from and translated into API calls.
  * For the most part, this object is pretty boring as it simply translates
  * between method calls and RIL parcels. Somebody's gotta do the job...
  */
 let RIL = {
 
   /**
+   * Set quirk flags based on the RIL model detected. Note that this
+   * requires the RIL being "warmed up" first, which happens when on
+   * an incoming or outgoing call.
+   */
+  rilQuirksInitialized: false,
+  initRILQuirks: function initRILQuirks() {
+    // The Samsung Galaxy S2 I-9100 radio sends an extra Uint32 in the
+    // call state.
+    let model_id = libcutils.property_get("ril.model_id");
+    if (DEBUG) debug("Detected RIL model " + model_id);
+    if (model_id == "I9100") {
+      if (DEBUG) debug("Enabling RILQUIRKS_CALLSTATE_EXTRA_UINT32 for I9100.");
+      RILQUIRKS_CALLSTATE_EXTRA_UINT32 = true;
+    }
+
+    this.rilQuirksInitialized = true;
+  },
+
+  /**
    * Retrieve the ICC's status.
    *
    * Response will call Phone.onICCStatus().
    */
   getICCStatus: function getICCStatus() {
     Buf.simpleRequest(REQUEST_GET_SIM_STATUS);
   },
 
@@ -860,17 +883,17 @@ let RIL = {
    * Handle incoming requests from the RIL. We find the method that
    * corresponds to the request type. Incidentally, the request type
    * _is_ the method name, so that's easy.
    */
 
   handleParcel: function handleParcel(request_type, length) {
     let method = this[request_type];
     if (typeof method == "function") {
-      debug("Handling parcel as " + method.name);
+      if (DEBUG) debug("Handling parcel as " + method.name);
       method.call(this, length);
     }
   }
 };
 
 RIL[REQUEST_GET_SIM_STATUS] = function REQUEST_GET_SIM_STATUS() {
   let iccStatus = {
     cardState:                   Buf.readUint32(), // CARD_STATE_*
@@ -910,53 +933,60 @@ RIL[REQUEST_ENTER_SIM_PUK] = function RE
 RIL[REQUEST_ENTER_SIM_PIN2] = null;
 RIL[REQUEST_ENTER_SIM_PUK2] = null;
 RIL[REQUEST_CHANGE_SIM_PIN] = function REQUEST_CHANGE_SIM_PIN() {
   Phone.onChangeICCPIN();
 };
 RIL[REQUEST_CHANGE_SIM_PIN2] = null;
 RIL[REQUEST_ENTER_NETWORK_DEPERSONALIZATION] = null;
 RIL[REQUEST_GET_CURRENT_CALLS] = function REQUEST_GET_CURRENT_CALLS(length) {
+  if (!this.rilQuirksInitialized) {
+    this.initRILQuirks();
+  }
+
   let calls_length = 0;
   // The RIL won't even send us the length integer if there are no active calls.
   // So only read this integer if the parcel actually has it.
   if (length) {
     calls_length = Buf.readUint32();
   }
   if (!calls_length) {
     Phone.onCurrentCalls(null);
     return;
   }
 
   let calls = {};
   for (let i = 0; i < calls_length; i++) {
-    let call = {
-      state:              Buf.readUint32(), // CALL_STATE_*
-      callIndex:          Buf.readUint32(), // GSM index (1-based)
-      toa:                Buf.readUint32(),
-      isMpty:             Boolean(Buf.readUint32()),
-      isMT:               Boolean(Buf.readUint32()),
-      als:                Buf.readUint32(),
-      isVoice:            Boolean(Buf.readUint32()),
-      isVoicePrivacy:     Boolean(Buf.readUint32()),
-      somethingOrOther:   Buf.readUint32(), //XXX TODO whatziz? not in ril.h, but it's in the output...
-      number:             Buf.readString(), //TODO munge with TOA
-      numberPresentation: Buf.readUint32(), // CALL_PRESENTATION_*
-      name:               Buf.readString(),
-      namePresentation:   Buf.readUint32(),
-      uusInfo:            null
-    };
+    let call = {};
+    call.state          = Buf.readUint32(); // CALL_STATE_*
+    call.callIndex      = Buf.readUint32(); // GSM index (1-based)
+    call.toa            = Buf.readUint32();
+    call.isMpty         = Boolean(Buf.readUint32());
+    call.isMT           = Boolean(Buf.readUint32());
+    call.als            = Buf.readUint32();
+    call.isVoice        = Boolean(Buf.readUint32());
+    call.isVoicePrivacy = Boolean(Buf.readUint32());
+    if (RILQUIRKS_CALLSTATE_EXTRA_UINT32) {
+      Buf.readUint32();
+    }
+    call.number             = Buf.readString(); //TODO munge with TOA
+    call.numberPresentation = Buf.readUint32(); // CALL_PRESENTATION_*
+    call.name               = Buf.readString();
+    call.namePresentation   = Buf.readUint32();
+
+    call.uusInfo = null;
     let uusInfoPresent = Buf.readUint32();
     if (uusInfoPresent == 1) {
       call.uusInfo = {
         type:     Buf.readUint32(),
         dcs:      Buf.readUint32(),
         userData: null //XXX TODO byte array?!?
       };
     }
+
     calls[call.callIndex] = call;
   }
   Phone.onCurrentCalls(calls);
 };
 RIL[REQUEST_DIAL] = function REQUEST_DIAL(length) {
   Phone.onDial();
 };
 RIL[REQUEST_GET_IMSI] = function REQUEST_GET_IMSI(length) {
@@ -1213,19 +1243,16 @@ RIL[UNSOLICITED_RESEND_INCALL_MUTE] = nu
 
 /**
  * This object represents the phone's state and functionality. It is
  * essentially a state machine that's being acted upon from RIL and the
  * mainthread via postMessage communication.
  */
 let Phone = {
 
-  //XXX TODO beware, this is just demo code. It's still missing
-  // communication with the UI thread.
-
   /**
    * One of the RADIO_STATE_* constants.
    */
   radioState: RADIO_STATE_UNAVAILABLE,
 
   /**
    * Strings
    */
@@ -1308,17 +1335,19 @@ let Phone = {
   },
 
   /**
    * Handlers for messages from the RIL. They all begin with on* and are called
    * from RIL object.
    */
 
   onRadioStateChanged: function onRadioStateChanged(newState) {
-    debug("Radio state changed from " + this.radioState + " to " + newState);
+    if (DEBUG) {
+      debug("Radio state changed from " + this.radioState + " to " + newState);
+    }
     if (this.radioState == newState) {
       // No change in state, return.
       return;
     }
 
     let gsm = newState == RADIO_STATE_SIM_NOT_READY        ||
               newState == RADIO_STATE_SIM_LOCKED_OR_ABSENT ||
               newState == RADIO_STATE_SIM_READY;
@@ -1450,23 +1479,23 @@ let Phone = {
   },
 
   onCallRing: function onCallRing(info) {
     // For now we don't need to do anything here because we'll also get a
     // call state changed notification.
   },
 
   onNetworkStateChanged: function onNetworkStateChanged() {
-    debug("Network state changed, re-requesting phone state.");
+    if (DEBUG) debug("Network state changed, re-requesting phone state.");
     this.requestNetworkInfo();
   },
 
   onICCStatus: function onICCStatus(iccStatus) {
     if (DEBUG) {
-        debug("iccStatus: " + JSON.stringify(iccStatus));
+      debug("iccStatus: " + JSON.stringify(iccStatus));
     }
     this.iccStatus = iccStatus;
 
     if ((!iccStatus) || (iccStatus.cardState == CARD_STATE_ABSENT)) {
       if (DEBUG) debug("ICC absent");
       if (this.cardState == DOM_CARDSTATE_ABSENT) {
         return;
       }
new file mode 100644
--- /dev/null
+++ b/dom/system/b2g/systemlibs.js
@@ -0,0 +1,79 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const SYSTEM_PROPERTY_KEY_MAX = 32;
+const SYSTEM_PROPERTY_VALUE_MAX = 92;
+
+/**
+ * Expose some system-level functions.
+ */
+let libcutils = (function() {
+  let lib;
+  try {
+    lib = ctypes.open("libcutils.so");
+  } catch(ex) {
+    // Return a fallback option in case libcutils.so isn't present (e.g.
+    // when building Firefox with MOZ_B2G_RIL.
+    dump("Could not load libcutils.so. Using fake propdb.");
+    let fake_propdb = Object.create(null);
+    return {
+      property_get: function fake_property_get(key, defaultValue) {
+        if (key in fake_propdb) {
+          return fake_propdb[key];
+        }
+        return defaultValue === undefined ? null : defaultValue;
+      },
+      property_set: function fake_property_set(key, value) {
+        fake_propdb[key] = value;
+      }
+    };
+  }
+
+  let c_property_get = lib.declare("property_get", ctypes.default_abi,
+                                   ctypes.int,       // return value: length
+                                   ctypes.char.ptr,  // key
+                                   ctypes.char.ptr,  // value
+                                   ctypes.char.ptr); // default
+  let c_property_set = lib.declare("property_set", ctypes.default_abi,
+                                   ctypes.int,       // return value: success
+                                   ctypes.char.ptr,  // key
+                                   ctypes.char.ptr); // value
+  let c_value_buf = ctypes.char.array(SYSTEM_PROPERTY_VALUE_MAX)();
+
+  return {
+
+    /**
+     * Get a system property.
+     *
+     * @param key
+     *        Name of the property
+     * @param defaultValue [optional]
+     *        Default value to return if the property isn't set (default: null)
+     */
+    property_get: function property_get(key, defaultValue) {
+      if (defaultValue === undefined) {
+        defaultValue = null;
+      }
+      c_property_get(key, c_value_buf, defaultValue);
+      return c_value_buf.readString();
+    },
+
+    /**
+     * Set a system property
+     *
+     * @param key
+     *        Name of the property
+     * @param value
+     *        Value to set the property to.
+     */
+    property_set: function property_set(key, value) {
+      let rv = c_property_set(key, value);
+      if (rv) {
+        throw Error('libcutils.property_set("' + key + '", "' + value +
+                    '") failed with error ' + rv);
+      }
+    }
+
+  };
+})();
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -288,31 +288,31 @@ CreateJSContextForWorker(WorkerPrivate* 
     NS_WARNING("Could not create new runtime!");
     return nsnull;
   }
 
   // This is the real place where we set the max memory for the runtime.
   JS_SetGCParameter(runtime, JSGC_MAX_BYTES,
                     aWorkerPrivate->GetJSRuntimeHeapSize());
 
+  JS_SetNativeStackQuota(runtime, WORKER_CONTEXT_NATIVE_STACK_LIMIT);
+
   JSContext* workerCx = JS_NewContext(runtime, 0);
   if (!workerCx) {
     JS_DestroyRuntime(runtime);
     NS_WARNING("Could not create new context!");
     return nsnull;
   }
 
   JS_SetContextPrivate(workerCx, aWorkerPrivate);
 
   JS_SetErrorReporter(workerCx, ErrorReporter);
 
   JS_SetOperationCallback(workerCx, OperationCallback);
 
-  JS_SetNativeStackQuota(workerCx, WORKER_CONTEXT_NATIVE_STACK_LIMIT);
-
   NS_ASSERTION((aWorkerPrivate->GetJSContextOptions() &
                 kRequiredJSContextOptions) == kRequiredJSContextOptions,
                "Somehow we lost our required options!");
   JS_SetOptions(workerCx, aWorkerPrivate->GetJSContextOptions());
 
 #ifdef JS_GC_ZEAL
   {
     PRUint8 zeal = aWorkerPrivate->GetGCZeal();
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -150,16 +150,18 @@ SwapToISupportsArray(SmartPtr<T>& aSrc,
   T* raw = nsnull;
   aSrc.swap(raw);
 
   nsISupports* rawSupports =
     static_cast<typename ISupportsBaseInfo<T>::ISupportsBase*>(raw);
   dest->swap(rawSupports);
 }
 
+NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsWorkerMallocSizeOf, "js-worker")
+
 class WorkerMemoryReporter : public nsIMemoryMultiReporter
 {
   WorkerPrivate* mWorkerPrivate;
   nsCString mAddressString;
   nsCString mPathPrefix;
 
 public:
   NS_DECL_ISUPPORTS
@@ -227,17 +229,17 @@ public:
   }
 
   NS_IMETHOD
   CollectReports(nsIMemoryMultiReporterCallback* aCallback,
                  nsISupports* aClosure)
   {
     AssertIsOnMainThread();
 
-    JS::RuntimeStats rtStats(xpc::JsMallocSizeOf, xpc::GetCompartmentName,
+    JS::RuntimeStats rtStats(JsWorkerMallocSizeOf, xpc::GetCompartmentName,
                              xpc::DestroyCompartmentName);
     nsresult rv = CollectForRuntime(/* isQuick = */false, &rtStats);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     // Always report, even if we're disabled, so that we at least get an entry
     // in about::memory.
@@ -1518,17 +1520,17 @@ public:
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     JSAutoSuspendRequest asr(aCx);
 
     *mSucceeded = mIsQuick
-      ? JS::GetExplicitNonHeapForRuntime(JS_GetRuntime(aCx), static_cast<int64_t*>(mData), xpc::JsMallocSizeOf)
+      ? JS::GetExplicitNonHeapForRuntime(JS_GetRuntime(aCx), static_cast<int64_t*>(mData), JsWorkerMallocSizeOf)
       : JS::CollectRuntimeStats(JS_GetRuntime(aCx), static_cast<JS::RuntimeStats*>(mData));
 
     {
       MutexAutoLock lock(mMutex);
       mDone = true;
       mCondVar.Notify();
     }
 
--- a/embedding/android/AndroidManifest.xml.in
+++ b/embedding/android/AndroidManifest.xml.in
@@ -4,19 +4,27 @@
       package="@ANDROID_PACKAGE_NAME@"
       android:installLocation="auto"
       android:versionCode="@ANDROID_VERSION_CODE@"
       android:versionName="@MOZ_APP_VERSION@"
 #ifdef MOZ_ANDROID_SHARED_ID
       android:sharedUserId="@MOZ_ANDROID_SHARED_ID@"
 #endif
       >
+
     <uses-sdk android:minSdkVersion="5"
               android:targetSdkVersion="11"/>
 
+#ifdef MOZ_TABLETS_ONLY
+    <supports-screens android:smallScreens="false"
+                      android:normalScreens="false"
+                      android:largeScreens="false"
+                      android:xlargeScreens="true" />
+#endif
+
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
 
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
     <uses-permission android:name="android.permission.VIBRATE"/>
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -1787,10 +1787,10 @@ public class GeckoAppShell
         GeckoNetworkManager.getInstance().enableNotifications();
     }
 
     public static void disableNetworkNotifications() {
         GeckoNetworkManager.getInstance().disableNotifications();
     }
 
     // This is only used in Native Fennec.
-    public static void preventPanning() { }
+    public static void setPreventPanning(final boolean aPreventPanning) { }
 }
--- a/embedding/android/Makefile.in
+++ b/embedding/android/Makefile.in
@@ -74,31 +74,37 @@ PROCESSEDJAVAFILES = \
 
 ifneq (,$(findstring -march=armv7,$(OS_CFLAGS)))
 MIN_CPU_VERSION=7
 else
 MIN_CPU_VERSION=5
 endif
 
 ifeq (,$(ANDROID_VERSION_CODE))
-ANDROID_VERSION_CODE=$(shell $(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid | cut -c1-10)
+# increment the version code by 1 so xul fennec will win any compatability ties
+ANDROID_VERSION_CODE=$(shell echo `$(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid | cut -c1-10` + 1 | bc)
 endif
 
 DEFINES += \
   -DANDROID_PACKAGE_NAME=$(ANDROID_PACKAGE_NAME) \
   -DMOZ_APP_DISPLAYNAME="$(MOZ_APP_DISPLAYNAME)" \
   -DMOZ_APP_NAME=$(MOZ_APP_NAME) \
   -DMOZ_APP_VERSION=$(MOZ_APP_VERSION) \
   -DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME) \
   -DMOZ_MIN_CPU_VERSION=$(MIN_CPU_VERSION) \
   -DMOZ_CRASHREPORTER=$(MOZ_CRASHREPORTER) \
   -DANDROID_VERSION_CODE=$(ANDROID_VERSION_CODE) \
   -DMOZILLA_OFFICIAL=$(MOZILLA_OFFICIAL) \
   $(NULL)
 
+MOZ_MOBILE_COMPAT = @MOZ_MOBILE_COMPAT@
+ifeq (Tablets,$(MOZ_MOBILE_COMPAT))
+DEFINES += -DMOZ_TABLETS_ONLY=1
+endif
+
 GARBAGE += \
   AndroidManifest.xml  \
   classes.dex  \
   $(PROCESSEDJAVAFILES) \
   gecko.ap_  \
   res/values/strings.xml \
   R.java \
   $(NULL)
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -1103,18 +1103,16 @@ GLContext::ResizeOffscreenFBO(const gfxI
 
     const bool useDrawMSFBO = (samples > 0);
 
     if (!useDrawMSFBO && !aUseReadFBO) {
         // Early out, as no FBO resize work is necessary.
         return true;
     }
 
-    const bool firstTime = (mOffscreenDrawFBO == 0 && mOffscreenReadFBO == 0);
-
     GLuint curBoundFramebufferDraw = 0;
     GLuint curBoundFramebufferRead = 0;
     GLuint curBoundRenderbuffer = 0;
     GLuint curBoundTexture = 0;
 
     GLint viewport[4];
 
     const bool useDepthStencil =
@@ -1407,40 +1405,33 @@ GLContext::ResizeOffscreenFBO(const gfxI
 
     mOffscreenSize = aSize;
     mOffscreenActualSize = aSize;
 
     mActualFormat = cf;
 
 #ifdef DEBUG
     if (DebugMode()) {
-        printf_stderr("%s %dx%d offscreen FBO: r: %d g: %d b: %d a: %d depth: %d stencil: %d samples: %d\n",
-                      firstTime ? "Created" : "Resized",
+        printf_stderr("Resized %dx%d offscreen FBO: r: %d g: %d b: %d a: %d depth: %d stencil: %d samples: %d\n",
                       mOffscreenActualSize.width, mOffscreenActualSize.height,
                       mActualFormat.red, mActualFormat.green, mActualFormat.blue, mActualFormat.alpha,
                       mActualFormat.depth, mActualFormat.stencil, mActualFormat.samples);
     }
 #endif
 
     // Make sure we know that the buffers are new and thus dirty:
     ForceDirtyFBOs();
 
     // We're good, and the framebuffer is already attached.
     // Now restore the GL state back to what it was before the resize took place.
     BindDrawFBO(curBoundFramebufferDraw);
     BindReadFBO(curBoundFramebufferRead);
     fBindTexture(LOCAL_GL_TEXTURE_2D, curBoundTexture);
     fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, curBoundRenderbuffer);
-
-    // -don't- restore the viewport the first time through this, since
-    // the previous one isn't valid.
-    if (firstTime)
-        fViewport(0, 0, aSize.width, aSize.height); // XXX This is coming out in 711642
-    else
-        fViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
+    fViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
 
     return true;
 }
 
 void
 GLContext::DeleteOffscreenFBO()
 {
     fDeleteFramebuffers(1, &mOffscreenDrawFBO);
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ImageLayers.cpp
@@ -0,0 +1,275 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Bas Schouten <bschouten@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the 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 "mozilla/ipc/Shmem.h"
+#include "ImageLayers.h"
+#include "gfxImageSurface.h"
+#include "yuv_convert.h"
+
+#ifdef XP_MACOSX
+#include "nsCoreAnimationSupport.h"
+#endif
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace layers {
+
+already_AddRefed<Image>
+ImageFactory::CreateImage(const Image::Format *aFormats,
+                          PRUint32 aNumFormats,
+                          const gfxIntSize &,
+                          BufferRecycleBin *aRecycleBin)
+{
+  if (!aNumFormats) {
+    return nsnull;
+  }
+  nsRefPtr<Image> img;
+  if (FormatInList(aFormats, aNumFormats, Image::PLANAR_YCBCR)) {
+    img = new PlanarYCbCrImage(aRecycleBin);
+  } else if (FormatInList(aFormats, aNumFormats, Image::CAIRO_SURFACE)) {
+    img = new CairoImage();
+#ifdef XP_MACOSX
+  } else if (FormatInList(aFormats, aNumFormats, Image::MAC_IO_SURFACE)) {
+    img = new MacIOSurfaceImage();
+#endif
+  }
+  return img.forget();
+}
+
+BufferRecycleBin::BufferRecycleBin()
+  : mLock("mozilla.layers.BufferRecycleBin.mLock")
+{
+}
+
+void
+BufferRecycleBin::RecycleBuffer(PRUint8* aBuffer, PRUint32 aSize)
+{
+  MutexAutoLock lock(mLock);
+
+  if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) {
+    mRecycledBuffers.Clear();
+  }
+  mRecycledBufferSize = aSize;
+  mRecycledBuffers.AppendElement(aBuffer);
+}
+
+PRUint8*
+BufferRecycleBin::GetBuffer(PRUint32 aSize)
+{
+  MutexAutoLock lock(mLock);
+
+  if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize)
+    return new PRUint8[aSize];
+
+  PRUint32 last = mRecycledBuffers.Length() - 1;
+  PRUint8* result = mRecycledBuffers[last].forget();
+  mRecycledBuffers.RemoveElementAt(last);
+  return result;
+}
+
+ImageContainer::~ImageContainer()
+{
+}
+
+already_AddRefed<Image>
+ImageContainer::CreateImage(const Image::Format *aFormats,
+                            PRUint32 aNumFormats)
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  return mImageFactory->CreateImage(aFormats, aNumFormats, mScaleHint, mRecycleBin);
+}
+
+void
+ImageContainer::SetCurrentImage(Image *aImage)
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  mActiveImage = aImage;
+  CurrentImageChanged();
+}
+
+already_AddRefed<gfxASurface>
+ImageContainer::GetCurrentAsSurface(gfxIntSize *aSize)
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  if (!mActiveImage) {
+    return nsnull;
+  }
+
+  *aSize = mActiveImage->GetSize();
+  return mActiveImage->GetAsSurface();
+}
+
+gfxIntSize
+ImageContainer::GetCurrentSize()
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  if (!mActiveImage) {
+    return gfxIntSize(0,0);
+  }
+
+  return mActiveImage->GetSize();
+}
+
+PlanarYCbCrImage::PlanarYCbCrImage(BufferRecycleBin *aRecycleBin)
+  : Image(nsnull, PLANAR_YCBCR)
+  , mBufferSize(0)
+  , mRecycleBin(aRecycleBin)
+{
+}
+
+PlanarYCbCrImage::~PlanarYCbCrImage()
+{
+  if (mBuffer) {
+    mRecycleBin->RecycleBuffer(mBuffer.forget(), mBufferSize);
+  }
+}
+
+PRUint8* 
+PlanarYCbCrImage::AllocateBuffer(PRUint32 aSize)
+{
+  return mRecycleBin->GetBuffer(aSize); 
+}
+
+void
+PlanarYCbCrImage::CopyData(const Data& aData)
+{
+  mData = aData;
+
+  mData.mYStride = mData.mYSize.width;
+  mData.mCbCrStride = mData.mCbCrSize.width;
+
+  // update buffer size
+  mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
+                mData.mYStride * mData.mYSize.height;
+
+  // get new buffer
+  mBuffer = AllocateBuffer(mBufferSize); 
+  if (!mBuffer)
+    return;
+
+  mData.mYChannel = mBuffer;
+  mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
+  mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
+
+  for (int i = 0; i < mData.mYSize.height; i++) {
+    memcpy(mData.mYChannel + i * mData.mYStride,
+           aData.mYChannel + i * aData.mYStride,
+           mData.mYStride);
+  }
+  for (int i = 0; i < mData.mCbCrSize.height; i++) {
+    memcpy(mData.mCbChannel + i * mData.mCbCrStride,
+           aData.mCbChannel + i * aData.mCbCrStride,
+           mData.mCbCrStride);
+    memcpy(mData.mCrChannel + i * mData.mCbCrStride,
+           aData.mCrChannel + i * aData.mCbCrStride,
+           mData.mCbCrStride);
+  }
+
+  mSize = aData.mPicSize;
+}
+
+void
+PlanarYCbCrImage::SetData(const Data &aData)
+{
+  CopyData(aData);
+}
+
+already_AddRefed<gfxASurface>
+PlanarYCbCrImage::GetAsSurface()
+{
+  if (mSurface) {
+    nsRefPtr<gfxASurface> result = mSurface.get();
+    return result.forget();
+  }
+
+  nsRefPtr<gfxImageSurface> imageSurface =
+    new gfxImageSurface(mSize, gfxASurface::ImageFormatRGB24);
+  
+  gfx::YUVType type = 
+    gfx::TypeFromSize(mData.mYSize.width,
+                      mData.mYSize.height,
+                      mData.mCbCrSize.width,
+                      mData.mCbCrSize.height);
+
+  // Convert from YCbCr to RGB now
+  gfx::ConvertYCbCrToRGB32(mData.mYChannel,
+                           mData.mCbChannel,
+                           mData.mCrChannel,
+                           imageSurface->Data(),
+                           mData.mPicX,
+                           mData.mPicY,
+                           mData.mPicSize.width,
+                           mData.mPicSize.height,
+                           mData.mYStride,
+                           mData.mCbCrStride,
+                           imageSurface->Stride(),
+                           type);
+
+  mSurface = imageSurface;
+
+  return imageSurface.forget().get();
+}
+
+#ifdef XP_MACOSX
+void
+MacIOSurfaceImage::SetData(const Data& aData)
+{
+  mIOSurface = nsIOSurface::LookupSurface(aData.mIOSurface->GetIOSurfaceID());
+  mSize = gfxIntSize(mIOSurface->GetWidth(), mIOSurface->GetHeight());
+}
+
+already_AddRefed<gfxASurface>
+MacIOSurfaceImage::GetAsSurface()
+{
+  return mIOSurface->GetAsSurface();
+}
+
+void
+MacIOSurfaceImage::Update(ImageContainer* aContainer)
+{
+  if (mUpdateCallback) {
+    mUpdateCallback(aContainer, mPluginInstanceOwner);
+  }
+}
+#endif
+
+}
+}
--- a/gfx/layers/ImageLayers.h
+++ b/gfx/layers/ImageLayers.h
@@ -41,30 +41,42 @@
 #include "Layers.h"
 
 #include "nsISupportsImpl.h"
 #include "gfxPattern.h"
 #include "nsThreadUtils.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/mozalloc.h"
+#include "mozilla/Mutex.h"
+#include "gfxPlatform.h"
 
-class nsIOSurface;
+#ifdef XP_MACOSX
+#include "nsIOSurface.h"
+#endif
 
 namespace mozilla {
 namespace layers {
 
 enum StereoMode {
   STEREO_MODE_MONO,
   STEREO_MODE_LEFT_RIGHT,
   STEREO_MODE_RIGHT_LEFT,
   STEREO_MODE_BOTTOM_TOP,
   STEREO_MODE_TOP_BOTTOM
 };
 
+struct ImageBackendData
+{
+  virtual ~ImageBackendData() {}
+
+protected:
+  ImageBackendData() {}
+};
+
 /**
  * A class representing a buffer of pixel data. The data can be in one
  * of various formats including YCbCr.
  * 
  * Create an image using an ImageContainer. Fill the image with data, and
  * then call ImageContainer::SetImage to display it. An image must not be
  * modified after calling SetImage. Image implementations do not need to
  * perform locking; when filling an Image, the Image client is responsible
@@ -99,152 +111,225 @@ public:
      * 
      * Images in CAIRO_SURFACE format should only be created and
      * manipulated on the main thread, since the underlying cairo surface
      * is main-thread-only.
      */
     CAIRO_SURFACE,
 
     /**
-     * The MAC_IO_SURFACE format creates a MacIOSurfaceImage. This
-     * is only supported on Mac with OpenGL layers.
+     * The MAC_IO_SURFACE format creates a MacIOSurfaceImage.
      *
      * It wraps an IOSurface object and binds it directly to a GL texture.
      */
     MAC_IO_SURFACE
   };
 
   Format GetFormat() { return mFormat; }
   void* GetImplData() { return mImplData; }
 
+  virtual already_AddRefed<gfxASurface> GetAsSurface() = 0;
+  virtual gfxIntSize GetSize() = 0;
+
+  ImageBackendData* GetBackendData(LayerManager::LayersBackend aBackend)
+  { return mBackendData[aBackend]; }
+  void SetBackendData(LayerManager::LayersBackend aBackend, ImageBackendData* aData)
+  { mBackendData[aBackend] = aData; }
+
 protected:
   Image(void* aImplData, Format aFormat) :
     mImplData(aImplData),
     mFormat(aFormat)
   {}
 
+  nsAutoPtr<ImageBackendData> mBackendData[LayerManager::LAYERS_LAST];
+
   void* mImplData;
   Format mFormat;
 };
 
 /**
+ * A RecycleBin is owned by an ImageContainer. We store buffers in it that we
+ * want to recycle from one image to the next.It's a separate object from 
+ * ImageContainer because images need to store a strong ref to their RecycleBin
+ * and we must avoid creating a reference loop between an ImageContainer and
+ * its active image.
+ */
+class BufferRecycleBin {
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecycleBin)
+
+  typedef mozilla::gl::GLContext GLContext;
+
+public:
+  BufferRecycleBin();
+
+  void RecycleBuffer(PRUint8* aBuffer, PRUint32 aSize);
+  // Returns a recycled buffer of the right size, or allocates a new buffer.
+  PRUint8* GetBuffer(PRUint32 aSize);
+
+private:
+  typedef mozilla::Mutex Mutex;
+
+  // This protects mRecycledBuffers, mRecycledBufferSize, mRecycledTextures
+  // and mRecycledTextureSizes
+  Mutex mLock;
+
+  // We should probably do something to prune this list on a timer so we don't
+  // eat excess memory while video is paused...
+  nsTArray<nsAutoArrayPtr<PRUint8> > mRecycledBuffers;
+  // This is only valid if mRecycledBuffers is non-empty
+  PRUint32 mRecycledBufferSize;
+};
+
+/**
+ * Returns true if aFormat is in the given format array.
+ */
+static inline bool
+FormatInList(const Image::Format* aFormats, PRUint32 aNumFormats,
+             Image::Format aFormat)
+{
+  for (PRUint32 i = 0; i < aNumFormats; ++i) {
+    if (aFormats[i] == aFormat) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/**
+ * A class that manages Image creation for a LayerManager. The only reason
+ * we need a separate class here is that LayerMananers aren't threadsafe
+ * (because layers can only be used on the main thread) and we want to
+ * be able to create images from any thread, to facilitate video playback
+ * without involving the main thread, for example.
+ * Different layer managers can implement child classes of this making it
+ * possible to create layer manager specific images.
+ * This class is not meant to be used directly but rather can be set on an
+ * image container. This is usually done by the layer system internally and
+ * not explicitly by users. For PlanarYCbCr or Cairo images the default
+ * implementation will creates images whose data lives in system memory, for
+ * MacIOSurfaces the default implementation will be a simple nsIOSurface
+ * wrapper.
+ */
+
+class THEBES_API ImageFactory
+{
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageFactory)
+protected:
+  friend class ImageContainer;
+
+  ImageFactory() {}
+  virtual ~ImageFactory() {}
+
+  virtual already_AddRefed<Image> CreateImage(const Image::Format* aFormats,
+                                              PRUint32 aNumFormats,
+                                              const gfxIntSize &aScaleHint,
+                                              BufferRecycleBin *aRecycleBin);
+
+};
+
+/**
  * A class that manages Images for an ImageLayer. The only reason
  * we need a separate class here is that ImageLayers aren't threadsafe
  * (because layers can only be used on the main thread) and we want to
  * be able to set the current Image from any thread, to facilitate
  * video playback without involving the main thread, for example.
  */
 class THEBES_API ImageContainer {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer)
 
 public:
   ImageContainer() :
     mReentrantMonitor("ImageContainer.mReentrantMonitor"),
     mPaintCount(0),
-    mPreviousImagePainted(false)
+    mPreviousImagePainted(false),
+    mImageFactory(new ImageFactory()),
+    mRecycleBin(new BufferRecycleBin())
   {}
 
-  virtual ~ImageContainer() {}
+  ~ImageContainer();
 
   /**
    * Create an Image in one of the given formats.
    * Picks the "best" format from the list and creates an Image of that
    * format.
    * Returns null if this backend does not support any of the formats.
    * Can be called on any thread. This method takes mReentrantMonitor
    * when accessing thread-shared state.
    */
-  virtual already_AddRefed<Image> CreateImage(const Image::Format* aFormats,
-                                              PRUint32 aNumFormats) = 0;
+  already_AddRefed<Image> CreateImage(const Image::Format* aFormats,
+                                      PRUint32 aNumFormats);
 
   /**
    * Set an Image as the current image to display. The Image must have
    * been created by this ImageContainer.
    * Can be called on any thread. This method takes mReentrantMonitor
    * when accessing thread-shared state.
    * 
    * The Image data must not be modified after this method is called!
    */
-  virtual void SetCurrentImage(Image* aImage) = 0;
-
-  /**
-   * Ask any PlanarYCbCr images created by this container to delay
-   * YUV -> RGB conversion until draw time. See PlanarYCbCrImage::SetDelayedConversion.
-   */
-  virtual void SetDelayedConversion(bool aDelayed) {}
+  void SetCurrentImage(Image* aImage);
 
   /**
    * Get the current Image.
    * This has to add a reference since otherwise there are race conditions
    * where the current image is destroyed before the caller can add
    * a reference.
    * Can be called on any thread. This method takes mReentrantMonitor
    * when accessing thread-shared state.
    * Implementations must call CurrentImageChanged() while holding
    * mReentrantMonitor.
    */
-  virtual already_AddRefed<Image> GetCurrentImage() = 0;
+  already_AddRefed<Image> GetCurrentImage()