merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 12 Dec 2013 16:09:30 +0100
changeset 160266 0cf1107e27916d94756fc088f8986b77275072ce
parent 160265 61bcbf08ca46e27459f82337a86e5aeb6ca64e23 (current diff)
parent 160145 07e7a99841a64c59faf7ba1bd007e791c0c07b83 (diff)
child 160267 defc9b99115fbd96fa18126e599cd3d3fda33175
push id37540
push userkwierso@gmail.com
push dateFri, 13 Dec 2013 03:23:05 +0000
treeherdermozilla-inbound@1bc33fa19b24 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone29.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 mozilla-central to fx-team
CLOBBER
build/docs/Vagrantfile
build/docs/conf.py
build/docs/python/makeutils.rst
build/docs/python/mozbuild.action.rst
build/docs/python/mozbuild.backend.rst
build/docs/python/mozbuild.compilation.rst
build/docs/python/mozbuild.controller.rst
build/docs/python/mozbuild.frontend.rst
build/docs/python/mozbuild.rst
build/docs/python/mozpack.chrome.rst
build/docs/python/mozpack.packager.rst
build/docs/python/mozpack.rst
build/docs/python/mozversioncontrol.rst
extensions/widgetutils/Makefile.in
extensions/widgetutils/install.rdf
extensions/widgetutils/moz.build
extensions/widgetutils/src/Makefile.in
extensions/widgetutils/src/moz.build
extensions/widgetutils/src/nsWidgetUtils.cpp
mobile/android/base/moz.build
mobile/android/base/resources/drawable-hdpi/awesomebar_sep_left.9.png
mobile/android/base/resources/drawable-hdpi/awesomebar_sep_right.9.png
mobile/android/base/resources/drawable-hdpi/awesomebar_tab_center.9.png
mobile/android/base/resources/drawable-hdpi/awesomebar_tab_left.9.png
mobile/android/base/resources/drawable-hdpi/awesomebar_tab_right.9.png
mobile/android/base/resources/drawable-hdpi/ic_notif_button_cancel.png
mobile/android/base/resources/drawable-hdpi/ic_notif_button_pause.png
mobile/android/base/resources/drawable-hdpi/ic_notif_button_resume.png
mobile/android/base/resources/drawable-mdpi/awesomebar_sep_left.9.png
mobile/android/base/resources/drawable-mdpi/awesomebar_sep_right.9.png
mobile/android/base/resources/drawable-mdpi/awesomebar_tab_center.9.png
mobile/android/base/resources/drawable-mdpi/awesomebar_tab_left.9.png
mobile/android/base/resources/drawable-mdpi/awesomebar_tab_right.9.png
mobile/android/base/resources/drawable-mdpi/crash_reporter.png
mobile/android/base/resources/drawable-mdpi/ic_notif_button_cancel.png
mobile/android/base/resources/drawable-mdpi/ic_notif_button_pause.png
mobile/android/base/resources/drawable-mdpi/ic_notif_button_resume.png
mobile/android/base/resources/drawable-xhdpi/awesomebar_sep_left.9.png
mobile/android/base/resources/drawable-xhdpi/awesomebar_sep_right.9.png
mobile/android/base/resources/drawable-xhdpi/awesomebar_tab_center.9.png
mobile/android/base/resources/drawable-xhdpi/awesomebar_tab_left.9.png
mobile/android/base/resources/drawable-xhdpi/awesomebar_tab_right.9.png
mobile/android/base/resources/drawable-xhdpi/ic_notif_button_cancel.png
mobile/android/base/resources/drawable-xhdpi/ic_notif_button_pause.png
mobile/android/base/resources/drawable-xhdpi/ic_notif_button_resume.png
mobile/android/base/resources/drawable-xlarge-hdpi-v11/awesomebar_sep_left.9.png
mobile/android/base/resources/drawable-xlarge-hdpi-v11/awesomebar_sep_right.9.png
mobile/android/base/resources/drawable-xlarge-hdpi-v11/awesomebar_tab_center.9.png
mobile/android/base/resources/drawable-xlarge-hdpi-v11/awesomebar_tab_left.9.png
mobile/android/base/resources/drawable-xlarge-hdpi-v11/awesomebar_tab_right.9.png
mobile/android/base/resources/drawable-xlarge-mdpi-v11/awesomebar_sep_left.9.png
mobile/android/base/resources/drawable-xlarge-mdpi-v11/awesomebar_sep_right.9.png
mobile/android/base/resources/drawable-xlarge-mdpi-v11/awesomebar_tab_center.9.png
mobile/android/base/resources/drawable-xlarge-mdpi-v11/awesomebar_tab_left.9.png
mobile/android/base/resources/drawable-xlarge-mdpi-v11/awesomebar_tab_right.9.png
mobile/android/base/resources/drawable-xlarge-xhdpi-v11/awesomebar_sep_left.9.png
mobile/android/base/resources/drawable-xlarge-xhdpi-v11/awesomebar_sep_right.9.png
mobile/android/base/resources/drawable-xlarge-xhdpi-v11/awesomebar_tab_center.9.png
mobile/android/base/resources/drawable-xlarge-xhdpi-v11/awesomebar_tab_left.9.png
mobile/android/base/resources/drawable-xlarge-xhdpi-v11/awesomebar_tab_right.9.png
mobile/android/base/resources/layout/crash_reporter.xml
mobile/android/branding/aurora/content/fennec_144x144.png
mobile/android/branding/aurora/content/fennec_48x48.png
mobile/android/branding/aurora/content/fennec_72x72.png
mobile/android/branding/aurora/content/fennec_96x96.png
mobile/android/branding/beta/content/fennec_144x144.png
mobile/android/branding/beta/content/fennec_48x48.png
mobile/android/branding/beta/content/fennec_72x72.png
mobile/android/branding/beta/content/fennec_96x96.png
mobile/android/branding/nightly/content/fennec_144x144.png
mobile/android/branding/nightly/content/fennec_48x48.png
mobile/android/branding/nightly/content/fennec_72x72.png
mobile/android/branding/nightly/content/fennec_96x96.png
mobile/android/branding/official/content/fennec_144x144.png
mobile/android/branding/official/content/fennec_48x48.png
mobile/android/branding/official/content/fennec_72x72.png
mobile/android/branding/official/content/fennec_96x96.png
mobile/android/branding/unofficial/content/fennec_144x144.png
mobile/android/branding/unofficial/content/fennec_48x48.png
mobile/android/branding/unofficial/content/fennec_72x72.png
mobile/android/branding/unofficial/content/fennec_96x96.png
netwerk/sctp/datachannel/Makefile.in
services/datareporting/README.rst
services/healthreport/README.rst
services/metrics/README.rst
--- a/CLOBBER
+++ b/CLOBBER
@@ -14,8 +14,13 @@
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 Bug 942231 needs a clobber -- JNI wrappers need to be re-generated.
+and
+Bug 934646 needs a clobber -- the icon resources previously copied
+into $OBJDIR/mobile/android/base/res will conflict with those in
+$BRANDING_DIRECTORY/res.
+
--- a/accessible/src/html/HTMLFormControlAccessible.cpp
+++ b/accessible/src/html/HTMLFormControlAccessible.cpp
@@ -9,28 +9,28 @@
 #include "nsAccUtils.h"
 #include "nsEventShell.h"
 #include "nsTextEquivUtils.h"
 #include "Relation.h"
 #include "Role.h"
 #include "States.h"
 
 #include "nsContentList.h"
-#include "nsCxPusher.h"
 #include "mozilla/dom/HTMLInputElement.h"
 #include "nsIAccessibleRelation.h"
 #include "nsIDOMNSEditableElement.h"
 #include "nsIDOMHTMLTextAreaElement.h"
 #include "nsIEditor.h"
 #include "nsIFormControl.h"
 #include "nsINameSpaceManager.h"
 #include "nsIPersistentProperties2.h"
 #include "nsISelectionController.h"
 #include "nsIServiceManager.h"
 #include "nsITextControlFrame.h"
+#include "mozilla/dom/ScriptSettings.h"
 
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::a11y;
 
@@ -463,18 +463,17 @@ HTMLTextFieldAccessible::GetEditor() con
 {
   nsCOMPtr<nsIDOMNSEditableElement> editableElt(do_QueryInterface(mContent));
   if (!editableElt)
     return nullptr;
 
   // nsGenericHTMLElement::GetEditor has a security check.
   // Make sure we're not restricted by the permissions of
   // whatever script is currently running.
-  nsCxPusher pusher;
-  pusher.PushNull();
+  mozilla::dom::AutoSystemCaller asc;
 
   nsCOMPtr<nsIEditor> editor;
   editableElt->GetEditor(getter_AddRefs(editor));
 
   return editor.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "8a192fcf2927a866574996b4895426213e01a325", 
+    "revision": "5bfef5faac50d14e055f642a44ed2df8483fb2fe", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -52,13 +52,13 @@ MOZ_TIME_MANAGER=1
 
 MOZ_B2G_CERTDATA=1
 MOZ_PAY=1
 MOZ_TOOLKIT_SEARCH=
 MOZ_PLACES=
 MOZ_B2G=1
 
 if test "$OS_TARGET" = "Android"; then
-MOZ_NUWA_PROCESS=0
+MOZ_NUWA_PROCESS=
 fi
 MOZ_FOLD_LIBS=1
 
 MOZ_JSDOWNLOADS=1
--- a/browser/devtools/commandline/test/browser_cmd_screenshot.js
+++ b/browser/devtools/commandline/test/browser_cmd_screenshot.js
@@ -165,31 +165,27 @@ function addTabWithToolbarRunTests(win) 
 }
 
 function addWindow(windowOptions, callback) {
   waitForExplicitFinish();
   let deferred = promise.defer();
 
   let win = OpenBrowserWindow(windowOptions);
 
-  let onLoad = function() {
-    win.removeEventListener("load", onLoad, false);
-
+  whenDelayedStartupFinished(win, function() {
     // Would like to get rid of this executeSoon, but without it the url
     // (TEST_URI) provided in addTabWithToolbarRunTests hasn't loaded
     executeSoon(function() {
       try {
         let reply = callback(win);
         promise.resolve(reply).then(function() {
           win.close();
           deferred.resolve();
         });
       }
       catch (ex) {
         deferred.reject(ex);
       }
     });
-  };
-
-  win.addEventListener("load", onLoad, false);
+  });
 
   return deferred.promise;
 }
--- a/browser/devtools/commandline/test/head.js
+++ b/browser/devtools/commandline/test/head.js
@@ -5,16 +5,25 @@
 const TEST_BASE_HTTP = "http://example.com/browser/browser/devtools/commandline/test/";
 const TEST_BASE_HTTPS = "https://example.com/browser/browser/devtools/commandline/test/";
 
 // Import the GCLI test helper
 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);
 Services.scriptloader.loadSubScript(testDir + "/mockCommands.js", this);
 
+function whenDelayedStartupFinished(aWindow, aCallback) {
+  Services.obs.addObserver(function observer(aSubject, aTopic) {
+    if (aWindow == aSubject) {
+      Services.obs.removeObserver(observer, aTopic);
+      executeSoon(aCallback);
+    }
+  }, "browser-delayed-startup-finished", false);
+}
+
 /**
  * Force GC on shutdown, because it seems that GCLI can outrun the garbage
  * collector in some situations, which causes test failures in later tests
  * Bug 774619 is an example.
  */
 registerCleanupFunction(function tearDown() {
   window.QueryInterface(Ci.nsIInterfaceRequestor)
       .getInterface(Ci.nsIDOMWindowUtils)
--- a/browser/metro/shell/commandexecutehandler/Makefile.in
+++ b/browser/metro/shell/commandexecutehandler/Makefile.in
@@ -1,14 +1,12 @@
 # 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/.
 
-NO_PROFILE_GUIDED_OPTIMIZE = 1
-
 include $(topsrcdir)/config/config.mk
 
 DIST_PROGRAM = CommandExecuteHandler$(BIN_SUFFIX)
 
 # Don't link against mozglue.dll
 MOZ_GLUE_LDFLAGS =
 MOZ_GLUE_PROGRAM_LDFLAGS =
 
--- a/browser/metro/shell/commandexecutehandler/moz.build
+++ b/browser/metro/shell/commandexecutehandler/moz.build
@@ -11,8 +11,10 @@ SOURCES += [
     'CommandExecuteHandler.cpp',
 ]
 
 # We want this exe in dist/bin
 DIST_SUBDIR = ''
 
 for var in ('UNICODE', '_UNICODE', 'NS_NO_XPCOM'):
     DEFINES[var] = True
+
+NO_PGO = True
--- a/browser/metro/shell/linktool/Makefile.in
+++ b/browser/metro/shell/linktool/Makefile.in
@@ -1,14 +1,12 @@
 # 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/.
 
-NO_PROFILE_GUIDED_OPTIMIZE = 1
-
 include $(topsrcdir)/config/config.mk
 
 OS_LIBS = \
 	kernel32.lib \
 	user32.lib \
 	ole32.lib \
 	shlwapi.lib \
 	shell32.lib \
--- a/browser/metro/shell/linktool/moz.build
+++ b/browser/metro/shell/linktool/moz.build
@@ -9,8 +9,10 @@ PROGRAM = 'linktool'
 SOURCES += [
     'linktool.cpp',
 ]
 
 DIST_SUBDIR = 'metro/install'
 
 for var in ('UNICODE', '_UNICODE'):
     DEFINES[var] = True
+
+NO_PGO = True
--- a/browser/metro/shell/testing/Makefile.in
+++ b/browser/metro/shell/testing/Makefile.in
@@ -4,18 +4,16 @@
 
 # static win runtime linking
 USE_STATIC_LIBS = 1
 
 # don't use moz glue libs
 MOZ_GLUE_LDFLAGS =
 MOZ_GLUE_PROGRAM_LDFLAGS =
 
-NO_PROFILE_GUIDED_OPTIMIZE = 1
-
 include $(topsrcdir)/config/config.mk
 
 OS_LIBS = \
 	kernel32.lib \
 	user32.lib \
 	ole32.lib \
 	shlwapi.lib \
 	propsys.lib \
--- a/browser/metro/shell/testing/moz.build
+++ b/browser/metro/shell/testing/moz.build
@@ -10,8 +10,10 @@ SOURCES += [
     'metrotestharness.cpp',
 ]
 
 # We want this exe in dist/bin
 DIST_SUBDIR = ''
 
 for var in ('UNICODE', '_UNICODE'):
     DEFINES[var] = True
+
+NO_PGO = True
--- a/build/ConfigStatus.py
+++ b/build/ConfigStatus.py
@@ -59,17 +59,19 @@ def config_status(topobjdir='.', topsrcd
 
     parser = OptionParser()
     parser.add_option('--recheck', dest='recheck', action='store_true',
                       help='update config.status by reconfiguring in the same conditions')
     parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
                       help='display verbose output')
     parser.add_option('-n', dest='not_topobjdir', action='store_true',
                       help='do not consider current directory as top object directory')
-    (options, args) = parser.parse_args()
+    parser.add_option('-d', '--diff', action='store_true',
+                      help='print diffs of changed files.')
+    options, args = parser.parse_args()
 
     # Without -n, the current directory is meant to be the top object directory
     if not options.not_topobjdir:
         topobjdir = os.path.abspath('.')
 
     env = ConfigEnvironment(topsrcdir, topobjdir, defines=defines,
             non_global_defines=non_global_defines, substs=substs)
 
@@ -93,8 +95,12 @@ def config_status(topobjdir='.', topsrcd
     log_manager.add_terminal_logging(level=log_level)
     log_manager.enable_unstructured()
 
     print('Reticulating splines...', file=sys.stderr)
     summary = backend.consume(definitions)
 
     for line in summary.summaries():
         print(line, file=sys.stderr)
+
+    if options.diff:
+        for path, diff in sorted(summary.file_diffs.items()):
+            print(diff)
deleted file mode 100644
--- a/build/docs/Vagrantfile
+++ /dev/null
@@ -1,13 +0,0 @@
-# -*- mode: ruby -*-
-# vi: set ft=ruby :
-
-# We intentionally use the old config format because Mozilla's Jenkins
-# server doesn't run a modern Vagrant.
-Vagrant::Config.run do |config|
-  config.vm.box = "precise64"
-  config.vm.box_url = "http://files.vagrantup.com/precise64.box"
-  config.vm.share_folder("gecko", "/gecko", "../..")
-  # Doxygen needs more than the default memory or it will swap and be
-  # extremely slow.
-  config.vm.customize ["modifyvm", :id, "--memory", 2048]
-end
deleted file mode 100644
--- a/build/docs/conf.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-from __future__ import unicode_literals
-
-import os
-import re
-
-from datetime import datetime
-
-
-here = os.path.abspath(os.path.dirname(__file__))
-mozilla_dir = os.path.normpath(os.path.join(here, '..', '..'))
-
-import mdn_theme
-
-extensions = [
-    'sphinx.ext.autodoc',
-    'sphinx.ext.graphviz',
-    'sphinx.ext.todo',
-    'mozbuild.sphinx',
-]
-
-templates_path = ['_templates']
-source_suffix = '.rst'
-master_doc = 'index'
-project = u'Mozilla Build System'
-year = datetime.now().year
-
-# Grab the version from the source tree's milestone.
-with open(os.path.join(mozilla_dir, 'config', 'milestone.txt'), 'rt') as fh:
-    for line in fh:
-        line = line.strip()
-
-        if not line or line.startswith('#'):
-            continue
-
-        release = line
-        break
-
-version = re.sub(r'[ab]\d+$', '', release)
-
-exclude_patterns = ['_build']
-pygments_style = 'sphinx'
-
-html_theme_path = [mdn_theme.get_theme_dir()]
-html_theme = 'mdn'
-
-html_static_path = ['_static']
-htmlhelp_basename = 'MozillaBuildSystemdoc'
--- a/build/docs/index.rst
+++ b/build/docs/index.rst
@@ -1,25 +1,18 @@
-==================================
-Mozilla Build System Documentation
-==================================
-
-Overview
-========
-
-.. toctree::
-   :maxdepth: 1
-
-   glossary
+============
+Build System
+============
 
 Important Concepts
 ==================
 .. toctree::
    :maxdepth: 1
 
+   glossary
    build-overview
    supported-configurations
    Mozconfig Files <mozconfigs>
    mozbuild-files
    mozbuild-symbols
    Profile Guided Optimization <pgo>
    slow
    environment-variables
@@ -36,29 +29,8 @@ mozbuild
 mozbuild is a Python package containing a lot of the code for the
 Mozilla build system.
 
 .. toctree::
    :maxdepth: 1
 
    mozbuild/index
    mozbuild/dumbmake
-
-Python Packages
-===============
-
-.. toctree::
-   :maxdepth: 2
-
-   python/codegen
-   python/makeutils
-   python/mozbuild
-   python/mozpack
-   python/mozversioncontrol
-
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
-
deleted file mode 100644
--- a/build/docs/python/makeutils.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-makeutils Module
-================
-
-.. automodule:: makeutils
-    :members:
-    :undoc-members:
-    :show-inheritance:
deleted file mode 100644
--- a/build/docs/python/mozbuild.action.rst
+++ /dev/null
@@ -1,35 +0,0 @@
-action Package
-==============
-
-:mod:`link_deps` Module
------------------------
-
-.. automodule:: mozbuild.action.link_deps
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`process_install_manifest` Module
---------------------------------------
-
-.. automodule:: mozbuild.action.process_install_manifest
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`xpccheck` Module
-----------------------
-
-.. automodule:: mozbuild.action.xpccheck
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`xpidl-process` Module
----------------------------
-
-.. automodule:: mozbuild.action.xpidl-process
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
deleted file mode 100644
--- a/build/docs/python/mozbuild.backend.rst
+++ /dev/null
@@ -1,35 +0,0 @@
-backend Package
-===============
-
-:mod:`base` Module
-------------------
-
-.. automodule:: mozbuild.backend.base
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`common` Module
---------------------
-
-.. automodule:: mozbuild.backend.common
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`configenvironment` Module
--------------------------------
-
-.. automodule:: mozbuild.backend.configenvironment
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`recursivemake` Module
----------------------------
-
-.. automodule:: mozbuild.backend.recursivemake
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
deleted file mode 100644
--- a/build/docs/python/mozbuild.compilation.rst
+++ /dev/null
@@ -1,11 +0,0 @@
-compilation Package
-===================
-
-:mod:`warnings` Module
-----------------------
-
-.. automodule:: mozbuild.compilation.warnings
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
deleted file mode 100644
--- a/build/docs/python/mozbuild.controller.rst
+++ /dev/null
@@ -1,19 +0,0 @@
-controller Package
-==================
-
-:mod:`building` Module
-----------------------
-
-.. automodule:: mozbuild.controller.building
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`clobber` Module
----------------------
-
-.. automodule:: mozbuild.controller.clobber
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
deleted file mode 100644
--- a/build/docs/python/mozbuild.frontend.rst
+++ /dev/null
@@ -1,51 +0,0 @@
-frontend Package
-================
-
-:mod:`data` Module
-------------------
-
-.. automodule:: mozbuild.frontend.data
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`emitter` Module
----------------------
-
-.. automodule:: mozbuild.frontend.emitter
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`mach_commands` Module
----------------------------
-
-.. automodule:: mozbuild.frontend.mach_commands
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`reader` Module
---------------------
-
-.. automodule:: mozbuild.frontend.reader
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`sandbox` Module
----------------------
-
-.. automodule:: mozbuild.frontend.sandbox
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`sandbox_symbols` Module
------------------------------
-
-.. automodule:: mozbuild.frontend.sandbox_symbols
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
deleted file mode 100644
--- a/build/docs/python/mozbuild.rst
+++ /dev/null
@@ -1,103 +0,0 @@
-mozbuild Package
-================
-
-:mod:`base` Module
-------------------
-
-.. automodule:: mozbuild.base
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`config` Module
---------------------
-
-.. automodule:: mozbuild.config
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`html_build_viewer` Module
--------------------------------
-
-.. automodule:: mozbuild.html_build_viewer
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`mach_commands` Module
----------------------------
-
-.. automodule:: mozbuild.mach_commands
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`makeutil` Module
-----------------------
-
-.. automodule:: mozbuild.makeutil
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`mozconfig` Module
------------------------
-
-.. automodule:: mozbuild.mozconfig
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`mozinfo` Module
----------------------
-
-.. automodule:: mozbuild.mozinfo
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`pythonutil` Module
-------------------------
-
-.. automodule:: mozbuild.pythonutil
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`sphinx` Module
---------------------
-
-.. automodule:: mozbuild.sphinx
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`util` Module
-------------------
-
-.. automodule:: mozbuild.util
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`virtualenv` Module
-------------------------
-
-.. automodule:: mozbuild.virtualenv
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
-
-    mozbuild.action
-    mozbuild.backend
-    mozbuild.compilation
-    mozbuild.controller
-    mozbuild.frontend
-    mozbuild.test
-
deleted file mode 100644
--- a/build/docs/python/mozpack.chrome.rst
+++ /dev/null
@@ -1,19 +0,0 @@
-chrome Package
-==============
-
-:mod:`flags` Module
--------------------
-
-.. automodule:: mozpack.chrome.flags
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`manifest` Module
-----------------------
-
-.. automodule:: mozpack.chrome.manifest
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
deleted file mode 100644
--- a/build/docs/python/mozpack.packager.rst
+++ /dev/null
@@ -1,35 +0,0 @@
-packager Package
-================
-
-:mod:`packager` Package
------------------------
-
-.. automodule:: mozpack.packager
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`formats` Module
----------------------
-
-.. automodule:: mozpack.packager.formats
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`l10n` Module
-------------------
-
-.. automodule:: mozpack.packager.l10n
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`unpack` Module
---------------------
-
-.. automodule:: mozpack.packager.unpack
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
deleted file mode 100644
--- a/build/docs/python/mozpack.rst
+++ /dev/null
@@ -1,76 +0,0 @@
-mozpack Package
-===============
-
-:mod:`copier` Module
---------------------
-
-.. automodule:: mozpack.copier
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`errors` Module
---------------------
-
-.. automodule:: mozpack.errors
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`executables` Module
--------------------------
-
-.. automodule:: mozpack.executables
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`files` Module
--------------------
-
-.. automodule:: mozpack.files
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`manifests` Module
------------------------
-
-.. automodule:: mozpack.manifests
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`mozjar` Module
---------------------
-
-.. automodule:: mozpack.mozjar
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`path` Module
-------------------
-
-.. automodule:: mozpack.path
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-:mod:`unify` Module
--------------------
-
-.. automodule:: mozpack.unify
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
-
-    mozpack.chrome
-    mozpack.packager
-    mozpack.test
-
deleted file mode 100644
--- a/build/docs/python/mozversioncontrol.rst
+++ /dev/null
@@ -1,11 +0,0 @@
-mozversioncontrol Package
-=========================
-
-:mod:`repoupdate` Module
-------------------------
-
-.. automodule:: mozversioncontrol.repoupdate
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -70,16 +70,17 @@ MACH_MODULES = [
     'python/mozbuild/mozbuild/mach_commands.py',
     'python/mozbuild/mozbuild/frontend/mach_commands.py',
     'testing/mach_commands.py',
     'testing/marionette/mach_commands.py',
     'testing/mochitest/mach_commands.py',
     'testing/xpcshell/mach_commands.py',
     'testing/talos/mach_commands.py',
     'testing/xpcshell/mach_commands.py',
+    'tools/docs/mach_commands.py',
     'tools/mercurial/mach_commands.py',
     'tools/mach_commands.py',
 ]
 
 
 CATEGORIES = {
     'build': {
         'short': 'Build Commands',
--- a/build/mobile/robocop/moz.build
+++ b/build/mobile/robocop/moz.build
@@ -1,11 +1,7 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-ANDROID_RESFILES = [
-    'res/values/strings.xml',
-]
-
 DEFINES['ANDROID_PACKAGE_NAME'] = CONFIG['ANDROID_PACKAGE_NAME']
--- a/build/mobile/sutagent/android/fencp/moz.build
+++ b/build/mobile/sutagent/android/fencp/moz.build
@@ -1,13 +1,5 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
-
-ANDROID_RESFILES = [
-    'res/drawable-hdpi/icon.png',
-    'res/drawable-ldpi/icon.png',
-    'res/drawable-mdpi/icon.png',
-    'res/layout/main.xml',
-    'res/values/strings.xml',
-]
--- a/build/mobile/sutagent/android/ffxcp/moz.build
+++ b/build/mobile/sutagent/android/ffxcp/moz.build
@@ -1,13 +1,5 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
-
-ANDROID_RESFILES = [
-    'res/drawable-hdpi/icon.png',
-    'res/drawable-ldpi/icon.png',
-    'res/drawable-mdpi/icon.png',
-    'res/layout/main.xml',
-    'res/values/strings.xml',
-]
--- a/build/mobile/sutagent/android/moz.build
+++ b/build/mobile/sutagent/android/moz.build
@@ -1,15 +1,5 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
-
-ANDROID_RESFILES = [
-    'res/drawable/ateamlogo.png',
-    'res/drawable/ic_stat_first.png',
-    'res/drawable/ic_stat_neterror.png',
-    'res/drawable/ic_stat_warning.png',
-    'res/drawable/icon.png',
-    'res/layout/main.xml',
-    'res/values/strings.xml',
-]
--- a/build/mobile/sutagent/android/watcher/moz.build
+++ b/build/mobile/sutagent/android/watcher/moz.build
@@ -1,16 +1,5 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
-
-ANDROID_RESFILES = [
-    'res/drawable-hdpi/ateamlogo.png',
-    'res/drawable-hdpi/icon.png',
-    'res/drawable-ldpi/ateamlogo.png',
-    'res/drawable-ldpi/icon.png',
-    'res/drawable-mdpi/ateamlogo.png',
-    'res/drawable-mdpi/icon.png',
-    'res/layout/main.xml',
-    'res/values/strings.xml',
-]
--- a/build/moz.build
+++ b/build/moz.build
@@ -1,14 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+SPHINX_TREES['build'] = 'docs'
+
 if CONFIG['OS_ARCH'] not in ('WINNT', 'OS2'):
     DIRS += ['unix']
 elif CONFIG['OS_ARCH'] == 'WINNT':
     DIRS += ['win32']
 
 if CONFIG['OS_TARGET'] == 'Android' and not CONFIG['MOZ_ANDROID_LIBSTDCXX']:
     DIRS += ['stlport']
 
--- a/build/unix/elfhack/Makefile.in
+++ b/build/unix/elfhack/Makefile.in
@@ -1,17 +1,15 @@
 #
 # 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/.
 
 INTERNAL_TOOLS = 1
 
-NO_PROFILE_GUIDED_OPTIMIZE = 1
-
 VPATH += $(topsrcdir)/build
 
 OS_CXXFLAGS := $(filter-out -fno-exceptions,$(OS_CXXFLAGS)) -fexceptions
 
 WRAP_LDFLAGS=
 
 include $(topsrcdir)/config/rules.mk
 
--- a/build/unix/elfhack/inject/Makefile.in
+++ b/build/unix/elfhack/inject/Makefile.in
@@ -1,15 +1,14 @@
 #
 # 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/.
 
 INTERNAL_TOOLS = 1
-NO_PROFILE_GUIDED_OPTIMIZE = 1
 
 include $(topsrcdir)/config/rules.mk
 
 export:: $(CSRCS:.c=.$(OBJ_SUFFIX))
 
 $(CSRCS): %.c: ../inject.c
 	cp $< $@
 
--- a/build/unix/elfhack/inject/moz.build
+++ b/build/unix/elfhack/inject/moz.build
@@ -13,8 +13,10 @@ elif CONFIG['TARGET_CPU'].startswith('ar
 else:
     cpu = CONFIG['TARGET_CPU']
 
 GENERATED_SOURCES += [
     "%s.c" % cpu,
 ]
 
 DEFINES['ELFHACK_BUILD'] = True
+
+NO_PGO = True
--- a/build/unix/elfhack/moz.build
+++ b/build/unix/elfhack/moz.build
@@ -20,8 +20,10 @@ if not CONFIG['CROSS_COMPILE']:
 HOST_SOURCES += [
     'elf.cpp',
     'elfhack.cpp',
 ]
 
 HOST_PROGRAM = 'elfhack'
 
 DEFINES['ELFHACK_BUILD'] = True
+
+NO_PGO = True
--- a/build/unix/stdc++compat/Makefile.in
+++ b/build/unix/stdc++compat/Makefile.in
@@ -1,12 +1,11 @@
 # 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/.
 
 STL_FLAGS =
 NO_EXPAND_LIBS = 1
-NO_PROFILE_GUIDED_OPTIMIZE = 1
 
 include $(topsrcdir)/config/rules.mk
 
 CXXFLAGS += -DMOZ_LIBSTDCXX_VERSION=$(MOZ_LIBSTDCXX_TARGET_VERSION)
 HOST_CXXFLAGS += -DMOZ_LIBSTDCXX_VERSION=$(MOZ_LIBSTDCXX_HOST_VERSION)
--- a/build/unix/stdc++compat/moz.build
+++ b/build/unix/stdc++compat/moz.build
@@ -10,8 +10,10 @@ if CONFIG['MOZ_LIBSTDCXX_TARGET_VERSION'
 
 if CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']:
     HOST_LIBRARY_NAME = 'host_stdc++compat'
     HOST_SOURCES += [
         'stdc++compat.cpp',
     ]
 
 FORCE_STATIC_LIB = True
+
+NO_PGO = True
--- a/build/virtualenv_packages.txt
+++ b/build/virtualenv_packages.txt
@@ -8,11 +8,12 @@ optional:setup.py:python/psutil:build_ex
 optional:psutil.pth:python/psutil
 which.pth:python/which
 ply.pth:other-licenses/ply/
 codegen.pth:python/codegen/
 mock.pth:python/mock-1.0.0
 mozilla.pth:build
 mozilla.pth:config
 mozilla.pth:xpcom/typelib/xpt/tools
+moztreedocs.pth:tools/docs
 copy:build/buildconfig.py
 packages.txt:testing/mozbase/packages.txt
 objdir:build
--- a/build/win32/Makefile.in
+++ b/build/win32/Makefile.in
@@ -1,14 +1,12 @@
 # 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/.
 
-NO_PROFILE_GUIDED_OPTIMIZE = 1
-
 ifdef ENABLE_TESTS
 
 USE_STATIC_LIBS = 1
 
 endif # ENABLE_TESTS
 
 MOZ_GLUE_LDFLAGS =
 
--- a/build/win32/moz.build
+++ b/build/win32/moz.build
@@ -9,8 +9,10 @@ if CONFIG['_MSC_VER'] and CONFIG['OS_TES
 
 TEST_DIRS += ['crashinjectdll']
 
 if CONFIG['ENABLE_TESTS']:
     PROGRAM = 'crashinject'
     SOURCES += [
         'crashinject.cpp',
     ]
+
+NO_PGO = True
--- a/config/config.mk
+++ b/config/config.mk
@@ -30,17 +30,17 @@ endif
 -include $(DEPTH)/.mozconfig.mk
 
 # Integrate with mozbuild-generated make files. We first verify that no
 # variables provided by the automatically generated .mk files are
 # present. If they are, this is a violation of the separation of
 # responsibility between Makefile.in and mozbuild files.
 _MOZBUILD_EXTERNAL_VARIABLES := \
   ANDROID_GENERATED_RESFILES \
-  ANDROID_RESFILES \
+  ANDROID_RES_DIRS \
   CMSRCS \
   CMMSRCS \
   CPP_UNIT_TESTS \
   DIRS \
   EXTRA_PP_COMPONENTS \
   EXTRA_PP_JS_MODULES \
   FORCE_SHARED_LIB \
   FORCE_STATIC_LIB \
@@ -65,16 +65,17 @@ endif
   TEST_DIRS \
   TIERS \
   TOOL_DIRS \
   XPCSHELL_TESTS \
   XPIDL_MODULE \
   $(NULL)
 
 _DEPRECATED_VARIABLES := \
+  ANDROID_RESFILES \
   MOCHITEST_FILES_PARTS \
   MOCHITEST_BROWSER_FILES_PARTS \
   SHORT_LIBNAME \
   $(NULL)
 
 ifndef EXTERNALLY_MANAGED_MAKE_FILE
 # Using $(firstword) may not be perfect. But it should be good enough for most
 # scenarios.
--- a/config/makefiles/java-build.mk
+++ b/config/makefiles/java-build.mk
@@ -2,49 +2,27 @@
 # vim:set ts=8 sw=8 sts=8 noet:
 #
 # 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/.
 
 ifndef INCLUDED_JAVA_BUILD_MK #{
 
-ifdef ANDROID_RESFILES #{
-ifndef IGNORE_ANDROID_RESFILES #{
-res-dep := .deps-copy-java-res
-
-GENERATED_DIRS += res
-GARBAGE        += $(res-dep)
-
-export:: $(res-dep)
-
-res-dep-preqs := \
-  $(addprefix $(srcdir)/,$(ANDROID_RESFILES)) \
-  $(call mkdir_deps,res) \
-  $(if $(IS_LANGUAGE_REPACK),FORCE) \
-  $(NULL)
-
-# nop-build: only copy res/ files when needed
-$(res-dep): $(res-dep-preqs)
-	$(call copy_dir,$(srcdir)/res,$(CURDIR)/res)
-	@$(TOUCH) $@
-endif #} IGNORE_ANDROID_RESFILES
-endif #} ANDROID_RESFILES
-
-
 ifdef JAVAFILES #{
 GENERATED_DIRS += classes
 
 export:: classes
 classes: $(call mkdir_deps,classes)
 endif #} JAVAFILES
 
 
 ifdef ANDROID_APK_NAME #{
-_ANDROID_RES_FLAG := -S $(or $(ANDROID_RES_DIR),res)
+android_res_dirs := $(addprefix $(srcdir)/,$(or $(ANDROID_RES_DIRS),res))
+_ANDROID_RES_FLAG := $(addprefix -S ,$(android_res_dirs))
 _ANDROID_ASSETS_FLAG := $(addprefix -A ,$(ANDROID_ASSETS_DIR))
 
 GENERATED_DIRS += classes
 
 classes.dex: $(call mkdir_deps,classes)
 classes.dex: R.java
 classes.dex: $(ANDROID_APK_NAME).ap_
 classes.dex: $(JAVAFILES)
@@ -52,17 +30,21 @@ classes.dex: $(JAVAFILES)
 	$(DX) --dex --output=$@ classes $(ANDROID_EXTRA_JARS)
 
 # R.java and $(ANDROID_APK_NAME).ap_ are both produced by aapt.  To
 # save an aapt invocation, we produce them both at the same time.
 
 R.java: .aapt.deps
 $(ANDROID_APK_NAME).ap_: .aapt.deps
 
-.aapt.deps: AndroidManifest.xml $(wildcard $(ANDROID_RES_DIR)) $(wildcard $(ANDROID_ASSETS_DIR))
+# This uses the fact that Android resource directories list all
+# resource files one subdirectory below the parent resource directory.
+android_res_files := $(wildcard $(addsuffix /*,$(wildcard $(addsuffix /*,$(android_res_dirs)))))
+
+.aapt.deps: AndroidManifest.xml $(android_res_files) $(wildcard $(ANDROID_ASSETS_DIR))
 	$(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar $(_ANDROID_RES_FLAG) $(_ANDROID_ASSETS_FLAG) \
 		-J ${@D} \
 		-F $(ANDROID_APK_NAME).ap_
 	@$(TOUCH) $@
 
 $(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex
 	cp $< $@
 	$(ZIP) -0 $@ classes.dex
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -637,17 +637,17 @@ endif
 ifdef IS_TOOL_DIR
 # One would think "tools:: libs" would work, but it turns out that combined with
 # bug 907365, this makes make forget to run some rules sometimes.
 tools::
 	@$(MAKE) libs
 endif
 
 ##############################################
-ifndef NO_PROFILE_GUIDED_OPTIMIZE
+ifneq (1,$(NO_PROFILE_GUIDED_OPTIMIZE))
 ifdef MOZ_PROFILE_USE
 ifeq ($(OS_ARCH)_$(GNU_CC), WINNT_)
 # When building with PGO, we have to make sure to re-link
 # in the MOZ_PROFILE_USE phase if we linked in the
 # MOZ_PROFILE_GENERATE phase. We'll touch this pgo.relink
 # file in the link rule in the GENERATE phase to indicate
 # that we need a relink.
 ifdef SHARED_LIBRARY
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1540,16 +1540,17 @@ public:
   static nsresult ProcessViewportInfo(nsIDocument *aDocument,
                                       const nsAString &viewportInfo);
 
   static nsIScriptContext* GetContextForEventHandlers(nsINode* aNode,
                                                       nsresult* aRv);
 
   static JSContext *GetCurrentJSContext();
   static JSContext *GetSafeJSContext();
+  static JSContext *GetCurrentJSContextForThread();
   static JSContext *GetDefaultJSContextForThread();
 
   /**
    * Case insensitive comparison between two strings. However it only ignores
    * case for ASCII characters a-z.
    */
   static bool EqualsIgnoreASCIICase(const nsAString& aStr1,
                                     const nsAString& aStr2);
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -5258,16 +5258,27 @@ nsContentUtils::GetDefaultJSContextForTh
   if (MOZ_LIKELY(NS_IsMainThread())) {
     return GetSafeJSContext();
   } else {
     return workers::GetCurrentThreadJSContext();
   }
 }
 
 /* static */
+JSContext *
+nsContentUtils::GetCurrentJSContextForThread()
+{
+  if (MOZ_LIKELY(NS_IsMainThread())) {
+    return GetCurrentJSContext();
+  } else {
+    return workers::GetCurrentThreadJSContext();
+  }
+}
+
+/* static */
 nsresult
 nsContentUtils::ASCIIToLower(nsAString& aStr)
 {
   PRUnichar* iter = aStr.BeginWriting();
   PRUnichar* end = aStr.EndWriting();
   if (MOZ_UNLIKELY(!iter || !end)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -34,24 +34,24 @@
 
 #include "nsIChannel.h"
 #include "nsIStreamListener.h"
 
 #include "nsIFrame.h"
 #include "nsIDOMNode.h"
 
 #include "nsContentUtils.h"
-#include "nsCxPusher.h"
 #include "nsLayoutUtils.h"
 #include "nsIContentPolicy.h"
 #include "nsEventDispatcher.h"
 #include "nsSVGEffects.h"
 
 #include "mozAutoDocUpdate.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/ScriptSettings.h"
 
 #if defined(XP_WIN)
 // Undefine LoadImage to prevent naming conflict with Windows.
 #undef LoadImage
 #endif
 
 using namespace mozilla;
 
@@ -1189,22 +1189,16 @@ nsImageLoadingContent::ClearCurrentReque
 
 void
 nsImageLoadingContent::ClearPendingRequest(nsresult aReason,
                                            uint32_t aFlags)
 {
   if (!mPendingRequest)
     return;
 
-  // Push a null JSContext on the stack so that code that runs within
-  // the below code doesn't think it's being called by JS. See bug
-  // 604262.
-  nsCxPusher pusher;
-  pusher.PushNull();
-
   // Deregister this image from the refresh driver so it no longer receives
   // notifications.
   nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mPendingRequest,
                                         &mPendingRequestRegistered);
 
   UntrackImage(mPendingRequest, aFlags);
   mPendingRequest->CancelAndForgetObserver(aReason);
   mPendingRequest = nullptr;
@@ -1254,41 +1248,31 @@ nsImageLoadingContent::BindToTree(nsIDoc
                                   nsIContent* aBindingParent,
                                   bool aCompileEventHandlers)
 {
   // We may be entering the document, so if our image should be tracked,
   // track it.
   if (!aDocument)
     return;
 
-  // Push a null JSContext on the stack so that callbacks triggered by the
-  // below code won't think they're being called from JS.
-  nsCxPusher pusher;
-  pusher.PushNull();
-
   TrackImage(mCurrentRequest);
   TrackImage(mPendingRequest);
 
   if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD)
     aDocument->BlockOnload();
 }
 
 void
 nsImageLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   // We may be leaving the document, so if our image is tracked, untrack it.
   nsCOMPtr<nsIDocument> doc = GetOurCurrentDoc();
   if (!doc)
     return;
 
-  // Push a null JSContext on the stack so that callbacks triggered by the
-  // below code won't think they're being called from JS.
-  nsCxPusher pusher;
-  pusher.PushNull();
-
   UntrackImage(mCurrentRequest);
   UntrackImage(mPendingRequest);
 
   if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD)
     doc->UnblockOnload(false);
 }
 
 void
--- a/content/canvas/src/CanvasImageCache.cpp
+++ b/content/canvas/src/CanvasImageCache.cpp
@@ -9,35 +9,37 @@
 #include "imgIRequest.h"
 #include "gfxASurface.h"
 #include "gfxPoint.h"
 #include "mozilla/dom/Element.h"
 #include "nsTHashtable.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "nsContentUtils.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/gfx/2D.h"
 
 namespace mozilla {
 
 using namespace dom;
+using namespace gfx;
 
 struct ImageCacheKey {
   ImageCacheKey(Element* aImage, HTMLCanvasElement* aCanvas)
     : mImage(aImage), mCanvas(aCanvas) {}
   Element* mImage;
   HTMLCanvasElement* mCanvas;
 };
 
 struct ImageCacheEntryData {
   ImageCacheEntryData(const ImageCacheEntryData& aOther)
     : mImage(aOther.mImage)
     , mILC(aOther.mILC)
     , mCanvas(aOther.mCanvas)
     , mRequest(aOther.mRequest)
-    , mSurface(aOther.mSurface)
+    , mSourceSurface(aOther.mSourceSurface)
     , mSize(aOther.mSize)
   {}
   ImageCacheEntryData(const ImageCacheKey& aKey)
     : mImage(aKey.mImage)
     , mILC(nullptr)
     , mCanvas(aKey.mCanvas)
   {}
 
@@ -46,17 +48,17 @@ struct ImageCacheEntryData {
   size_t SizeInBytes() { return mSize.width * mSize.height * 4; }
 
   // Key
   nsRefPtr<Element> mImage;
   nsIImageLoadingContent* mILC;
   nsRefPtr<HTMLCanvasElement> mCanvas;
   // Value
   nsCOMPtr<imgIRequest> mRequest;
-  nsRefPtr<gfxASurface> mSurface;
+  RefPtr<SourceSurface> mSourceSurface;
   gfxIntSize mSize;
   nsExpirationState mState;
 };
 
 class ImageCacheEntry : public PLDHashEntryHdr {
 public:
   typedef ImageCacheKey KeyType;
   typedef const ImageCacheKey* KeyTypePointer;
@@ -122,54 +124,54 @@ public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 };
 
 void
 CanvasImageCache::NotifyDrawImage(Element* aImage,
                                   HTMLCanvasElement* aCanvas,
                                   imgIRequest* aRequest,
-                                  gfxASurface* aSurface,
+                                  SourceSurface* aSource,
                                   const gfxIntSize& aSize)
 {
   if (!gImageCache) {
     gImageCache = new ImageCache();
     nsContentUtils::RegisterShutdownObserver(new CanvasImageCacheShutdownObserver());
   }
 
   ImageCacheEntry* entry = gImageCache->mCache.PutEntry(ImageCacheKey(aImage, aCanvas));
   if (entry) {
-    if (entry->mData->mSurface) {
+    if (entry->mData->mSourceSurface) {
       // We are overwriting an existing entry.
       gImageCache->mTotal -= entry->mData->SizeInBytes();
       gImageCache->RemoveObject(entry->mData);
     }
     gImageCache->AddObject(entry->mData);
 
     nsCOMPtr<nsIImageLoadingContent> ilc = do_QueryInterface(aImage);
     if (ilc) {
       ilc->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
                       getter_AddRefs(entry->mData->mRequest));
     }
     entry->mData->mILC = ilc;
-    entry->mData->mSurface = aSurface;
+    entry->mData->mSourceSurface = aSource;
     entry->mData->mSize = aSize;
 
     gImageCache->mTotal += entry->mData->SizeInBytes();
   }
 
   if (!sCanvasImageCacheLimit)
     return;
 
   // Expire the image cache early if its larger than we want it to be.
   while (gImageCache->mTotal > size_t(sCanvasImageCacheLimit))
     gImageCache->AgeOneGeneration();
 }
 
-gfxASurface*
+SourceSurface*
 CanvasImageCache::Lookup(Element* aImage,
                          HTMLCanvasElement* aCanvas,
                          gfxIntSize* aSize)
 {
   if (!gImageCache)
     return nullptr;
 
   ImageCacheEntry* entry = gImageCache->mCache.GetEntry(ImageCacheKey(aImage, aCanvas));
@@ -179,17 +181,17 @@ CanvasImageCache::Lookup(Element* aImage
   nsCOMPtr<imgIRequest> request;
   entry->mData->mILC->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, getter_AddRefs(request));
   if (request != entry->mData->mRequest)
     return nullptr;
 
   gImageCache->MarkUsed(entry->mData);
 
   *aSize = entry->mData->mSize;
-  return entry->mData->mSurface;
+  return entry->mData->mSourceSurface;
 }
 
 NS_IMPL_ISUPPORTS1(CanvasImageCacheShutdownObserver, nsIObserver)
 
 NS_IMETHODIMP
 CanvasImageCacheShutdownObserver::Observe(nsISupports *aSubject,
                                           const char *aTopic,
                                           const PRUnichar *aData)
--- a/content/canvas/src/CanvasImageCache.h
+++ b/content/canvas/src/CanvasImageCache.h
@@ -6,43 +6,47 @@
 #ifndef CANVASIMAGECACHE_H_
 #define CANVASIMAGECACHE_H_
 
 namespace mozilla {
 namespace dom {
 class Element;
 class HTMLCanvasElement;
 } // namespace dom
+namespace gfx {
+class SourceSurface;
+} // namespace gfx
 } // namespace mozilla
 class imgIRequest;
 class gfxASurface;
 
 #include "gfxPoint.h"
 
 namespace mozilla {
 
 class CanvasImageCache {
+  typedef mozilla::gfx::SourceSurface SourceSurface;
 public:
   /**
    * Notify that image element aImage was (or is about to be) drawn to aCanvas
    * using the first frame of aRequest's image. The data for the surface is
    * in aSurface, and the image size is in aSize.
    */
   static void NotifyDrawImage(dom::Element* aImage,
                               dom::HTMLCanvasElement* aCanvas,
                               imgIRequest* aRequest,
-                              gfxASurface* aSurface,
+                              SourceSurface* aSource,
                               const gfxIntSize& aSize);
 
   /**
    * Check whether aImage has recently been drawn into aCanvas. If we return
    * a non-null surface, then the image was recently drawn into the canvas
    * (with the same image request) and the returned surface contains the image
    * data, and the image size will be returned in aSize.
    */
-  static gfxASurface* Lookup(dom::Element* aImage,
-                             dom::HTMLCanvasElement* aCanvas,
-                             gfxIntSize* aSize);
+  static SourceSurface* Lookup(dom::Element* aImage,
+                               dom::HTMLCanvasElement* aCanvas,
+                               gfxIntSize* aSize);
 };
 
 }
 
 #endif /* CANVASIMAGECACHE_H_ */
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -1445,39 +1445,31 @@ CanvasRenderingContext2D::CreatePattern(
       return pat.forget();
     }
   } else if (element.IsHTMLImageElement()) {
     htmlElement = &element.GetAsHTMLImageElement();
   } else {
     htmlElement = &element.GetAsHTMLVideoElement();
   }
 
+  EnsureTarget();
+
   // The canvas spec says that createPattern should use the first frame
   // of animated images
   nsLayoutUtils::SurfaceFromElementResult res =
     nsLayoutUtils::SurfaceFromElement(htmlElement,
-      nsLayoutUtils::SFE_WANT_FIRST_FRAME | nsLayoutUtils::SFE_WANT_NEW_SURFACE);
-
-  if (!res.mSurface) {
+      nsLayoutUtils::SFE_WANT_FIRST_FRAME, mTarget);
+
+  if (!res.mSourceSurface) {
     error.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
-  // Ignore nullptr cairo surfaces! See bug 666312.
-  if (!res.mSurface->CairoSurface() || res.mSurface->CairoStatus()) {
-    error.Throw(NS_ERROR_NOT_AVAILABLE);
-    return nullptr;
-  }
-
-  EnsureTarget();
-  RefPtr<SourceSurface> srcSurf =
-    gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, res.mSurface);
-
   nsRefPtr<CanvasPattern> pat =
-    new CanvasPattern(this, srcSurf, repeatMode, res.mPrincipal,
+    new CanvasPattern(this, res.mSourceSurface, repeatMode, res.mPrincipal,
                              res.mIsWriteOnly, res.mCORSUsed);
 
   return pat.forget();
 }
 
 //
 // shadows
 //
@@ -3079,43 +3071,35 @@ CanvasRenderingContext2D::DrawImage(cons
     if (image.IsHTMLImageElement()) {
       HTMLImageElement* img = &image.GetAsHTMLImageElement();
       element = img;
     } else {
       HTMLVideoElement* video = &image.GetAsHTMLVideoElement();
       element = video;
     }
 
-    gfxASurface* imgsurf =
+    srcSurf =
       CanvasImageCache::Lookup(element, mCanvasElement, &imgSize);
-    if (imgsurf) {
-      srcSurf = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, imgsurf);
-    }
   }
 
   if (!srcSurf) {
     // The canvas spec says that drawImage should draw the first frame
     // of animated images
     uint32_t sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME;
     nsLayoutUtils::SurfaceFromElementResult res =
-      nsLayoutUtils::SurfaceFromElement(element, sfeFlags);
-
-    if (!res.mSurface) {
+      nsLayoutUtils::SurfaceFromElement(element, sfeFlags, mTarget);
+
+    if (!res.mSourceSurface) {
       // Spec says to silently do nothing if the element is still loading.
       if (!res.mIsStillLoading) {
         error.Throw(NS_ERROR_NOT_AVAILABLE);
       }
       return;
     }
 
-    // Ignore cairo surfaces that are bad! See bug 666312.
-    if (res.mSurface->CairoStatus()) {
-      return;
-    }
-
     imgSize = res.mSize;
 
     // Scale sw/sh based on aspect ratio
     if (image.IsHTMLVideoElement()) {
       HTMLVideoElement* video = &image.GetAsHTMLVideoElement();
       int32_t displayWidth = video->VideoWidth();
       int32_t displayHeight = video->VideoHeight();
       sw *= (double)imgSize.width / (double)displayWidth;
@@ -3124,21 +3108,21 @@ CanvasRenderingContext2D::DrawImage(cons
 
     if (mCanvasElement) {
       CanvasUtils::DoDrawImageSecurityCheck(mCanvasElement,
                                             res.mPrincipal, res.mIsWriteOnly,
                                             res.mCORSUsed);
     }
 
     if (res.mImageRequest) {
-      CanvasImageCache::NotifyDrawImage(element, mCanvasElement,
-                                        res.mImageRequest, res.mSurface, imgSize);
+      CanvasImageCache::NotifyDrawImage(element, mCanvasElement, res.mImageRequest,
+                                        res.mSourceSurface, imgSize);
     }
 
-    srcSurf = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mTarget, res.mSurface);
+    srcSurf = res.mSourceSurface;
   }
 
   if (optional_argc == 0) {
     sx = sy = 0.0;
     dw = sw = (double) imgSize.width;
     dh = sh = (double) imgSize.height;
   } else if (optional_argc == 2) {
     sx = sy = 0.0;
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -23,16 +23,17 @@
 #include "nsLayoutUtils.h"
 
 #include "GLContextProvider.h"
 #include "gfxImageSurface.h"
 
 #include "mozilla/LinkedList.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Scoped.h"
+#include "mozilla/gfx/2D.h"
 
 #ifdef XP_MACOSX
 #include "ForceDiscreteGPUHelperCGL.h"
 #endif
 
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/ErrorResult.h"
 
@@ -415,28 +416,29 @@ public:
     // us in TexImage2D
     template<class ElementType>
     void TexImage2D(GLenum target, GLint level,
                     GLenum internalformat, GLenum format, GLenum type,
                     ElementType& elt, ErrorResult& rv)
     {
         if (IsContextLost())
             return;
-        nsRefPtr<gfxImageSurface> isurf;
+        RefPtr<gfx::DataSourceSurface> data;
         WebGLTexelFormat srcFormat;
         nsLayoutUtils::SurfaceFromElementResult res = SurfaceFromElement(elt);
-        rv = SurfaceFromElementResultToImageSurface(res, getter_AddRefs(isurf),
+        rv = SurfaceFromElementResultToImageSurface(res, data,
                                                     &srcFormat);
-        if (rv.Failed() || !isurf)
+        if (rv.Failed() || !data)
             return;
 
-        uint32_t byteLength = isurf->Stride() * isurf->Height();
+        gfx::IntSize size = data->GetSize();
+        uint32_t byteLength = data->Stride() * size.height;
         return TexImage2D_base(target, level, internalformat,
-                               isurf->Width(), isurf->Height(), isurf->Stride(),
-                               0, format, type, isurf->Data(), byteLength,
+                               size.width, size.height, data->Stride(),
+                               0, format, type, data->GetData(), byteLength,
                                -1, srcFormat, mPixelStorePremultiplyAlpha);
     }
     void TexParameterf(GLenum target, GLenum pname, GLfloat param) {
         TexParameter_base(target, pname, nullptr, &param);
     }
     void TexParameteri(GLenum target, GLenum pname, GLint param) {
         TexParameter_base(target, pname, &param, nullptr);
     }
@@ -454,29 +456,30 @@ public:
     // us in TexSubImage2D
     template<class ElementType>
     void TexSubImage2D(GLenum target, GLint level,
                        GLint xoffset, GLint yoffset, GLenum format,
                        GLenum type, ElementType& elt, ErrorResult& rv)
     {
         if (IsContextLost())
             return;
-        nsRefPtr<gfxImageSurface> isurf;
+        RefPtr<gfx::DataSourceSurface> data;
         WebGLTexelFormat srcFormat;
         nsLayoutUtils::SurfaceFromElementResult res = SurfaceFromElement(elt);
-        rv = SurfaceFromElementResultToImageSurface(res, getter_AddRefs(isurf),
+        rv = SurfaceFromElementResultToImageSurface(res, data,
                                                     &srcFormat);
-        if (rv.Failed() || !isurf)
+        if (rv.Failed() || !data)
             return;
 
-        uint32_t byteLength = isurf->Stride() * isurf->Height();
+        gfx::IntSize size = data->GetSize();
+        uint32_t byteLength = data->Stride() * size.height;
         return TexSubImage2D_base(target, level, xoffset, yoffset,
-                                  isurf->Width(), isurf->Height(),
-                                  isurf->Stride(), format, type,
-                                  isurf->Data(), byteLength,
+                                  size.width, size.height,
+                                  data->Stride(), format, type,
+                                  data->GetData(), byteLength,
                                   -1, srcFormat, mPixelStorePremultiplyAlpha);
         
     }
 
     void Uniform1i(WebGLUniformLocation* location, GLint x);
     void Uniform2i(WebGLUniformLocation* location, GLint x, GLint y);
     void Uniform3i(WebGLUniformLocation* location, GLint x, GLint y,
                    GLint z);
@@ -987,32 +990,32 @@ protected:
                       WebGLTexelFormat srcFormat, bool srcPremultiplied,
                       WebGLTexelFormat dstFormat, bool dstPremultiplied,
                       size_t dstTexelSize);
 
     template<class ElementType>
     nsLayoutUtils::SurfaceFromElementResult SurfaceFromElement(ElementType* aElement) {
         MOZ_ASSERT(aElement);
         uint32_t flags =
-            nsLayoutUtils::SFE_WANT_IMAGE_SURFACE;
+             nsLayoutUtils::SFE_WANT_IMAGE_SURFACE;
 
         if (mPixelStoreColorspaceConversion == LOCAL_GL_NONE)
             flags |= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION;
         if (!mPixelStorePremultiplyAlpha)
             flags |= nsLayoutUtils::SFE_NO_PREMULTIPLY_ALPHA;
         return nsLayoutUtils::SurfaceFromElement(aElement, flags);
     }
     template<class ElementType>
     nsLayoutUtils::SurfaceFromElementResult SurfaceFromElement(ElementType& aElement)
     {
       return SurfaceFromElement(&aElement);
     }
 
     nsresult SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromElementResult& res,
-                                                    gfxImageSurface **imageOut,
+                                                    RefPtr<gfx::DataSourceSurface>& imageOut,
                                                     WebGLTexelFormat *format);
 
     void CopyTexSubImage2D_base(GLenum target,
                                 GLint level,
                                 GLenum internalformat,
                                 GLint xoffset,
                                 GLint yoffset,
                                 GLint x,
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -43,16 +43,17 @@
 #endif
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ImageData.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gl;
+using namespace mozilla::gfx;
 
 static bool BaseTypeAndSizeFromUniformType(GLenum uType, GLenum *baseType, GLint *unitSize);
 static GLenum InternalFormatForFormatAndType(GLenum format, GLenum type, bool isGLES2);
 
 inline const WebGLRectangleObject *WebGLContext::FramebufferRectangleObject() const {
     return mBoundFramebuffer ? mBoundFramebuffer->RectangleObject()
                              : static_cast<const WebGLRectangleObject*>(this);
 }
@@ -2635,24 +2636,24 @@ WebGLContext::StencilOpSeparate(GLenum f
         return;
 
     MakeContextCurrent();
     gl->fStencilOpSeparate(face, sfail, dpfail, dppass);
 }
 
 nsresult
 WebGLContext::SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromElementResult& res,
-                                                     gfxImageSurface **imageOut, WebGLTexelFormat *format)
+                                                     RefPtr<DataSourceSurface>& imageOut, WebGLTexelFormat *format)
 {
-   *imageOut = nullptr;
    *format = WebGLTexelFormat::None;
 
-    if (!res.mSurface)
+    if (!res.mSourceSurface)
         return NS_OK;
-    if (res.mSurface->GetType() != gfxSurfaceTypeImage) {
+    RefPtr<DataSourceSurface> data = res.mSourceSurface->GetDataSurface();
+    if (!data) {
         // SurfaceFromElement lied!
         return NS_OK;
     }
 
     // We disallow loading cross-domain images and videos that have not been validated
     // with CORS as WebGL textures. The reason for doing that is that timing
     // attacks on WebGL shaders are able to retrieve approximations of the
     // pixel values in WebGL textures; see bug 655987.
@@ -2682,39 +2683,36 @@ WebGLContext::SurfaceFromElementResultTo
                         "See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures");
         return NS_ERROR_DOM_SECURITY_ERR;
     }
 
     // End of security checks, now we should be safe regarding cross-domain images
     // Notice that there is never a need to mark the WebGL canvas as write-only, since we reject write-only/cross-domain
     // texture sources in the first place.
 
-    gfxImageSurface* surf = static_cast<gfxImageSurface*>(res.mSurface.get());
-
-    res.mSurface.forget();
-    *imageOut = surf;
-
-    switch (surf->Format()) {
-        case gfxImageFormatARGB32:
+    switch (data->GetFormat()) {
+        case FORMAT_B8G8R8A8:
             *format = WebGLTexelFormat::BGRA8; // careful, our ARGB means BGRA
             break;
-        case gfxImageFormatRGB24:
+        case FORMAT_B8G8R8X8:
             *format = WebGLTexelFormat::BGRX8; // careful, our RGB24 is not tightly packed. Whence BGRX8.
             break;
-        case gfxImageFormatA8:
+        case FORMAT_A8:
             *format = WebGLTexelFormat::A8;
             break;
-        case gfxImageFormatRGB16_565:
+        case FORMAT_R5G6B5:
             *format = WebGLTexelFormat::RGB565;
             break;
         default:
             NS_ASSERTION(false, "Unsupported image format. Unimplemented.");
             return NS_ERROR_NOT_IMPLEMENTED;
     }
 
+    imageOut = data;
+
     return NS_OK;
 }
 
 
 
 void
 WebGLContext::Uniform1i(WebGLUniformLocation *location_object, GLint a1)
 {
--- a/content/events/src/nsDOMEventTargetHelper.cpp
+++ b/content/events/src/nsDOMEventTargetHelper.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDOMEventTargetHelper.h"
 #include "nsContentUtils.h"
 #include "nsEventDispatcher.h"
 #include "nsIDocument.h"
 #include "prprf.h"
 #include "nsGlobalWindow.h"
+#include "ScriptSettings.h"
 #include "mozilla/Likely.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMEventTargetHelper)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMEventTargetHelper)
@@ -266,20 +267,20 @@ nsDOMEventTargetHelper::DispatchTrustedE
 }
 
 nsresult
 nsDOMEventTargetHelper::SetEventHandler(nsIAtom* aType,
                                         JSContext* aCx,
                                         const JS::Value& aValue)
 {
   nsRefPtr<EventHandlerNonNull> handler;
-  JSObject* callable;
+  JS::Rooted<JSObject*> callable(aCx);
   if (aValue.isObject() &&
       JS_ObjectIsCallable(aCx, callable = &aValue.toObject())) {
-    handler = new EventHandlerNonNull(callable);
+    handler = new EventHandlerNonNull(callable, mozilla::dom::GetIncumbentGlobal());
   }
   SetEventHandler(aType, EmptyString(), handler);
   return NS_OK;
 }
 
 void
 nsDOMEventTargetHelper::GetEventHandler(nsIAtom* aType,
                                         JSContext* aCx,
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -878,29 +878,33 @@ nsEventListenerManager::CompileEventHand
 
   if (handler) {
     nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mTarget);
     // Bind it
     JS::Rooted<JSObject*> boundHandler(cx);
     JS::Rooted<JSObject*> scope(cx, listener->GetEventScope());
     context->BindCompiledEventHandler(mTarget, scope, handler, &boundHandler);
     aListenerStruct = nullptr;
+    // Note - We pass null for aIncumbentGlobal below. We could also pass the
+    // compilation global, but since the handler is guaranteed to be scripted,
+    // there's no need to use an override, since the JS engine will always give
+    // us the right answer.
     if (!boundHandler) {
       listener->ForgetHandler();
     } else if (listener->EventName() == nsGkAtoms::onerror && win) {
       nsRefPtr<OnErrorEventHandlerNonNull> handlerCallback =
-        new OnErrorEventHandlerNonNull(boundHandler);
+        new OnErrorEventHandlerNonNull(boundHandler, /* aIncumbentGlobal = */ nullptr);
       listener->SetHandler(handlerCallback);
     } else if (listener->EventName() == nsGkAtoms::onbeforeunload && win) {
       nsRefPtr<OnBeforeUnloadEventHandlerNonNull> handlerCallback =
-        new OnBeforeUnloadEventHandlerNonNull(boundHandler);
+        new OnBeforeUnloadEventHandlerNonNull(boundHandler, /* aIncumbentGlobal = */ nullptr);
       listener->SetHandler(handlerCallback);
     } else {
       nsRefPtr<EventHandlerNonNull> handlerCallback =
-        new EventHandlerNonNull(boundHandler);
+        new EventHandlerNonNull(boundHandler, /* aIncumbentGlobal = */ nullptr);
       listener->SetHandler(handlerCallback);
     }
   }
 
   return result;
 }
 
 nsresult
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -2499,21 +2499,21 @@ nsEventStateManager::DoScrollHistory(int
     }
   }
 }
 
 void
 nsEventStateManager::DoScrollZoom(nsIFrame *aTargetFrame,
                                   int32_t adjustment)
 {
-  // Exclude form controls and XUL content.
+  // Exclude form controls and content in chrome docshells.
   nsIContent *content = aTargetFrame->GetContent();
   if (content &&
       !content->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) &&
-      !content->OwnerDoc()->IsXUL())
+      !nsContentUtils::IsInChromeDocshell(content->OwnerDoc()))
     {
       // positive adjustment to decrease zoom, negative to increase
       int32_t change = (adjustment > 0) ? -1 : 1;
 
       if (Preferences::GetBool("browser.zoom.full") || content->GetCurrentDoc()->IsSyntheticDocument()) {
         ChangeFullZoom(change);
       } else {
         ChangeTextSize(change);
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -202,20 +202,16 @@ public:
   // in the common case where we don't have a "MozAudioAvailable" listener.
   void NotifyAudioAvailableListener();
 
   // Called by the media decoder and the video frame to get the
   // ImageContainer containing the video data.
   virtual VideoFrameContainer* GetVideoFrameContainer() MOZ_FINAL MOZ_OVERRIDE;
   layers::ImageContainer* GetImageContainer();
 
-  // Called by the video frame to get the print surface, if this is
-  // a static document and we're not actually playing video
-  gfxASurface* GetPrintSurface() { return mPrintSurface; }
-
   // Dispatch events
   using nsGenericHTMLElement::DispatchEvent;
   virtual nsresult DispatchEvent(const nsAString& aName) MOZ_FINAL MOZ_OVERRIDE;
   virtual nsresult DispatchAsyncEvent(const nsAString& aName) MOZ_FINAL MOZ_OVERRIDE;
   nsresult DispatchAudioAvailableEvent(float* aFrameBuffer,
                                        uint32_t aFrameBufferLength,
                                        float aTime);
 
@@ -1008,18 +1004,16 @@ protected:
   // defaultPlaybackRate, then the implication is that the user is using a
   // feature such as fast forward or slow motion playback.
   double mPlaybackRate;
 
   // True if pitch correction is applied when playbackRate is set to a
   // non-intrinsic value.
   bool mPreservesPitch;
 
-  nsRefPtr<gfxASurface> mPrintSurface;
-
   // Reference to the source element last returned by GetNextSource().
   // This is the child source element which we're trying to load from.
   nsCOMPtr<nsIContent> mSourceLoadCandidate;
 
   // An audio stream for writing audio directly from JS.
   nsAutoPtr<AudioStream> mAudioStream;
 
   // Range of time played.
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -3218,21 +3218,16 @@ VideoFrameContainer* HTMLMediaElement::G
   if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_METADATA &&
       mMediaSize == nsIntSize(-1, -1)) {
     return nullptr;
   }
 
   if (mVideoFrameContainer)
     return mVideoFrameContainer;
 
-  // If we have a print surface, this is just a static image so
-  // no image container is required
-  if (mPrintSurface)
-    return nullptr;
-
   // Only video frames need an image container.
   nsCOMPtr<nsIDOMHTMLVideoElement> video = do_QueryObject(this);
   if (!video)
     return nullptr;
 
   mVideoFrameContainer =
     new VideoFrameContainer(this, LayerManager::CreateAsynchronousImageContainer());
 
@@ -3599,36 +3594,17 @@ already_AddRefed<nsILoadGroup> HTMLMedia
 
 nsresult
 HTMLMediaElement::CopyInnerTo(Element* aDest)
 {
   nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest);
   NS_ENSURE_SUCCESS(rv, rv);
   if (aDest->OwnerDoc()->IsStaticDocument()) {
     HTMLMediaElement* dest = static_cast<HTMLMediaElement*>(aDest);
-    if (mPrintSurface) {
-      dest->mPrintSurface = mPrintSurface;
-      dest->mMediaSize = mMediaSize;
-    } else {
-      nsIFrame* frame = GetPrimaryFrame();
-      Element* element;
-      if (frame && frame->GetType() == nsGkAtoms::HTMLVideoFrame &&
-          static_cast<nsVideoFrame*>(frame)->ShouldDisplayPoster()) {
-        nsIContent* content = static_cast<nsVideoFrame*>(frame)->GetPosterImage();
-        element = content ? content->AsElement() : nullptr;
-      } else {
-        element = const_cast<HTMLMediaElement*>(this);
-      }
-
-      nsLayoutUtils::SurfaceFromElementResult res =
-        nsLayoutUtils::SurfaceFromElement(element,
-                                          nsLayoutUtils::SFE_WANT_NEW_SURFACE);
-      dest->mPrintSurface = res.mSurface;
-      dest->mMediaSize = nsIntSize(res.mSize.width, res.mSize.height);
-    }
+    dest->mMediaSize = mMediaSize;
   }
   return rv;
 }
 
 already_AddRefed<TimeRanges>
 HTMLMediaElement::Buffered() const
 {
   nsRefPtr<TimeRanges> ranges = new TimeRanges();
--- a/content/html/content/src/nsTextEditorState.cpp
+++ b/content/html/content/src/nsTextEditorState.cpp
@@ -33,21 +33,21 @@
 #include "nsISelectionPrivate.h"
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIEditor.h"
 #include "nsTextEditRules.h"
 #include "mozilla/Selection.h"
 #include "nsEventListenerManager.h"
 #include "nsContentUtils.h"
-#include "nsCxPusher.h"
 #include "mozilla/Preferences.h"
 #include "nsTextNode.h"
 #include "nsIController.h"
 #include "mozilla/TextEvents.h"
+#include "mozilla/dom/ScriptSettings.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID);
 
 static nsINativeKeyBindings *sNativeInputBindings = nullptr;
 static nsINativeKeyBindings *sNativeTextAreaBindings = nullptr;
@@ -1278,23 +1278,22 @@ nsTextEditorState::PrepareEditor(const n
 
     // Get the DOM document
     nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(shell->GetDocument());
     if (!domdoc)
       return NS_ERROR_FAILURE;
 
     // What follows is a bit of a hack.  The editor uses the public DOM APIs
     // for its content manipulations, and it causes it to fail some security
-    // checks deep inside when initializing.  So we push a null JSContext
-    // on the JS stack here to make it clear that we're native code.
+    // checks deep inside when initializing. So we explictly make it clear that
+    // we're native code.
     // Note that any script that's directly trying to access our value
     // has to be going through some scriptable object to do that and that
     // already does the relevant security checks.
-    nsCxPusher pusher;
-    pusher.PushNull();
+    AutoSystemCaller asc;
 
     rv = newEditor->Init(domdoc, GetRootNode(), mSelCon, editorFlags);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Initialize the controller for the editor
 
   if (!SuppressEventHandlers(presContext)) {
@@ -1772,19 +1771,18 @@ nsTextEditorState::GetValue(nsAString& a
     // access its own DOM nodes!  Let's try to deal with that by pushing a null
     // JSContext on the JSContext stack to make it clear that we're native
     // code.  Note that any script that's directly trying to access our value
     // has to be going through some scriptable object to do that and that
     // already does the relevant security checks.
     // XXXbz if we could just get the textContent of our anonymous content (eg
     // if plaintext editor didn't create <br> nodes all over), we wouldn't need
     // this.
-    { /* Scope for context pusher */
-      nsCxPusher pusher;
-      pusher.PushNull();
+    { /* Scope for AutoSystemCaller. */
+      AutoSystemCaller asc;
 
       mEditor->OutputToString(NS_LITERAL_STRING("text/plain"), flags,
                               aValue);
     }
     if (canCache) {
       mCachedValue = aValue;
     } else {
       mCachedValue.Truncate();
@@ -1852,19 +1850,18 @@ nsTextEditorState::SetValue(const nsAStr
       if (!domDoc) {
         NS_WARNING("Why don't we have a document?");
         return;
       }
 
       // Time to mess with our security context... See comments in GetValue()
       // for why this is needed.  Note that we have to do this up here, because
       // otherwise SelectAll() will fail.
-      { /* Scope for context pusher */
-        nsCxPusher pusher;
-        pusher.PushNull();
+      {
+        AutoSystemCaller asc;
 
         nsCOMPtr<nsISelection> domSel;
         nsCOMPtr<nsISelectionPrivate> selPriv;
         mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
                               getter_AddRefs(domSel));
         if (domSel)
         {
           selPriv = do_QueryInterface(domSel);
--- a/content/xbl/src/nsXBLPrototypeHandler.cpp
+++ b/content/xbl/src/nsXBLPrototypeHandler.cpp
@@ -317,17 +317,17 @@ nsXBLPrototypeHandler::ExecuteHandler(Ev
 
   // Now, wrap the bound handler into the content compartment and use it.
   JSAutoCompartment ac2(cx, globalObject);
   if (!JS_WrapObject(cx, &bound)) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<EventHandlerNonNull> handlerCallback =
-    new EventHandlerNonNull(bound);
+    new EventHandlerNonNull(bound, /* aIncumbentGlobal = */ nullptr);
 
   nsEventHandler eventHandler(handlerCallback);
 
   // Execute it.
   nsCOMPtr<nsIJSEventListener> eventListener;
   rv = NS_NewJSEventListener(globalObject,
                              scriptTarget, onEventAtom,
                              eventHandler,
--- a/db/sqlite3/src/Makefile.in
+++ b/db/sqlite3/src/Makefile.in
@@ -41,21 +41,16 @@ MODULE_OPTIMIZE_FLAGS = -O2
 endif
 
 # Force /O2 optimisation on Windows because using the default /O1 causes
 # crashes with MSVC2005 and PGO. See bug 719584.
 ifeq ($(OS_ARCH),WINNT)
 MODULE_OPTIMIZE_FLAGS = -O2
 endif
 
-# disable PGO for Sun Studio
-ifdef SOLARIS_SUNPRO_CC
-NO_PROFILE_GUIDED_OPTIMIZE = 1
-endif
-
 include $(topsrcdir)/config/rules.mk
 
 # next line allows use of MOZ_OBJDIR in .mozconfig with older gcc on BeOS, maybe others
 LOCAL_INCLUDES += -I$(srcdir)
 
 ifeq ($(OS_ARCH),OS2)
 ADD_TO_DEF_FILE = $(PYTHON) -m mozbuild.action.preprocessor $(DEFINES) \
        $(srcdir)/sqlite.def | sed -e '1,/^EXPORTS$$/ d' -e 's,sqlite3,_\0,' \
--- a/db/sqlite3/src/moz.build
+++ b/db/sqlite3/src/moz.build
@@ -57,8 +57,12 @@ if CONFIG['OS_TARGET'] == 'Android':
     # default to user readable only to fit Android security model
     DEFINES['SQLITE_DEFAULT_FILE_PERMISSIONS'] = '0600'
 
 # Force using malloc_usable_size when building with jemalloc because _msize
 # causes assertions on Win64. See bug 719579.
 if CONFIG['OS_ARCH'] == 'WINNT' and CONFIG['MOZ_MEMORY']:
     DEFINES['HAVE_MALLOC_USABLE_SIZE'] = True
     DEFINES['SQLITE_WITHOUT_MSIZE'] = True
+
+# disable PGO for Sun Studio
+if CONFIG['SOLARIS_SUNPRO_CC']:
+    NO_PGO = True
new file mode 100644
--- /dev/null
+++ b/dom/base/ScriptSettings.cpp
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim: ft=cpp tw=78 sw=2 et ts=2
+/* 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/. */
+
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/ThreadLocal.h"
+#include "mozilla/Assertions.h"
+
+#include "jsapi.h"
+#include "xpcpublic.h"
+#include "nsIGlobalObject.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIScriptContext.h"
+#include "nsContentUtils.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace dom {
+
+class ScriptSettingsStack;
+static mozilla::ThreadLocal<ScriptSettingsStack*> sScriptSettingsTLS;
+
+ScriptSettingsStackEntry ScriptSettingsStackEntry::SystemSingleton;
+
+class ScriptSettingsStack {
+public:
+  static ScriptSettingsStack& Ref() {
+    return *sScriptSettingsTLS.get();
+  }
+  ScriptSettingsStack() {};
+
+  void Push(ScriptSettingsStackEntry* aSettings) {
+    // The bottom-most entry must always be a candidate entry point.
+    MOZ_ASSERT_IF(mStack.Length() == 0 || mStack.LastElement()->IsSystemSingleton(),
+                  aSettings->mIsCandidateEntryPoint);
+    mStack.AppendElement(aSettings);
+  }
+
+  void PushSystem() {
+    mStack.AppendElement(&ScriptSettingsStackEntry::SystemSingleton);
+  }
+
+  void Pop() {
+    MOZ_ASSERT(mStack.Length() > 0);
+    mStack.RemoveElementAt(mStack.Length() - 1);
+  }
+
+  nsIGlobalObject* Incumbent() {
+    if (!mStack.Length()) {
+      return nullptr;
+    }
+    return mStack.LastElement()->mGlobalObject;
+  }
+
+  nsIGlobalObject* EntryPoint() {
+    if (!mStack.Length())
+      return nullptr;
+    for (int i = mStack.Length() - 1; i >= 0; --i) {
+      if (mStack[i]->mIsCandidateEntryPoint) {
+        return mStack[i]->mGlobalObject;
+      }
+    }
+    MOZ_ASSUME_UNREACHABLE("Non-empty stack should always have an entry point");
+  }
+
+private:
+  // These pointers are caller-owned.
+  nsTArray<ScriptSettingsStackEntry*> mStack;
+};
+
+void
+InitScriptSettings()
+{
+  if (!sScriptSettingsTLS.initialized()) {
+    bool success = sScriptSettingsTLS.init();
+    if (!success) {
+      MOZ_CRASH();
+    }
+  }
+
+  ScriptSettingsStack* ptr = new ScriptSettingsStack();
+  sScriptSettingsTLS.set(ptr);
+}
+
+void DestroyScriptSettings()
+{
+  ScriptSettingsStack* ptr = sScriptSettingsTLS.get();
+  MOZ_ASSERT(ptr);
+  sScriptSettingsTLS.set(nullptr);
+  delete ptr;
+}
+
+// Note: When we're ready to expose it, GetEntryGlobal will look similar to
+// GetIncumbentGlobal below.
+
+nsIGlobalObject*
+GetIncumbentGlobal()
+{
+  // We need the current JSContext in order to check the JS for
+  // scripted frames that may have appeared since anyone last
+  // manipulated the stack. If it's null, that means that there
+  // must be no entry point on the stack, and therefore no incumbent
+  // global either.
+  JSContext *cx = nsContentUtils::GetCurrentJSContextForThread();
+  if (!cx) {
+    MOZ_ASSERT(ScriptSettingsStack::Ref().EntryPoint() == nullptr);
+    return nullptr;
+  }
+
+  // See what the JS engine has to say. If we've got a scripted caller
+  // override in place, the JS engine will lie to us and pretend that
+  // there's nothing on the JS stack, which will cause us to check the
+  // incumbent script stack below.
+  JS::RootedScript script(cx);
+  if (JS_DescribeScriptedCaller(cx, &script, nullptr)) {
+    JS::RootedObject global(cx, JS_GetGlobalFromScript(script));
+    MOZ_ASSERT(global);
+    return xpc::GetNativeForGlobal(global);
+  }
+
+  // Ok, nothing from the JS engine. Let's use whatever's on the
+  // explicit stack.
+  return ScriptSettingsStack::Ref().Incumbent();
+}
+
+AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
+                                 bool aIsMainThread,
+                                 JSContext* aCx)
+  : mStack(ScriptSettingsStack::Ref())
+  , mEntry(aGlobalObject, /* aCandidate = */ true)
+{
+  MOZ_ASSERT(aGlobalObject);
+  if (!aCx) {
+    // If the caller didn't provide a cx, hunt one down. This isn't exactly
+    // fast, but the callers that care about performance can pass an explicit
+    // cx for now. Eventually, the whole cx pushing thing will go away
+    // entirely.
+    MOZ_ASSERT(aIsMainThread, "cx is mandatory off-main-thread");
+    nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aGlobalObject);
+    if (sgo && sgo->GetScriptContext()) {
+      aCx = sgo->GetScriptContext()->GetNativeContext();
+    }
+    if (!aCx) {
+      aCx = nsContentUtils::GetSafeJSContext();
+    }
+  }
+  if (aIsMainThread) {
+    mCxPusher.Push(aCx);
+  }
+  mAc.construct(aCx, aGlobalObject->GetGlobalJSObject());
+  mStack.Push(&mEntry);
+}
+
+AutoEntryScript::~AutoEntryScript()
+{
+  MOZ_ASSERT(mStack.Incumbent() == mEntry.mGlobalObject);
+  mStack.Pop();
+}
+
+AutoIncumbentScript::AutoIncumbentScript(nsIGlobalObject* aGlobalObject)
+  : mStack(ScriptSettingsStack::Ref())
+  , mEntry(aGlobalObject, /* aCandidate = */ false)
+  , mCallerOverride(nsContentUtils::GetCurrentJSContextForThread())
+{
+  mStack.Push(&mEntry);
+}
+
+AutoIncumbentScript::~AutoIncumbentScript()
+{
+  MOZ_ASSERT(mStack.Incumbent() == mEntry.mGlobalObject);
+  mStack.Pop();
+}
+
+AutoSystemCaller::AutoSystemCaller(bool aIsMainThread)
+  : mStack(ScriptSettingsStack::Ref())
+{
+  if (aIsMainThread) {
+    mCxPusher.PushNull();
+  }
+  mStack.PushSystem();
+}
+
+AutoSystemCaller::~AutoSystemCaller()
+{
+  mStack.Pop();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/base/ScriptSettings.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim: ft=cpp tw=78 sw=2 et ts=2
+/* 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/. */
+
+/* Utilities for managing the script settings object stack defined in webapps */
+
+#ifndef mozilla_dom_ScriptSettings_h
+#define mozilla_dom_ScriptSettings_h
+
+#include "nsCxPusher.h"
+#include "MainThreadUtils.h"
+#include "nsIGlobalObject.h"
+
+#include "mozilla/Maybe.h"
+
+class nsIGlobalObject;
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * System-wide setup/teardown routines. Init and Destroy should be invoked
+ * once each, at startup and shutdown (respectively).
+ */
+void InitScriptSettings();
+void DestroyScriptSettings();
+
+// Note: We don't yet expose GetEntryGlobal, because in order for it to be
+// correct, we first need to replace a bunch of explicit cx pushing in the
+// browser with AutoEntryScript. But GetIncumbentGlobal is simpler, because it
+// can mostly be inferred from the JS stack.
+nsIGlobalObject* GetIncumbentGlobal();
+
+class ScriptSettingsStack;
+struct ScriptSettingsStackEntry {
+  nsCOMPtr<nsIGlobalObject> mGlobalObject;
+  bool mIsCandidateEntryPoint;
+
+  ScriptSettingsStackEntry(nsIGlobalObject *aGlobal, bool aCandidate)
+    : mGlobalObject(aGlobal)
+    , mIsCandidateEntryPoint(aCandidate)
+  {
+    MOZ_ASSERT(mGlobalObject);
+    MOZ_ASSERT(mGlobalObject->GetGlobalJSObject(),
+               "Must have an actual JS global for the duration on the stack");
+    MOZ_ASSERT(JS_IsGlobalObject(mGlobalObject->GetGlobalJSObject()),
+               "No outer windows allowed");
+  }
+
+  ~ScriptSettingsStackEntry() {
+    // We must have an actual JS global for the entire time this is on the stack.
+    MOZ_ASSERT_IF(mGlobalObject, mGlobalObject->GetGlobalJSObject());
+  }
+
+  bool IsSystemSingleton() { return this == &SystemSingleton; }
+  static ScriptSettingsStackEntry SystemSingleton;
+
+private:
+  ScriptSettingsStackEntry() : mGlobalObject(nullptr)
+                             , mIsCandidateEntryPoint(true)
+  {}
+};
+
+/*
+ * A class that represents a new script entry point.
+ */
+class AutoEntryScript {
+public:
+  AutoEntryScript(nsIGlobalObject* aGlobalObject,
+                  bool aIsMainThread = NS_IsMainThread(),
+                  // Note: aCx is mandatory off-main-thread.
+                  JSContext* aCx = nullptr);
+  ~AutoEntryScript();
+
+private:
+  dom::ScriptSettingsStack& mStack;
+  dom::ScriptSettingsStackEntry mEntry;
+  nsCxPusher mCxPusher;
+  mozilla::Maybe<JSAutoCompartment> mAc; // This can de-Maybe-fy when mCxPusher
+                                         // goes away.
+};
+
+/*
+ * A class that can be used to force a particular incumbent script on the stack.
+ */
+class AutoIncumbentScript {
+public:
+  AutoIncumbentScript(nsIGlobalObject* aGlobalObject);
+  ~AutoIncumbentScript();
+private:
+  dom::ScriptSettingsStack& mStack;
+  dom::ScriptSettingsStackEntry mEntry;
+  JS::AutoHideScriptedCaller mCallerOverride;
+};
+
+/*
+ * A class used for C++ to indicate that existing entry and incumbent scripts
+ * should not apply to anything in scope, and that callees should act as if
+ * they were invoked "from C++".
+ */
+class AutoSystemCaller {
+public:
+  AutoSystemCaller(bool aIsMainThread = NS_IsMainThread());
+  ~AutoSystemCaller();
+private:
+  dom::ScriptSettingsStack& mStack;
+  nsCxPusher mCxPusher;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ScriptSettings_h
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -54,16 +54,17 @@ EXPORTS.mozilla.dom += [
     'DOMError.h',
     'DOMException.h',
     'DOMRequest.h',
     'MessageChannel.h',
     'MessagePort.h',
     'MessagePortList.h',
     'Navigator.h',
     'ScreenOrientation.h',
+    'ScriptSettings.h',
     'StructuredCloneTags.h',
     'URL.h',
 ]
 
 UNIFIED_SOURCES += [
     'BarProps.cpp',
     'CompositionStringSynthesizer.cpp',
     'Crypto.cpp',
@@ -89,16 +90,17 @@ UNIFIED_SOURCES += [
     'nsPerformance.cpp',
     'nsQueryContentEventResult.cpp',
     'nsScreen.cpp',
     'nsScriptNameSpaceManager.cpp',
     'nsStructuredCloneContainer.cpp',
     'nsWindowMemoryReporter.cpp',
     'nsWindowRoot.cpp',
     'nsWrapperCache.cpp',
+    'ScriptSettings.cpp',
     'URL.cpp',
     'WindowNamedPropertiesHandler.cpp',
 ]
 
 # these files couldn't be in UNIFIED_SOURCES for now for reasons given below:
 SOURCES += [
     # this file doesn't like windows.h
     'MessagePort.cpp',
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -88,16 +88,17 @@
 #ifdef XP_WIN
 #undef GetClassName
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
+using namespace mozilla::gfx;
 
 class gfxContext;
 
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 DOMCI_DATA(WindowUtils, nsDOMWindowUtils)
 
 NS_INTERFACE_MAP_BEGIN(nsDOMWindowUtils)
@@ -1380,31 +1381,30 @@ nsDOMWindowUtils::NodesFromRect(float aX
 
   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
   NS_ENSURE_STATE(doc);
 
   return doc->NodesFromRectHelper(aX, aY, aTopSize, aRightSize, aBottomSize, aLeftSize, 
                                   aIgnoreRootScrollFrame, aFlushLayout, aReturn);
 }
 
-static already_AddRefed<gfxImageSurface>
-CanvasToImageSurface(nsIDOMHTMLCanvasElement* aCanvas)
+static TemporaryRef<DataSourceSurface>
+CanvasToDataSourceSurface(nsIDOMHTMLCanvasElement* aCanvas)
 {
   nsCOMPtr<nsINode> node = do_QueryInterface(aCanvas);
   if (!node) {
     return nullptr;
   }
 
   NS_ABORT_IF_FALSE(node->IsElement(),
                     "An nsINode that implements nsIDOMHTMLCanvasElement should "
                     "be an element.");
   nsLayoutUtils::SurfaceFromElementResult result =
-    nsLayoutUtils::SurfaceFromElement(node->AsElement(),
-                                      nsLayoutUtils::SFE_WANT_IMAGE_SURFACE);
-  return result.mSurface.forget().downcast<gfxImageSurface>();
+    nsLayoutUtils::SurfaceFromElement(node->AsElement());
+  return result.mSourceSurface->GetDataSurface();
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::CompareCanvases(nsIDOMHTMLCanvasElement *aCanvas1,
                                   nsIDOMHTMLCanvasElement *aCanvas2,
                                   uint32_t* aMaxDifference,
                                   uint32_t* retVal)
 {
@@ -1412,45 +1412,45 @@ nsDOMWindowUtils::CompareCanvases(nsIDOM
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   if (aCanvas1 == nullptr ||
       aCanvas2 == nullptr ||
       retVal == nullptr)
     return NS_ERROR_FAILURE;
 
-  nsRefPtr<gfxImageSurface> img1 = CanvasToImageSurface(aCanvas1);
-  nsRefPtr<gfxImageSurface> img2 = CanvasToImageSurface(aCanvas2);
+  RefPtr<DataSourceSurface> img1 = CanvasToDataSourceSurface(aCanvas1);
+  RefPtr<DataSourceSurface> img2 = CanvasToDataSourceSurface(aCanvas2);
 
   if (img1 == nullptr || img2 == nullptr ||
       img1->GetSize() != img2->GetSize() ||
       img1->Stride() != img2->Stride())
     return NS_ERROR_FAILURE;
 
   int v;
-  gfxIntSize size = img1->GetSize();
+  IntSize size = img1->GetSize();
   uint32_t stride = img1->Stride();
 
   // we can optimize for the common all-pass case
   if (stride == (uint32_t) size.width * 4) {
-    v = memcmp(img1->Data(), img2->Data(), size.width * size.height * 4);
+    v = memcmp(img1->GetData(), img2->GetData(), size.width * size.height * 4);
     if (v == 0) {
       if (aMaxDifference)
         *aMaxDifference = 0;
       *retVal = 0;
       return NS_OK;
     }
   }
 
   uint32_t dc = 0;
   uint32_t different = 0;
 
   for (int j = 0; j < size.height; j++) {
-    unsigned char *p1 = img1->Data() + j*stride;
-    unsigned char *p2 = img2->Data() + j*stride;
+    unsigned char *p1 = img1->GetData() + j*stride;
+    unsigned char *p2 = img2->GetData() + j*stride;
     v = memcmp(p1, p2, stride);
 
     if (v) {
       for (int i = 0; i < size.width; i++) {
         if (*(uint32_t*) p1 != *(uint32_t*) p2) {
 
           different++;
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -38,16 +38,17 @@
 // Helper Classes
 #include "nsJSUtils.h"
 #include "jsapi.h"              // for JSAutoRequest
 #include "js/OldDebugAPI.h"     // for JS_ClearWatchPointsForObject
 #include "jswrapper.h"
 #include "nsReadableUtils.h"
 #include "nsDOMClassInfo.h"
 #include "nsJSEnvironment.h"
+#include "ScriptSettings.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Likely.h"
 
 // Other Classes
 #include "nsEventListenerManager.h"
 #include "mozilla/dom/BarProps.h"
 #include "nsContentCID.h"
 #include "nsLayoutStatics.h"
@@ -204,16 +205,17 @@
 #include "nsSandboxFlags.h"
 #include "TimeChangeObserver.h"
 #include "mozilla/dom/AudioContext.h"
 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsITabChild.h"
 #include "nsIDOMMediaQueryList.h"
+#include "mozilla/dom/ScriptSettings.h"
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/SpeechSynthesis.h"
 #endif
 
 #ifdef MOZ_JSDEBUGGER
 #include "jsdIDebuggerService.h"
 #endif
@@ -4939,18 +4941,19 @@ NS_IMETHODIMP
 nsGlobalWindow::RequestAnimationFrame(const JS::Value& aCallback,
                                       JSContext* cx,
                                       int32_t* aHandle)
 {
   if (!aCallback.isObject() || !JS_ObjectIsCallable(cx, &aCallback.toObject())) {
     return NS_ERROR_INVALID_ARG;
   }
 
+  JS::Rooted<JSObject*> callbackObj(cx, &aCallback.toObject());
   nsRefPtr<FrameRequestCallback> callback =
-    new FrameRequestCallback(&aCallback.toObject());
+    new FrameRequestCallback(callbackObj, GetIncumbentGlobal());
 
   ErrorResult rv;
   *aHandle = RequestAnimationFrame(*callback, rv);
 
   return rv.ErrorCode();
 }
 
 NS_IMETHODIMP
@@ -11206,28 +11209,28 @@ nsGlobalWindow::OpenInternal(const nsASt
     if (!aCalledNoScript) {
       // We asserted at the top of this function that aNavigate is true for
       // !aCalledNoScript.
       rv = pwwatch->OpenWindow2(this, url.get(), name_ptr, options_ptr,
                                 /* aCalledFromScript = */ true,
                                 aDialog, aNavigate, argv,
                                 getter_AddRefs(domReturn));
     } else {
-      // Push a null JSContext here so that the window watcher won't screw us
+      // Force a system caller here so that the window watcher won't screw us
       // up.  We do NOT want this case looking at the JS context on the stack
       // when searching.  Compare comments on
       // nsIDOMWindow::OpenWindow and nsIWindowWatcher::OpenWindow.
 
       // Note: Because nsWindowWatcher is so broken, it's actually important
-      // that we don't push a null cx here, because that screws it up when it
-      // tries to compute the caller principal to associate with dialog
+      // that we don't force a system caller here, because that screws it up
+      // when it tries to compute the caller principal to associate with dialog
       // arguments. That whole setup just really needs to be rewritten. :-(
-      nsCxPusher pusher;
+      Maybe<AutoSystemCaller> asc;
       if (!aContentModal) {
-        pusher.PushNull();
+        asc.construct();
       }
 
 
       rv = pwwatch->OpenWindow2(this, url.get(), name_ptr, options_ptr,
                                 /* aCalledFromScript = */ false,
                                 aDialog, aNavigate, aExtraArgument,
                                 getter_AddRefs(domReturn));
 
@@ -13224,20 +13227,20 @@ nsGlobalWindow::DisableNetworkEvent(uint
                                              JS::Value *vp) {                \
     EventHandlerNonNull* h = GetOn##name_();                                 \
     vp->setObjectOrNull(h ? h->Callable().get() : nullptr);                  \
     return NS_OK;                                                            \
   }                                                                          \
   NS_IMETHODIMP nsGlobalWindow::SetOn##name_(JSContext *cx,                  \
                                              const JS::Value &v) {           \
     nsRefPtr<EventHandlerNonNull> handler;                                   \
-    JSObject *callable;                                                      \
+    JS::Rooted<JSObject*> callable(cx);                                      \
     if (v.isObject() &&                                                      \
         JS_ObjectIsCallable(cx, callable = &v.toObject())) {                 \
-      handler = new EventHandlerNonNull(callable);                           \
+      handler = new EventHandlerNonNull(callable, GetIncumbentGlobal());     \
     }                                                                        \
     SetOn##name_(handler);                                                   \
     return NS_OK;                                                            \
   }
 #define ERROR_EVENT(name_, id_, type_, struct_)                              \
   NS_IMETHODIMP nsGlobalWindow::GetOn##name_(JSContext *cx,                  \
                                              JS::Value *vp) {                \
     nsEventListenerManager *elm = GetExistingListenerManager();              \
@@ -13254,20 +13257,20 @@ nsGlobalWindow::DisableNetworkEvent(uint
   NS_IMETHODIMP nsGlobalWindow::SetOn##name_(JSContext *cx,                  \
                                              const JS::Value &v) {           \
     nsEventListenerManager *elm = GetOrCreateListenerManager();              \
     if (!elm) {                                                              \
       return NS_ERROR_OUT_OF_MEMORY;                                         \
     }                                                                        \
                                                                              \
     nsRefPtr<OnErrorEventHandlerNonNull> handler;                            \
-    JSObject *callable;                                                      \
+    JS::Rooted<JSObject*> callable(cx);                                      \
     if (v.isObject() &&                                                      \
         JS_ObjectIsCallable(cx, callable = &v.toObject())) {                 \
-      handler = new OnErrorEventHandlerNonNull(callable);                    \
+      handler = new OnErrorEventHandlerNonNull(callable, GetIncumbentGlobal()); \
     }                                                                        \
     elm->SetEventHandler(handler);                                           \
     return NS_OK;                                                            \
   }
 #define BEFOREUNLOAD_EVENT(name_, id_, type_, struct_)                       \
   NS_IMETHODIMP nsGlobalWindow::GetOn##name_(JSContext *cx,                  \
                                              JS::Value *vp) {                \
     nsEventListenerManager *elm = GetExistingListenerManager();              \
@@ -13285,20 +13288,20 @@ nsGlobalWindow::DisableNetworkEvent(uint
   NS_IMETHODIMP nsGlobalWindow::SetOn##name_(JSContext *cx,                  \
                                              const JS::Value &v) {           \
     nsEventListenerManager *elm = GetOrCreateListenerManager();              \
     if (!elm) {                                                              \
       return NS_ERROR_OUT_OF_MEMORY;                                         \
     }                                                                        \
                                                                              \
     nsRefPtr<OnBeforeUnloadEventHandlerNonNull> handler;                     \
-    JSObject *callable;                                                      \
+    JS::Rooted<JSObject*> callable(cx);                                      \
     if (v.isObject() &&                                                      \
         JS_ObjectIsCallable(cx, callable = &v.toObject())) {                 \
-      handler = new OnBeforeUnloadEventHandlerNonNull(callable);             \
+      handler = new OnBeforeUnloadEventHandlerNonNull(callable, GetIncumbentGlobal()); \
     }                                                                        \
     elm->SetEventHandler(handler);                                           \
     return NS_OK;                                                            \
   }
 #define WINDOW_ONLY_EVENT EVENT
 #define TOUCH_EVENT EVENT
 #include "nsEventNameList.h"
 #undef TOUCH_EVENT
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -355,17 +355,17 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW
     if (nsJSUtils::GetCallingLocation(cx, &filename, &mLineNo)) {
       mFileName.Assign(filename);
     }
   } else if (funobj) {
     *aAllowEval = true;
 
     mozilla::HoldJSObjects(this);
 
-    mFunction = new Function(funobj);
+    mFunction = new Function(funobj, GetIncumbentGlobal());
 
     // Create our arg array.  argc is the number of arguments passed
     // to setTimeout or setInterval; the first two are our callback
     // and the delay, so only arguments after that need to go in our
     // array.
     // std::max(argc - 2, 0) wouldn't work right because argc is unsigned.
     uint32_t argCount = std::max(argc, 2u) - 2;
 
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -22,16 +22,17 @@
 #include "nsIXPConnect.h"
 #include "WrapperFactory.h"
 #include "xpcprivate.h"
 #include "XPCQuickStubs.h"
 #include "XrayWrapper.h"
 #include "nsPrintfCString.h"
 #include "prprf.h"
 
+#include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/DOMErrorBinding.h"
 #include "mozilla/dom/HTMLObjectElement.h"
 #include "mozilla/dom/HTMLObjectElementBinding.h"
 #include "mozilla/dom/HTMLSharedObjectElement.h"
 #include "mozilla/dom/HTMLEmbedElementBinding.h"
 #include "mozilla/dom/HTMLAppletElementBinding.h"
 #include "WorkerPrivate.h"
@@ -2010,22 +2011,22 @@ ConstructJSImplementation(JSContext* aCx
 {
   // Get the window to use as a parent and for initialization.
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
   if (!window) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  // Make sure to have nothing on the JS context stack while creating and
+  // Make sure to divorce ourselves from the calling JS while creating and
   // initializing the object, so exceptions from that will get reported
   // properly, since those are never exceptions that a spec wants to be thrown.
-  {  // Scope for the nsCxPusher
-    nsCxPusher pusher;
-    pusher.PushNull();
+  {
+    AutoSystemCaller asc;
+
     // Get the XPCOM component containing the JS implementation.
     nsCOMPtr<nsISupports> implISupports = do_CreateInstance(aContractId);
     if (!implISupports) {
       NS_WARNING("Failed to get JS implementation for contract");
       aRv.Throw(NS_ERROR_FAILURE);
       return nullptr;
     }
     // Initialize the object, if it implements nsIDOMGlobalPropertyInitializer.
--- a/dom/bindings/CallbackFunction.h
+++ b/dom/bindings/CallbackFunction.h
@@ -20,18 +20,19 @@
 #include "mozilla/dom/CallbackObject.h"
 
 namespace mozilla {
 namespace dom {
 
 class CallbackFunction : public CallbackObject
 {
 public:
-  explicit CallbackFunction(JSObject* aCallable)
-    : CallbackObject(aCallable)
+  explicit CallbackFunction(JS::Handle<JSObject*> aCallable,
+                            nsIGlobalObject* aIncumbentGlobal)
+    : CallbackObject(aCallable, aIncumbentGlobal)
   {
     MOZ_ASSERT(JS_ObjectIsCallable(nullptr, mCallback));
   }
 
   JS::Handle<JSObject*> Callable() const
   {
     return Callback();
   }
--- a/dom/bindings/CallbackInterface.h
+++ b/dom/bindings/CallbackInterface.h
@@ -19,18 +19,19 @@
 #include "mozilla/dom/CallbackObject.h"
 
 namespace mozilla {
 namespace dom {
 
 class CallbackInterface : public CallbackObject
 {
 public:
-  explicit CallbackInterface(JSObject* aCallback)
-    : CallbackObject(aCallback)
+  explicit CallbackInterface(JS::Handle<JSObject*> aCallback,
+                             nsIGlobalObject *aIncumbentGlobal)
+    : CallbackObject(aCallback, aIncumbentGlobal)
   {
   }
 
 protected:
   bool GetCallableProperty(JSContext* cx, const char* aPropName,
                            JS::MutableHandle<JS::Value> aCallable);
 
 };
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -14,41 +14,44 @@
 #include "nsIScriptContext.h"
 #include "nsPIDOMWindow.h"
 #include "nsJSUtils.h"
 #include "nsCxPusher.h"
 #include "nsIScriptSecurityManager.h"
 #include "xpcprivate.h"
 #include "WorkerPrivate.h"
 #include "nsGlobalWindow.h"
+#include "WorkerScope.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CallbackObject)
   NS_INTERFACE_MAP_ENTRY(mozilla::dom::CallbackObject)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(CallbackObject)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(CallbackObject)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(CallbackObject)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CallbackObject)
   tmp->DropCallback();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIncumbentGlobal)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CallbackObject)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIncumbentGlobal)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CallbackObject)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCallback)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
-CallbackObject::CallSetup::CallSetup(JS::Handle<JSObject*> aCallback,
+CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
                                      ErrorResult& aRv,
                                      ExceptionHandling aExceptionHandling,
                                      JSCompartment* aCompartment)
   : mCx(nullptr)
   , mCompartment(aCompartment)
   , mErrorResult(aRv)
   , mExceptionHandling(aExceptionHandling)
   , mIsMainThread(NS_IsMainThread())
@@ -58,18 +61,19 @@ CallbackObject::CallSetup::CallSetup(JS:
   }
   // We need to produce a useful JSContext here.  Ideally one that the callback
   // is in some sense associated with, so that we can sort of treat it as a
   // "script entry point".  Though once we actually have script entry points,
   // we'll need to do the script entry point bits once we have an actual
   // callable.
 
   // First, find the real underlying callback.
-  JSObject* realCallback = js::UncheckedUnwrap(aCallback);
+  JSObject* realCallback = js::UncheckedUnwrap(aCallback->CallbackPreserveColor());
   JSContext* cx = nullptr;
+  nsIGlobalObject* globalObject = nullptr;
 
   if (mIsMainThread) {
     // Now get the global and JSContext for this callback.
     nsGlobalWindow* win = xpc::WindowGlobalOrNull(realCallback);
     if (win) {
       // Make sure that if this is a window it's the current inner, since the
       // nsIScriptContext and hence JSContext are associated with the outer
       // window.  Which means that if someone holds on to a function from a
@@ -80,52 +84,70 @@ CallbackObject::CallSetup::CallSetup(JS:
       if (!outer || win != outer->GetCurrentInnerWindow()) {
         // Just bail out from here
         return;
       }
       cx = win->GetContext() ? win->GetContext()->GetNativeContext()
                              // This happens - Removing it causes
                              // test_bug293235.xul to go orange.
                              : nsContentUtils::GetSafeJSContext();
+      globalObject = win;
     } else {
-      // No DOM Window. Use the SafeJSContext.
+      // No DOM Window. Store the global and use the SafeJSContext.
+      JSObject* glob = js::GetGlobalForObjectCrossCompartment(realCallback);
+      globalObject = xpc::GetNativeForGlobal(glob);
+      MOZ_ASSERT(globalObject);
       cx = nsContentUtils::GetSafeJSContext();
     }
-
-    // Make sure our JSContext is pushed on the stack.
-    mCxPusher.Push(cx);
   } else {
     cx = workers::GetCurrentThreadJSContext();
+    globalObject = workers::GetCurrentThreadWorkerPrivate()->GlobalScope();
   }
 
-  // Unmark the callable, and stick it in a Rooted before it can go gray again.
+  // Bail out if there's no useful global. This seems to happen intermittently
+  // on gaia-ui tests, probably because nsInProcessTabChildGlobal is returning
+  // null in some kind of teardown state.
+  if (!globalObject->GetGlobalJSObject()) {
+    return;
+  }
+
+  mAutoEntryScript.construct(globalObject, mIsMainThread, cx);
+  if (aCallback->IncumbentGlobalOrNull()) {
+    mAutoIncumbentScript.construct(aCallback->IncumbentGlobalOrNull());
+  }
+
+  // Unmark the callable (by invoking Callback() and not the CallbackPreserveColor()
+  // variant), and stick it in a Rooted before it can go gray again.
   // Nothing before us in this function can trigger a CC, so it's safe to wait
   // until here it do the unmark. This allows us to order the following two
   // operations _after_ the Push() above, which lets us take advantage of the
   // JSAutoRequest embedded in the pusher.
   //
   // We can do this even though we're not in the right compartment yet, because
   // Rooted<> does not care about compartments.
-  JS::ExposeObjectToActiveJS(aCallback);
-  mRootedCallable.construct(cx, aCallback);
+  mRootedCallable.construct(cx, aCallback->Callback());
 
   if (mIsMainThread) {
     // Check that it's ok to run this callback at all.
-    // Make sure to unwrap aCallback before passing it in to get the global of
-    // the callback object, not the wrapper.
+    // Make sure to use realCallback to get the global of the callback object,
+    // not the wrapper.
     bool allowed = nsContentUtils::GetSecurityManager()->
-      ScriptAllowed(js::GetGlobalForObjectCrossCompartment(js::UncheckedUnwrap(aCallback)));
+      ScriptAllowed(js::GetGlobalForObjectCrossCompartment(realCallback));
 
     if (!allowed) {
       return;
     }
   }
 
   // Enter the compartment of our callback, so we can actually work with it.
-  mAc.construct(cx, aCallback);
+  //
+  // Note that if the callback is a wrapper, this will not be the same
+  // compartment that we ended up in with mAutoEntryScript above, because the
+  // entry point is based off of the unwrapped callback (realCallback).
+  mAc.construct(cx, mRootedCallable.ref());
 
   // And now we're ready to go.
   mCx = cx;
 
   // Make sure the JS engine doesn't report exceptions we want to re-throw
   if (mExceptionHandling == eRethrowContentExceptions ||
       mExceptionHandling == eRethrowExceptions) {
     mSavedJSContextOptions = JS::ContextOptionsRef(cx);
@@ -189,27 +211,21 @@ CallbackObject::CallSetup::~CallSetup()
       nsJSUtils::ReportPendingException(mCx);
     }
   }
 
   // To get our nesting right we have to destroy our JSAutoCompartment first.
   // But be careful: it might not have been constructed at all!
   mAc.destroyIfConstructed();
 
-  // XXXbz For that matter why do we need to manually call ScriptEvaluated at
-  // all?  nsCxPusher::Pop will do that nowadays if !mScriptIsRunning, so the
-  // concerns from bug 295983 don't seem relevant anymore.  Do we want to make
-  // sure it's still called when !mScriptIsRunning?  I guess play it safe for
-  // now and do what CallEventHandler did, which is call always.
-
-  // Popping an nsCxPusher is safe even if it never got pushed.
-  mCxPusher.Pop();
+  mAutoIncumbentScript.destroyIfConstructed();
+  mAutoEntryScript.destroyIfConstructed();
 
   // It is important that this is the last thing we do, after leaving the
-  // compartment and popping the context.
+  // compartment and undoing all our entry/incumbent script changes
   if (mIsMainThread) {
     nsContentUtils::LeaveMicroTask();
   }
 }
 
 already_AddRefed<nsISupports>
 CallbackObjectHolderBase::ToXPCOMCallback(CallbackObject* aCallback,
                                           const nsIID& aIID) const
--- a/dom/bindings/CallbackObject.h
+++ b/dom/bindings/CallbackObject.h
@@ -19,18 +19,18 @@
 
 #include "nsISupports.h"
 #include "nsISupportsImpl.h"
 #include "nsCycleCollectionParticipant.h"
 #include "jswrapper.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/HoldDropJSObjects.h"
+#include "mozilla/dom/ScriptSettings.h"
 #include "nsContentUtils.h"
-#include "nsCxPusher.h"
 #include "nsWrapperCache.h"
 #include "nsJSEnvironment.h"
 #include "xpcpublic.h"
 
 namespace mozilla {
 namespace dom {
 
 #define DOM_CALLBACKOBJECT_IID \
@@ -40,19 +40,23 @@ namespace dom {
 class CallbackObject : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(DOM_CALLBACKOBJECT_IID)
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CallbackObject)
 
-  explicit CallbackObject(JSObject* aCallback)
+  // The caller may pass a global object which will act as an override for the
+  // incumbent script settings object when the callback is invoked (overriding
+  // the entry point computed from aCallback). If no override is required, the
+  // caller should pass null.
+  explicit CallbackObject(JS::Handle<JSObject*> aCallback, nsIGlobalObject *aIncumbentGlobal)
   {
-    Init(aCallback);
+    Init(aCallback, aIncumbentGlobal);
   }
 
   virtual ~CallbackObject()
   {
     DropCallback();
   }
 
   JS::Handle<JSObject*> Callback() const
@@ -71,69 +75,77 @@ public:
    */
   JS::Handle<JSObject*> CallbackPreserveColor() const
   {
     // Calling fromMarkedLocation() is safe because we trace our mCallback, and
     // because the value of mCallback cannot change after if has been set.
     return JS::Handle<JSObject*>::fromMarkedLocation(mCallback.address());
   }
 
+  nsIGlobalObject* IncumbentGlobalOrNull() const
+  {
+    return mIncumbentGlobal;
+  }
+
   enum ExceptionHandling {
     // Report any exception and don't throw it to the caller code.
     eReportExceptions,
     // Throw an exception to the caller code if the thrown exception is a
     // binding object for a DOMError from the caller's scope, otherwise report
     // it.
     eRethrowContentExceptions,
     // Throw any exception to the caller code.
     eRethrowExceptions
   };
 
 protected:
   explicit CallbackObject(CallbackObject* aCallbackObject)
   {
-    Init(aCallbackObject->mCallback);
+    Init(aCallbackObject->mCallback, aCallbackObject->mIncumbentGlobal);
   }
 
 private:
-  inline void Init(JSObject* aCallback)
+  inline void Init(JSObject* aCallback, nsIGlobalObject* aIncumbentGlobal)
   {
     MOZ_ASSERT(aCallback && !mCallback);
     // Set mCallback before we hold, on the off chance that a GC could somehow
     // happen in there... (which would be pretty odd, granted).
     mCallback = aCallback;
     mozilla::HoldJSObjects(this);
+
+    mIncumbentGlobal = aIncumbentGlobal;
   }
 
   CallbackObject(const CallbackObject&) MOZ_DELETE;
   CallbackObject& operator =(const CallbackObject&) MOZ_DELETE;
 
 protected:
   void DropCallback()
   {
     if (mCallback) {
       mCallback = nullptr;
       mozilla::DropJSObjects(this);
     }
   }
 
   JS::Heap<JSObject*> mCallback;
+  nsCOMPtr<nsIGlobalObject> mIncumbentGlobal;
 
   class MOZ_STACK_CLASS CallSetup
   {
     /**
      * A class that performs whatever setup we need to safely make a
      * call while this class is on the stack, After the constructor
      * returns, the call is safe to make if GetContext() returns
      * non-null.
      */
   public:
     // If aExceptionHandling == eRethrowContentExceptions then aCompartment
     // needs to be set to the caller's compartment.
-    CallSetup(JS::Handle<JSObject*> aCallable, ErrorResult& aRv,
+    CallSetup(CallbackObject* aCallback, ErrorResult& aRv,
               ExceptionHandling aExceptionHandling,
               JSCompartment* aCompartment = nullptr);
     ~CallSetup();
 
     JSContext* GetContext() const
     {
       return mCx;
     }
@@ -147,27 +159,27 @@ protected:
     // Members which can go away whenever
     JSContext* mCx;
 
     // Caller's compartment. This will only have a sensible value if
     // mExceptionHandling == eRethrowContentExceptions.
     JSCompartment* mCompartment;
 
     // And now members whose construction/destruction order we need to control.
-
-    nsCxPusher mCxPusher;
+    Maybe<AutoEntryScript> mAutoEntryScript;
+    Maybe<AutoIncumbentScript> mAutoIncumbentScript;
 
     // Constructed the rooter within the scope of mCxPusher above, so that it's
     // always within a request during its lifetime.
     Maybe<JS::Rooted<JSObject*> > mRootedCallable;
 
     // Can't construct a JSAutoCompartment without a JSContext either.  Also,
-    // Put mAc after mCxPusher so that we exit the compartment before we pop the
-    // JSContext.  Though in practice we'll often manually order those two
-    // things.
+    // Put mAc after mAutoEntryScript so that we exit the compartment before
+    // we pop the JSContext. Though in practice we'll often manually order
+    // those two things.
     Maybe<JSAutoCompartment> mAc;
 
     // An ErrorResult to possibly re-throw exceptions on and whether
     // we should re-throw them.
     ErrorResult& mErrorResult;
     const ExceptionHandling mExceptionHandling;
     JS::ContextOptions mSavedJSContextOptions;
     const bool mIsMainThread;
@@ -331,38 +343,17 @@ public:
 
   // Try to return a WebIDLCallbackT version of this object.
   already_AddRefed<WebIDLCallbackT> ToWebIDLCallback() const
   {
     if (HasWebIDLCallback()) {
       nsRefPtr<WebIDLCallbackT> callback = GetWebIDLCallback();
       return callback.forget();
     }
-
-    XPCOMCallbackT* callback = GetXPCOMCallback();
-    if (!callback) {
-      return nullptr;
-    }
-
-    nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = do_QueryInterface(callback);
-    if (!wrappedJS) {
-      return nullptr;
-    }
-
-    AutoSafeJSContext cx;
-
-    JS::Rooted<JSObject*> obj(cx, wrappedJS->GetJSObject());
-    if (!obj) {
-      return nullptr;
-    }
-
-    JSAutoCompartment ac(cx, obj);
-
-    nsRefPtr<WebIDLCallbackT> newCallback = new WebIDLCallbackT(obj);
-    return newCallback.forget();
+    return nullptr;
   }
 
 private:
   static const uintptr_t XPCOMCallbackFlag = 1u;
 
   friend void
   ImplCycleCollectionUnlink<WebIDLCallbackT,
                             XPCOMCallbackT>(CallbackObjectHolder& aField);
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -3391,17 +3391,20 @@ for (uint32_t i = 0; i < length; ++i) {
 
         if descriptor.interface.isCallback():
             name = descriptor.interface.identifier.name
             if type.nullable() or isCallbackReturnValue:
                 declType = CGGeneric("nsRefPtr<%s>" % name);
             else:
                 declType = CGGeneric("OwningNonNull<%s>" % name)
             conversion = (
-                "${declName} = new %s(&${val}.toObject());\n" % name)
+                "{ // Scope for tempRoot\n"
+                "  JS::Rooted<JSObject*> tempRoot(cx, &${val}.toObject());\n"
+                "  ${declName} = new %s(tempRoot, mozilla::dom::GetIncumbentGlobal());\n"
+                "}" % name)
 
             template = wrapObjectTemplate(conversion, type,
                                           "${declName} = nullptr",
                                           failureCode)
             return JSToNativeConversionInfo(template, declType=declType,
                                             dealWithOptional=isOptional)
 
         # This is an interface that we implement as a concrete class
@@ -3724,17 +3727,20 @@ for (uint32_t i = 0; i < length; ++i) {
         assert not type.treatNonCallableAsNull() or type.nullable()
 
         name = type.unroll().identifier.name
         if type.nullable():
             declType = CGGeneric("nsRefPtr<%s>" % name);
         else:
             declType = CGGeneric("OwningNonNull<%s>" % name)
         conversion = (
-            "  ${declName} = new %s(&${val}.toObject());\n" % name)
+            "{ // Scope for tempRoot\n"
+            "  JS::Rooted<JSObject*> tempRoot(cx, &${val}.toObject());\n"
+            "  ${declName} = new %s(tempRoot, mozilla::dom::GetIncumbentGlobal());\n"
+            "}\n" % name)
 
         if allowTreatNonCallableAsNull and type.treatNonCallableAsNull():
             haveCallable = "JS_ObjectIsCallable(cx, &${val}.toObject())"
             if not isDefinitelyObject:
                 haveCallable = "${val}.isObject() && " + haveCallable
             if defaultValue is not None:
                 assert(isinstance(defaultValue, IDLNullValue))
                 haveCallable = "${haveValue} && " + haveCallable
@@ -10544,17 +10550,17 @@ class CGJSImplClass(CGBindingImplClass):
         if descriptor.interface.hasChildInterfaces():
             decorators = ""
             # We need a public virtual destructor our subclasses can use
             destructor = ClassDestructor(virtual=True, visibility="public")
         else:
             decorators = "MOZ_FINAL"
             destructor = None
 
-        baseConstructors=["mImpl(new %s(aJSImplObject))" % jsImplName(descriptor.name),
+        baseConstructors=["mImpl(new %s(aJSImplObject, /* aIncumbentGlobal = */ nullptr))" % jsImplName(descriptor.name),
                           "mParent(aParent)"]
         parentInterface = descriptor.interface.parent
         while parentInterface:
             if parentInterface.isJSImplemented():
                 baseConstructors.insert(
                     0, "%s(aJSImplObject, aParent)" % parentClass )
                 break
             parentInterface = parentInterface.parent
@@ -10671,22 +10677,22 @@ class CGCallback(CGClass):
                 realMethods.extend(self.getMethodImpls(method))
         CGClass.__init__(self, name,
                          bases=[ClassBase(baseName)],
                          constructors=self.getConstructors(),
                          methods=realMethods+getters+setters)
 
     def getConstructors(self):
         return [ClassConstructor(
-            [Argument("JSObject*", "aCallback")],
+            [Argument("JS::Handle<JSObject*>", "aCallback"), Argument("nsIGlobalObject*", "aIncumbentGlobal")],
             bodyInHeader=True,
             visibility="public",
             explicit=True,
             baseConstructors=[
-                "%s(aCallback)" % self.baseName
+                "%s(aCallback, aIncumbentGlobal)" % self.baseName,
                 ])]
 
     def getMethodImpls(self, method):
         assert method.needThisHandling
         args = list(method.args)
         # Strip out the JSContext*/JSObject* args
         # that got added.
         assert args[0].name == "cx" and args[0].argType == "JSContext*"
@@ -10701,17 +10707,17 @@ class CGCallback(CGClass):
         # method, insert our optional argument for deciding whether the
         # CallSetup should re-throw exceptions on aRv.
         args.append(Argument("ExceptionHandling", "aExceptionHandling",
                              "eReportExceptions"))
         # And now insert our template argument.
         argsWithoutThis = list(args)
         args.insert(0, Argument("const T&",  "thisObj"))
 
-        setupCall = ("CallSetup s(CallbackPreserveColor(), aRv, aExceptionHandling);\n"
+        setupCall = ("CallSetup s(this, aRv, aExceptionHandling);\n"
                      "if (!s.GetContext()) {\n"
                      "  aRv.Throw(NS_ERROR_UNEXPECTED);\n"
                      "  return${errorReturn};\n"
                      "}\n")
 
         bodyWithThis = string.Template(
             setupCall+
             "JS::Rooted<JSObject*> thisObjJS(s.GetContext(),\n"
@@ -10991,17 +10997,17 @@ class CallbackMember(CGNativeMember):
         # well as a JSContext.
         return [Argument("JSContext*", "cx"),
                 Argument("JS::Handle<JSObject*>", "aThisObj")] + args
 
     def getCallSetup(self):
         if self.needThisHandling:
             # It's been done for us already
             return ""
-        callSetup = "CallSetup s(CallbackPreserveColor(), aRv"
+        callSetup = "CallSetup s(this, aRv"
         if self.rethrowContentException:
             # getArgs doesn't add the aExceptionHandling argument but does add
             # aCompartment for us.
             callSetup += ", eRethrowContentExceptions, aCompartment"
         else:
             callSetup += ", aExceptionHandling"
         callSetup += ");"
         return string.Template(
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2526,16 +2526,17 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED_1(Tab
                                      mMessageManager)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TabChildGlobal)
   NS_INTERFACE_MAP_ENTRY(nsIMessageListenerManager)
   NS_INTERFACE_MAP_ENTRY(nsIMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsISyncMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsIContentFrameMessageManager)
   NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
+  NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ContentFrameMessageManager)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(TabChildGlobal, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(TabChildGlobal, nsDOMEventTargetHelper)
 
 /* [notxpcom] boolean markForCC (); */
 // This method isn't automatically forwarded safely because it's notxpcom, so
@@ -2590,15 +2591,25 @@ TabChildGlobal::Atob(const nsAString& aA
 }
 
 JSContext*
 TabChildGlobal::GetJSContextForEventHandlers()
 {
   return nsContentUtils::GetSafeJSContext();
 }
 
-nsIPrincipal* 
+nsIPrincipal*
 TabChildGlobal::GetPrincipal()
 {
   if (!mTabChild)
     return nullptr;
   return mTabChild->GetPrincipal();
 }
+
+JSObject*
+TabChildGlobal::GetGlobalJSObject()
+{
+  NS_ENSURE_TRUE(mTabChild, nullptr);
+  nsCOMPtr<nsIXPConnectJSObjectHolder> ref = mTabChild->GetGlobal();
+  NS_ENSURE_TRUE(ref, nullptr);
+  return ref->GetJSObject();
+}
+
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -46,17 +46,18 @@ class RenderFrameChild;
 namespace dom {
 
 class TabChild;
 class PContentDialogChild;
 class ClonedMessageData;
 
 class TabChildGlobal : public nsDOMEventTargetHelper,
                        public nsIContentFrameMessageManager,
-                       public nsIScriptObjectPrincipal
+                       public nsIScriptObjectPrincipal,
+                       public nsIGlobalObject
 {
 public:
   TabChildGlobal(TabChild* aTabChild);
   void Init();
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TabChildGlobal, nsDOMEventTargetHelper)
   NS_FORWARD_SAFE_NSIMESSAGELISTENERMANAGER(mMessageManager)
   NS_FORWARD_SAFE_NSIMESSAGESENDER(mMessageManager)
@@ -122,16 +123,17 @@ public:
   PreHandleEvent(nsEventChainPreVisitor& aVisitor)
   {
     aVisitor.mForceContentDispatch = true;
     return NS_OK;
   }
 
   virtual JSContext* GetJSContextForEventHandlers() MOZ_OVERRIDE;
   virtual nsIPrincipal* GetPrincipal() MOZ_OVERRIDE;
+  virtual JSObject* GetGlobalJSObject() MOZ_OVERRIDE;
 
   nsCOMPtr<nsIContentFrameMessageManager> mMessageManager;
   TabChild* mTabChild;
 };
 
 class ContentListener MOZ_FINAL : public nsIDOMEventListener
 {
 public:
--- a/dom/locales/en-US/chrome/layout/css.properties
+++ b/dom/locales/en-US/chrome/layout/css.properties
@@ -139,8 +139,19 @@ PESupportsConditionExpectedCloseParen=Ex
 PESupportsConditionExpectedStart2=Expected 'not', '(', or function while parsing supports condition but found '%1$S'.
 PESupportsConditionExpectedNot=Expected 'not' while parsing supports condition but found '%1$S'.
 PESupportsGroupRuleStart=Expected '{' to begin @supports rule but found '%1$S'.
 PEFilterEOF=filter
 PEExpectedNoneOrURL=Expected 'none' or URL but found '%1$S'.
 PEExpectedNoneOrURLOrFilterFunction=Expected 'none', URL, or filter function but found '%1$S'.
 PEExpectedNonnegativeNP=Expected non-negative number or percentage.
 PEFilterFunctionArgumentsParsingError=Error in parsing arguments for filter function.
+PEVariableEOF=variable
+PEVariableEmpty=Expected variable value but found '%1$S'.
+PEValueWithVariablesParsingError=Error in parsing value for '%1$S' after substituting variables.
+PEValueWithVariablesFallbackInherit=Falling back to 'inherit'.
+PEValueWithVariablesFallbackInitial=Falling back to 'initial'.
+PEInvalidVariableReference=Property contained reference to invalid variable.
+PEInvalidVariableTokenFallback=Found invalid token '%1$S' at top level of variable reference fallback.
+PEExpectedVariableNameEOF=identifier for variable name
+PEExpectedVariableName=Expected identifier for variable name but found '%1$S'.
+PEExpectedVariableFallback=Expected variable reference fallback after ','.
+PEExpectedVariableCommaOrCloseParen=Expected ',' or ')' after variable name in variable reference but found '%1$S'.
copy from dom/mobilemessage/src/gonk/MobileMessageDatabaseService.js
copy to dom/mobilemessage/src/gonk/MobileMessageDB.jsm
--- a/dom/mobilemessage/src/gonk/MobileMessageDatabaseService.js
+++ b/dom/mobilemessage/src/gonk/MobileMessageDB.jsm
@@ -9,30 +9,25 @@ const {classes: Cc, interfaces: Ci, util
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
 Cu.importGlobalProperties(["indexedDB"]);
 
 var RIL = {};
 Cu.import("resource://gre/modules/ril_consts.js", RIL);
 
-const RIL_MOBILEMESSAGEDATABASESERVICE_CONTRACTID =
-  "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1";
-const RIL_MOBILEMESSAGEDATABASESERVICE_CID =
-  Components.ID("{29785f90-6b5b-11e2-9201-3b280170b2ec}");
 const RIL_GETMESSAGESCURSOR_CID =
   Components.ID("{484d1ad8-840e-4782-9dc4-9ebc4d914937}");
 const RIL_GETTHREADSCURSOR_CID =
   Components.ID("{95ee7c3e-d6f2-4ec4-ade5-0c453c036d35}");
 
 const DEBUG = false;
 const DISABLE_MMS_GROUPING_FOR_RECEIVING = true;
 
 
-const DB_NAME = "sms";
 const DB_VERSION = 20;
 const MESSAGE_STORE_NAME = "sms";
 const THREAD_STORE_NAME = "thread";
 const PARTICIPANT_STORE_NAME = "participant";
 const MOST_RECENT_STORE_NAME = "most-recent";
 
 const DELIVERY_SENDING = "sending";
 const DELIVERY_SENT = "sent";
@@ -76,75 +71,34 @@ XPCOMUtils.defineLazyServiceGetter(this,
 
 XPCOMUtils.defineLazyGetter(this, "MMS", function () {
   let MMS = {};
   Cu.import("resource://gre/modules/MmsPduHelper.jsm", MMS);
   return MMS;
 });
 
 /**
- * MobileMessageDatabaseService
+ * MobileMessageDB
  */
-function MobileMessageDatabaseService() {
-  // Prime the directory service's cache to ensure that the ProfD entry exists
-  // by the time IndexedDB queries for it off the main thread. (See bug 743635.)
-  Services.dirsvc.get("ProfD", Ci.nsIFile);
-
-  let that = this;
-  this.newTxn(READ_ONLY, function(error, txn, messageStore){
-    if (error) {
-      return;
-    }
-    // In order to get the highest key value, we open a key cursor in reverse
-    // order and get only the first pointed value.
-    let request = messageStore.openCursor(null, PREV);
-    request.onsuccess = function onsuccess(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        if (DEBUG) {
-          debug("Could not get the last key from mobile message database. " +
-                "Probably empty database");
-        }
-        return;
-      }
-      that.lastMessageId = cursor.key || 0;
-      if (DEBUG) debug("Last assigned message ID was " + that.lastMessageId);
-    };
-    request.onerror = function onerror(event) {
-      if (DEBUG) {
-        debug("Could not get the last key from mobile message database " +
-              event.target.errorCode);
-      }
-    };
-  });
-  this.updatePendingTransactionToError();
-}
-MobileMessageDatabaseService.prototype = {
-
-  classID: RIL_MOBILEMESSAGEDATABASESERVICE_CID,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIRilMobileMessageDatabaseService,
-                                         Ci.nsIMobileMessageDatabaseService,
-                                         Ci.nsIObserver]),
+this.MobileMessageDB = function() {};
+MobileMessageDB.prototype = {
+  dbName: null,
+  dbVersion: null,
 
   /**
    * Cache the DB here.
    */
   db: null,
 
   /**
    * Last sms/mms object store key value in the database.
    */
   lastMessageId: 0,
 
   /**
-   * nsIObserver
-   */
-  observe: function observe() {},
-
-  /**
    * Prepare the database. This may include opening the database and upgrading
    * it to the latest schema version.
    *
    * @param callback
    *        Function that takes an error and db argument. It is called when
    *        the database is ready to use or if an error occurs while preparing
    *        the database.
    *
@@ -158,26 +112,26 @@ MobileMessageDatabaseService.prototype =
     }
 
     let self = this;
     function gotDB(db) {
       self.db = db;
       callback(null, db);
     }
 
-    let request = indexedDB.open(DB_NAME, DB_VERSION);
+    let request = indexedDB.open(this.dbName, this.dbVersion);
     request.onsuccess = function (event) {
-      if (DEBUG) debug("Opened database:", DB_NAME, DB_VERSION);
+      if (DEBUG) debug("Opened database:", self.dbName, self.dbVersion);
       gotDB(event.target.result);
     };
     request.onupgradeneeded = function (event) {
       if (DEBUG) {
-        debug("Database needs upgrade:", DB_NAME,
+        debug("Database needs upgrade:", self.dbName,
               event.oldVersion, event.newVersion);
-        debug("Correct new database version:", event.newVersion == DB_VERSION);
+        debug("Correct new database version:", event.newVersion == self.dbVersion);
       }
 
       let db = event.target.result;
 
       let currentVersion = event.oldVersion;
 
       function update(currentVersion) {
         let next = update.bind(self, currentVersion + 1);
@@ -330,25 +284,93 @@ MobileMessageDatabaseService.prototype =
           stores.push(txn.objectStore(storeName));
         }
       }
       callback(null, txn, stores);
     });
   },
 
   /**
+   * Initialize this MobileMessageDB.
+   *
+   * @param aDbName
+   *        A string name for that database.
+   * @param aDbVersion
+   *        The version that mmdb should upgrade to. 0 for the lastest version.
+   * @param aCallback
+   *        A function when either the initialization transaction is completed
+   *        or any error occurs.  Should take only one argument -- null when
+   *        initialized with success or the error object otherwise.
+   */
+  init: function init(aDbName, aDbVersion, aCallback) {
+    this.dbName = aDbName;
+    this.dbVersion = aDbVersion || DB_VERSION;
+
+    let self = this;
+    this.newTxn(READ_ONLY, function(error, txn, messageStore){
+      if (error) {
+        if (aCallback) {
+          aCallback(error);
+        }
+        return;
+      }
+
+      if (aCallback) {
+        txn.oncomplete = function() {
+          aCallback(null);
+        };
+      }
+
+      // In order to get the highest key value, we open a key cursor in reverse
+      // order and get only the first pointed value.
+      let request = messageStore.openCursor(null, PREV);
+      request.onsuccess = function onsuccess(event) {
+        let cursor = event.target.result;
+        if (!cursor) {
+          if (DEBUG) {
+            debug("Could not get the last key from mobile message database. " +
+                  "Probably empty database");
+          }
+          return;
+        }
+        self.lastMessageId = cursor.key || 0;
+        if (DEBUG) debug("Last assigned message ID was " + self.lastMessageId);
+      };
+      request.onerror = function onerror(event) {
+        if (DEBUG) {
+          debug("Could not get the last key from mobile message database " +
+                event.target.errorCode);
+        }
+      };
+    });
+  },
+
+  close: function close() {
+    if (!this.db) {
+      return;
+    }
+
+    this.db.close();
+    this.db = null;
+    this.lastMessageId = 0;
+  },
+
+  /**
    * Sometimes user might reboot or remove battery while sending/receiving
    * message. This is function set the status of message records to error.
    */
-  updatePendingTransactionToError: function updatePendingTransactionToError() {
+  updatePendingTransactionToError:
+    function updatePendingTransactionToError(aError) {
+    if (aError) {
+      return;
+    }
+
     this.newTxn(READ_WRITE, function (error, txn, messageStore) {
-      if (DEBUG) {
-        txn.onerror = function onerror(event) {
-          debug("updatePendingTransactionToError fail, event = " + event);
-        };
+      if (error) {
+        return;
       }
 
       let deliveryIndex = messageStore.index("delivery");
 
       // Set all 'delivery: sending' records to 'delivery: error' and 'deliveryStatus:
       // error'.
       let keyRange = IDBKeyRange.bound([DELIVERY_SENDING, 0], [DELIVERY_SENDING, ""]);
       let cursorRequestSending = deliveryIndex.openCursor(keyRange);
@@ -2592,32 +2614,32 @@ let FilterSearcherHelper = {
       range = IDBKeyRange.upperBound(endDate.getTime());
     }
     this.filterIndex("timestamp", range, direction, txn, collect);
   },
 
   /**
    * Instanciate a filtering transaction.
    *
-   * @param service
-   *        A MobileMessageDatabaseService. Used to create
+   * @param mmdb
+   *        A MobileMessageDB.
    * @param txn
    *        Ongoing IDBTransaction context object.
    * @param error
    *        Previous error while creating the transaction.
    * @param filter
    *        A SmsFilter object.
    * @param reverse
    *        A boolean value indicating whether we should filter message in
    *        reversed order.
    * @param collect
    *        Result colletor function. It takes three parameters -- txn, message
    *        id, and message timestamp.
    */
-  transact: function transact(service, txn, error, filter, reverse, collect) {
+  transact: function transact(mmdb, txn, error, filter, reverse, collect) {
     if (error) {
       //TODO look at event.target.errorCode, pick appropriate error constant.
       if (DEBUG) debug("IDBRequest error " + error.target.errorCode);
       collect(txn, COLLECT_ID_ERROR, COLLECT_TIMESTAMP_UNUSED);
       return;
     }
 
     let direction = reverse ? PREV : NEXT;
@@ -2697,19 +2719,19 @@ let FilterSearcherHelper = {
     if (filter.numbers) {
       if (DEBUG) debug("filter.numbers " + filter.numbers.join(", "));
 
       if (!single) {
         collect = intersectionCollector.newContext();
       }
 
       let participantStore = txn.objectStore(PARTICIPANT_STORE_NAME);
-      service.findParticipantIdsByAddresses(participantStore, filter.numbers,
-                                            false, true,
-                                            (function (participantIds) {
+      mmdb.findParticipantIdsByAddresses(participantStore, filter.numbers,
+                                         false, true,
+                                         (function (participantIds) {
         if (!participantIds || !participantIds.length) {
           // Oops! No such participant at all.
 
           collect(txn, COLLECT_ID_END, COLLECT_TIMESTAMP_UNUSED);
           return;
         }
 
         if (participantIds.length == 1) {
@@ -3022,42 +3044,42 @@ UnionResultsCollector.prototype = {
   },
 
   newContext: function newContext() {
     this.contexts[1].processing++;
     return this.collect.bind(this, 1);
   }
 };
 
-function GetMessagesCursor(service, callback) {
-  this.service = service;
+function GetMessagesCursor(mmdb, callback) {
+  this.mmdb = mmdb;
   this.callback = callback;
   this.collector = new ResultsCollector();
 
   this.handleContinue(); // Trigger first run.
 }
 GetMessagesCursor.prototype = {
   classID: RIL_GETMESSAGESCURSOR_CID,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsICursorContinueCallback]),
 
-  service: null,
+  mmdb: null,
   callback: null,
   collector: null,
 
   getMessageTxn: function getMessageTxn(messageStore, messageId) {
     if (DEBUG) debug ("Fetching message " + messageId);
 
     let getRequest = messageStore.get(messageId);
     let self = this;
     getRequest.onsuccess = function onsuccess(event) {
       if (DEBUG) {
         debug("notifyNextMessageInListGot - messageId: " + messageId);
       }
       let domMessage =
-        self.service.createDomMessageFromRecord(event.target.result);
+        self.mmdb.createDomMessageFromRecord(event.target.result);
       self.callback.notifyCursorResult(domMessage);
     };
     getRequest.onerror = function onerror(event) {
       if (DEBUG) {
         debug("notifyCursorError - messageId: " + messageId);
       }
       self.callback.notifyCursorError(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
     };
@@ -3079,45 +3101,45 @@ GetMessagesCursor.prototype = {
     if (txn) {
       let messageStore = txn.objectStore(MESSAGE_STORE_NAME);
       this.getMessageTxn(messageStore, messageId);
       return;
     }
 
     // Or, we have to open another transaction ourselves.
     let self = this;
-    this.service.newTxn(READ_ONLY, function (error, txn, messageStore) {
+    this.mmdb.newTxn(READ_ONLY, function (error, txn, messageStore) {
       if (error) {
         self.callback.notifyCursorError(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
         return;
       }
       self.getMessageTxn(messageStore, messageId);
     }, [MESSAGE_STORE_NAME]);
   },
 
   // nsICursorContinueCallback
 
   handleContinue: function handleContinue() {
     if (DEBUG) debug("Getting next message in list");
     this.collector.squeeze(this.notify.bind(this));
   }
 };
 
-function GetThreadsCursor(service, callback) {
-  this.service = service;
+function GetThreadsCursor(mmdb, callback) {
+  this.mmdb = mmdb;
   this.callback = callback;
   this.collector = new ResultsCollector();
 
   this.handleContinue(); // Trigger first run.
 }
 GetThreadsCursor.prototype = {
   classID: RIL_GETTHREADSCURSOR_CID,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsICursorContinueCallback]),
 
-  service: null,
+  mmdb: null,
   callback: null,
   collector: null,
 
   getThreadTxn: function getThreadTxn(threadStore, threadId) {
     if (DEBUG) debug ("Fetching thread " + threadId);
 
     let getRequest = threadStore.get(threadId);
     let self = this;
@@ -3160,30 +3182,32 @@ GetThreadsCursor.prototype = {
     if (txn) {
       let threadStore = txn.objectStore(THREAD_STORE_NAME);
       this.getThreadTxn(threadStore, threadId);
       return;
     }
 
     // Or, we have to open another transaction ourselves.
     let self = this;
-    this.service.newTxn(READ_ONLY, function (error, txn, threadStore) {
+    this.mmdb.newTxn(READ_ONLY, function (error, txn, threadStore) {
       if (error) {
         self.callback.notifyCursorError(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
         return;
       }
       self.getThreadTxn(threadStore, threadId);
     }, [THREAD_STORE_NAME]);
   },
 
   // nsICursorContinueCallback
 
   handleContinue: function handleContinue() {
     if (DEBUG) debug("Getting next thread in list");
     this.collector.squeeze(this.notify.bind(this));
   }
 }
 
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MobileMessageDatabaseService]);
+this.EXPORTED_SYMBOLS = [
+  'MobileMessageDB'
+];
 
 function debug() {
-  dump("MobileMessageDatabaseService: " + Array.slice(arguments).join(" ") + "\n");
+  dump("MobileMessageDB: " + Array.slice(arguments).join(" ") + "\n");
 }
--- a/dom/mobilemessage/src/gonk/MobileMessageDatabaseService.js
+++ b/dom/mobilemessage/src/gonk/MobileMessageDatabaseService.js
@@ -3,3187 +3,114 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
-Cu.importGlobalProperties(["indexedDB"]);
 
-var RIL = {};
-Cu.import("resource://gre/modules/ril_consts.js", RIL);
+let MMDB = {};
+Cu.import("resource://gre/modules/MobileMessageDB.jsm", MMDB);
 
 const RIL_MOBILEMESSAGEDATABASESERVICE_CONTRACTID =
   "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1";
 const RIL_MOBILEMESSAGEDATABASESERVICE_CID =
   Components.ID("{29785f90-6b5b-11e2-9201-3b280170b2ec}");
-const RIL_GETMESSAGESCURSOR_CID =
-  Components.ID("{484d1ad8-840e-4782-9dc4-9ebc4d914937}");
-const RIL_GETTHREADSCURSOR_CID =
-  Components.ID("{95ee7c3e-d6f2-4ec4-ade5-0c453c036d35}");
-
-const DEBUG = false;
-const DISABLE_MMS_GROUPING_FOR_RECEIVING = true;
-
 
 const DB_NAME = "sms";
-const DB_VERSION = 20;
-const MESSAGE_STORE_NAME = "sms";
-const THREAD_STORE_NAME = "thread";
-const PARTICIPANT_STORE_NAME = "participant";
-const MOST_RECENT_STORE_NAME = "most-recent";
-
-const DELIVERY_SENDING = "sending";
-const DELIVERY_SENT = "sent";
-const DELIVERY_RECEIVED = "received";
-const DELIVERY_NOT_DOWNLOADED = "not-downloaded";
-const DELIVERY_ERROR = "error";
-
-const DELIVERY_STATUS_NOT_APPLICABLE = "not-applicable";
-const DELIVERY_STATUS_SUCCESS = "success";
-const DELIVERY_STATUS_PENDING = "pending";
-const DELIVERY_STATUS_ERROR = "error";
-
-const MESSAGE_CLASS_NORMAL = "normal";
-
-const FILTER_TIMESTAMP = "timestamp";
-const FILTER_NUMBERS = "numbers";
-const FILTER_DELIVERY = "delivery";
-const FILTER_READ = "read";
-
-// We canĀ“t create an IDBKeyCursor with a boolean, so we need to use numbers
-// instead.
-const FILTER_READ_UNREAD = 0;
-const FILTER_READ_READ = 1;
-
-const READ_ONLY = "readonly";
-const READ_WRITE = "readwrite";
-const PREV = "prev";
-const NEXT = "next";
-
-const COLLECT_ID_END = 0;
-const COLLECT_ID_ERROR = -1;
-const COLLECT_TIMESTAMP_UNUSED = 0;
-
-XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageService",
-                                   "@mozilla.org/mobilemessage/mobilemessageservice;1",
-                                   "nsIMobileMessageService");
-
-XPCOMUtils.defineLazyServiceGetter(this, "gMMSService",
-                                   "@mozilla.org/mms/rilmmsservice;1",
-                                   "nsIMmsService");
-
-XPCOMUtils.defineLazyGetter(this, "MMS", function () {
-  let MMS = {};
-  Cu.import("resource://gre/modules/MmsPduHelper.jsm", MMS);
-  return MMS;
-});
 
 /**
  * MobileMessageDatabaseService
  */
 function MobileMessageDatabaseService() {
   // Prime the directory service's cache to ensure that the ProfD entry exists
   // by the time IndexedDB queries for it off the main thread. (See bug 743635.)
   Services.dirsvc.get("ProfD", Ci.nsIFile);
 
-  let that = this;
-  this.newTxn(READ_ONLY, function(error, txn, messageStore){
-    if (error) {
-      return;
-    }
-    // In order to get the highest key value, we open a key cursor in reverse
-    // order and get only the first pointed value.
-    let request = messageStore.openCursor(null, PREV);
-    request.onsuccess = function onsuccess(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        if (DEBUG) {
-          debug("Could not get the last key from mobile message database. " +
-                "Probably empty database");
-        }
-        return;
-      }
-      that.lastMessageId = cursor.key || 0;
-      if (DEBUG) debug("Last assigned message ID was " + that.lastMessageId);
-    };
-    request.onerror = function onerror(event) {
-      if (DEBUG) {
-        debug("Could not get the last key from mobile message database " +
-              event.target.errorCode);
-      }
-    };
-  });
-  this.updatePendingTransactionToError();
+  let mmdb = new MMDB.MobileMessageDB();
+  mmdb.init(DB_NAME, 0, mmdb.updatePendingTransactionToError.bind(mmdb));
+  this.mmdb = mmdb;
 }
 MobileMessageDatabaseService.prototype = {
 
   classID: RIL_MOBILEMESSAGEDATABASESERVICE_CID,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIRilMobileMessageDatabaseService,
                                          Ci.nsIMobileMessageDatabaseService,
                                          Ci.nsIObserver]),
 
   /**
-   * Cache the DB here.
+   * MobileMessageDB instance.
    */
-  db: null,
-
-  /**
-   * Last sms/mms object store key value in the database.
-   */
-  lastMessageId: 0,
+  mmdb: null,
 
   /**
    * nsIObserver
    */
   observe: function observe() {},
 
   /**
-   * Prepare the database. This may include opening the database and upgrading
-   * it to the latest schema version.
-   *
-   * @param callback
-   *        Function that takes an error and db argument. It is called when
-   *        the database is ready to use or if an error occurs while preparing
-   *        the database.
-   *
-   * @return (via callback) a database ready for use.
-   */
-  ensureDB: function ensureDB(callback) {
-    if (this.db) {
-      if (DEBUG) debug("ensureDB: already have a database, returning early.");
-      callback(null, this.db);
-      return;
-    }
-
-    let self = this;
-    function gotDB(db) {
-      self.db = db;
-      callback(null, db);
-    }
-
-    let request = indexedDB.open(DB_NAME, DB_VERSION);
-    request.onsuccess = function (event) {
-      if (DEBUG) debug("Opened database:", DB_NAME, DB_VERSION);
-      gotDB(event.target.result);
-    };
-    request.onupgradeneeded = function (event) {
-      if (DEBUG) {
-        debug("Database needs upgrade:", DB_NAME,
-              event.oldVersion, event.newVersion);
-        debug("Correct new database version:", event.newVersion == DB_VERSION);
-      }
-
-      let db = event.target.result;
-
-      let currentVersion = event.oldVersion;
-
-      function update(currentVersion) {
-        let next = update.bind(self, currentVersion + 1);
-
-        switch (currentVersion) {
-          case 0:
-            if (DEBUG) debug("New database");
-            self.createSchema(db, next);
-            break;
-          case 1:
-            if (DEBUG) debug("Upgrade to version 2. Including `read` index");
-            self.upgradeSchema(event.target.transaction, next);
-            break;
-          case 2:
-            if (DEBUG) debug("Upgrade to version 3. Fix existing entries.");
-            self.upgradeSchema2(event.target.transaction, next);
-            break;
-          case 3:
-            if (DEBUG) debug("Upgrade to version 4. Add quick threads view.");
-            self.upgradeSchema3(db, event.target.transaction, next);
-            break;
-          case 4:
-            if (DEBUG) debug("Upgrade to version 5. Populate quick threads view.");
-            self.upgradeSchema4(event.target.transaction, next);
-            break;
-          case 5:
-            if (DEBUG) debug("Upgrade to version 6. Use PhonenumberJS.");
-            self.upgradeSchema5(event.target.transaction, next);
-            break;
-          case 6:
-            if (DEBUG) debug("Upgrade to version 7. Use multiple entry indexes.");
-            self.upgradeSchema6(event.target.transaction, next);
-            break;
-          case 7:
-            if (DEBUG) debug("Upgrade to version 8. Add participant/thread stores.");
-            self.upgradeSchema7(db, event.target.transaction, next);
-            break;
-          case 8:
-            if (DEBUG) debug("Upgrade to version 9. Add transactionId index for incoming MMS.");
-            self.upgradeSchema8(event.target.transaction, next);
-            break;
-          case 9:
-            if (DEBUG) debug("Upgrade to version 10. Upgrade type if it's not existing.");
-            self.upgradeSchema9(event.target.transaction, next);
-            break;
-          case 10:
-            if (DEBUG) debug("Upgrade to version 11. Add last message type into threadRecord.");
-            self.upgradeSchema10(event.target.transaction, next);
-            break;
-          case 11:
-            if (DEBUG) debug("Upgrade to version 12. Add envelopeId index for outgoing MMS.");
-            self.upgradeSchema11(event.target.transaction, next);
-            break;
-          case 12:
-            if (DEBUG) debug("Upgrade to version 13. Replaced deliveryStatus by deliveryInfo.");
-            self.upgradeSchema12(event.target.transaction, next);
-            break;
-          case 13:
-            if (DEBUG) debug("Upgrade to version 14. Fix the wrong participants.");
-            self.upgradeSchema13(event.target.transaction, next);
-            break;
-          case 14:
-            if (DEBUG) debug("Upgrade to version 15. Add deliveryTimestamp.");
-            self.upgradeSchema14(event.target.transaction, next);
-            break;
-          case 15:
-            if (DEBUG) debug("Upgrade to version 16. Add ICC ID for each message.");
-            self.upgradeSchema15(event.target.transaction, next);
-            break;
-          case 16:
-            if (DEBUG) debug("Upgrade to version 17. Add isReadReportSent for incoming MMS.");
-            self.upgradeSchema16(event.target.transaction, next);
-            break;
-          case 17:
-            if (DEBUG) debug("Upgrade to version 18. Add last message subject into threadRecord.");
-            self.upgradeSchema17(event.target.transaction, next);
-            break;
-          case 18:
-            if (DEBUG) debug("Upgrade to version 19. Add pid for incoming SMS.");
-            self.upgradeSchema18(event.target.transaction, next);
-            break;
-          case 19:
-            if (DEBUG) debug("Upgrade to version 20. Add readStatus and readTimestamp.");
-            self.upgradeSchema19(event.target.transaction, next);
-            break;
-          case 20:
-            // This will need to be moved for each new version
-            if (DEBUG) debug("Upgrade finished.");
-            break;
-          default:
-            event.target.transaction.abort();
-            callback("Old database version: " + event.oldVersion, null);
-            break;
-        }
-      }
-
-      update(currentVersion);
-    };
-    request.onerror = function (event) {
-      //TODO look at event.target.Code and change error constant accordingly
-      callback("Error opening database!", null);
-    };
-    request.onblocked = function (event) {
-      callback("Opening database request is blocked.", null);
-    };
-  },
-
-  /**
-   * Start a new transaction.
-   *
-   * @param txn_type
-   *        Type of transaction (e.g. READ_WRITE)
-   * @param callback
-   *        Function to call when the transaction is available. It will
-   *        be invoked with the transaction and opened object stores.
-   * @param storeNames
-   *        Names of the stores to open.
-   */
-  newTxn: function newTxn(txn_type, callback, storeNames) {
-    if (!storeNames) {
-      storeNames = [MESSAGE_STORE_NAME];
-    }
-    if (DEBUG) debug("Opening transaction for object stores: " + storeNames);
-    this.ensureDB(function (error, db) {
-      if (error) {
-        if (DEBUG) debug("Could not open database: " + error);
-        callback(error);
-        return;
-      }
-      let txn = db.transaction(storeNames, txn_type);
-      if (DEBUG) debug("Started transaction " + txn + " of type " + txn_type);
-      if (DEBUG) {
-        txn.oncomplete = function oncomplete(event) {
-          debug("Transaction " + txn + " completed.");
-        };
-        txn.onerror = function onerror(event) {
-          //TODO check event.target.errorCode and show an appropiate error
-          //     message according to it.
-          debug("Error occurred during transaction: " + event.target.errorCode);
-        };
-      }
-      let stores;
-      if (storeNames.length == 1) {
-        if (DEBUG) debug("Retrieving object store " + storeNames[0]);
-        stores = txn.objectStore(storeNames[0]);
-      } else {
-        stores = [];
-        for each (let storeName in storeNames) {
-          if (DEBUG) debug("Retrieving object store " + storeName);
-          stores.push(txn.objectStore(storeName));
-        }
-      }
-      callback(null, txn, stores);
-    });
-  },
-
-  /**
-   * Sometimes user might reboot or remove battery while sending/receiving
-   * message. This is function set the status of message records to error.
-   */
-  updatePendingTransactionToError: function updatePendingTransactionToError() {
-    this.newTxn(READ_WRITE, function (error, txn, messageStore) {
-      if (DEBUG) {
-        txn.onerror = function onerror(event) {
-          debug("updatePendingTransactionToError fail, event = " + event);
-        };
-      }
-
-      let deliveryIndex = messageStore.index("delivery");
-
-      // Set all 'delivery: sending' records to 'delivery: error' and 'deliveryStatus:
-      // error'.
-      let keyRange = IDBKeyRange.bound([DELIVERY_SENDING, 0], [DELIVERY_SENDING, ""]);
-      let cursorRequestSending = deliveryIndex.openCursor(keyRange);
-      cursorRequestSending.onsuccess = function(event) {
-        let messageCursor = event.target.result;
-        if (!messageCursor) {
-          return;
-        }
-
-        let messageRecord = messageCursor.value;
-
-        // Set delivery to error.
-        messageRecord.delivery = DELIVERY_ERROR;
-        messageRecord.deliveryIndex = [DELIVERY_ERROR, messageRecord.timestamp];
-
-        if (messageRecord.type == "sms") {
-          messageRecord.deliveryStatus = DELIVERY_STATUS_ERROR;
-        } else {
-          // Set delivery status to error.
-          for (let i = 0; i < messageRecord.deliveryInfo.length; i++) {
-            messageRecord.deliveryInfo[i].deliveryStatus = DELIVERY_STATUS_ERROR;
-          }
-        }
-
-        messageCursor.update(messageRecord);
-        messageCursor.continue();
-      };
-
-      // Set all 'delivery: not-downloaded' and 'deliveryStatus: pending'
-      // records to 'delivery: not-downloaded' and 'deliveryStatus: error'.
-      keyRange = IDBKeyRange.bound([DELIVERY_NOT_DOWNLOADED, 0], [DELIVERY_NOT_DOWNLOADED, ""]);
-      let cursorRequestNotDownloaded = deliveryIndex.openCursor(keyRange);
-      cursorRequestNotDownloaded.onsuccess = function(event) {
-        let messageCursor = event.target.result;
-        if (!messageCursor) {
-          return;
-        }
-
-        let messageRecord = messageCursor.value;
-
-        // We have no "not-downloaded" SMS messages.
-        if (messageRecord.type == "sms") {
-          messageCursor.continue();
-          return;
-        }
-
-        // Set delivery status to error.
-        let deliveryInfo = messageRecord.deliveryInfo;
-        if (deliveryInfo.length == 1 &&
-            deliveryInfo[0].deliveryStatus == DELIVERY_STATUS_PENDING) {
-          deliveryInfo[0].deliveryStatus = DELIVERY_STATUS_ERROR;
-        }
-
-        messageCursor.update(messageRecord);
-        messageCursor.continue();
-      };
-    });
-  },
-
-  /**
-   * Create the initial database schema.
-   *
-   * TODO need to worry about number normalization somewhere...
-   * TODO full text search on body???
-   */
-  createSchema: function createSchema(db, next) {
-    // This messageStore holds the main mobile message data.
-    let messageStore = db.createObjectStore(MESSAGE_STORE_NAME, { keyPath: "id" });
-    messageStore.createIndex("timestamp", "timestamp", { unique: false });
-    if (DEBUG) debug("Created object stores and indexes");
-    next();
-  },
-
-  /**
-   * Upgrade to the corresponding database schema version.
-   */
-  upgradeSchema: function upgradeSchema(transaction, next) {
-    let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-    messageStore.createIndex("read", "read", { unique: false });
-    next();
-  },
-
-  upgradeSchema2: function upgradeSchema2(transaction, next) {
-    let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-    messageStore.openCursor().onsuccess = function(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        next();
-        return;
-      }
-
-      let messageRecord = cursor.value;
-      messageRecord.messageClass = MESSAGE_CLASS_NORMAL;
-      messageRecord.deliveryStatus = DELIVERY_STATUS_NOT_APPLICABLE;
-      cursor.update(messageRecord);
-      cursor.continue();
-    };
-  },
-
-  upgradeSchema3: function upgradeSchema3(db, transaction, next) {
-    // Delete redundant "id" index.
-    let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-    if (messageStore.indexNames.contains("id")) {
-      messageStore.deleteIndex("id");
-    }
-
-    /**
-     * This mostRecentStore can be used to quickly construct a thread view of
-     * the mobile message database. Each entry looks like this:
-     *
-     * { senderOrReceiver: <String> (primary key),
-     *   id: <Number>,
-     *   timestamp: <Date>,
-     *   body: <String>,
-     *   unreadCount: <Number> }
-     *
-     */
-    let mostRecentStore = db.createObjectStore(MOST_RECENT_STORE_NAME,
-                                               { keyPath: "senderOrReceiver" });
-    mostRecentStore.createIndex("timestamp", "timestamp");
-    next();
-  },
-
-  upgradeSchema4: function upgradeSchema4(transaction, next) {
-    let threads = {};
-    let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-    let mostRecentStore = transaction.objectStore(MOST_RECENT_STORE_NAME);
-
-    messageStore.openCursor().onsuccess = function(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        for (let thread in threads) {
-          mostRecentStore.put(threads[thread]);
-        }
-        next();
-        return;
-      }
-
-      let messageRecord = cursor.value;
-      let contact = messageRecord.sender || messageRecord.receiver;
-
-      if (contact in threads) {
-        let thread = threads[contact];
-        if (!messageRecord.read) {
-          thread.unreadCount++;
-        }
-        if (messageRecord.timestamp > thread.timestamp) {
-          thread.id = messageRecord.id;
-          thread.body = messageRecord.body;
-          thread.timestamp = messageRecord.timestamp;
-        }
-      } else {
-        threads[contact] = {
-          senderOrReceiver: contact,
-          id: messageRecord.id,
-          timestamp: messageRecord.timestamp,
-          body: messageRecord.body,
-          unreadCount: messageRecord.read ? 0 : 1
-        };
-      }
-      cursor.continue();
-    };
-  },
-
-  upgradeSchema5: function upgradeSchema5(transaction, next) {
-    // Don't perform any upgrade. See Bug 819560.
-    next();
-  },
-
-  upgradeSchema6: function upgradeSchema6(transaction, next) {
-    let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-
-    // Delete "delivery" index.
-    if (messageStore.indexNames.contains("delivery")) {
-      messageStore.deleteIndex("delivery");
-    }
-    // Delete "sender" index.
-    if (messageStore.indexNames.contains("sender")) {
-      messageStore.deleteIndex("sender");
-    }
-    // Delete "receiver" index.
-    if (messageStore.indexNames.contains("receiver")) {
-      messageStore.deleteIndex("receiver");
-    }
-    // Delete "read" index.
-    if (messageStore.indexNames.contains("read")) {
-      messageStore.deleteIndex("read");
-    }
-
-    // Create new "delivery", "number" and "read" indexes.
-    messageStore.createIndex("delivery", "deliveryIndex");
-    messageStore.createIndex("number", "numberIndex", { multiEntry: true });
-    messageStore.createIndex("read", "readIndex");
-
-    // Populate new "deliverIndex", "numberIndex" and "readIndex" attributes.
-    messageStore.openCursor().onsuccess = function(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        next();
-        return;
-      }
-
-      let messageRecord = cursor.value;
-      let timestamp = messageRecord.timestamp;
-      messageRecord.deliveryIndex = [messageRecord.delivery, timestamp];
-      messageRecord.numberIndex = [
-        [messageRecord.sender, timestamp],
-        [messageRecord.receiver, timestamp]
-      ];
-      messageRecord.readIndex = [messageRecord.read, timestamp];
-      cursor.update(messageRecord);
-      cursor.continue();
-    };
-  },
-
-  /**
-   * Add participant/thread stores.
-   *
-   * The message store now saves original phone numbers/addresses input from
-   * content to message records. No normalization is made.
-   *
-   * For filtering messages by phone numbers, it first looks up corresponding
-   * participant IDs from participant table and fetch message records with
-   * matching keys defined in per record "participantIds" field.
-   *
-   * For message threading, messages with the same participant ID array are put
-   * in the same thread. So updating "unreadCount", "lastMessageId" and
-   * "lastTimestamp" are through the "threadId" carried by per message record.
-   * Fetching threads list is now simply walking through the thread sotre. The
-   * "mostRecentStore" is dropped.
-   */
-  upgradeSchema7: function upgradeSchema7(db, transaction, next) {
-    /**
-     * This "participant" object store keeps mappings of multiple phone numbers
-     * of the same recipient to an integer participant id. Each entry looks
-     * like:
-     *
-     * { id: <Number> (primary key),
-     *   addresses: <Array of strings> }
-     */
-    let participantStore = db.createObjectStore(PARTICIPANT_STORE_NAME,
-                                                { keyPath: "id",
-                                                  autoIncrement: true });
-    participantStore.createIndex("addresses", "addresses", { multiEntry: true });
-
-    /**
-     * This "threads" object store keeps mappings from an integer thread id to
-     * ids of the participants of that message thread. Each entry looks like:
-     *
-     * { id: <Number> (primary key),
-     *   participantIds: <Array of participant IDs>,
-     *   participantAddresses: <Array of the first addresses of the participants>,
-     *   lastMessageId: <Number>,
-     *   lastTimestamp: <Date>,
-     *   subject: <String>,
-     *   unreadCount: <Number> }
-     *
-     */
-    let threadStore = db.createObjectStore(THREAD_STORE_NAME,
-                                           { keyPath: "id",
-                                             autoIncrement: true });
-    threadStore.createIndex("participantIds", "participantIds");
-    threadStore.createIndex("lastTimestamp", "lastTimestamp");
-
-    /**
-     * Replace "numberIndex" with "participantIdsIndex" and create an additional
-     * "threadId". "numberIndex" will be removed later.
-     */
-    let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-    messageStore.createIndex("threadId", "threadIdIndex");
-    messageStore.createIndex("participantIds", "participantIdsIndex",
-                             { multiEntry: true });
-
-    // Now populate participantStore & threadStore.
-    let mostRecentStore = transaction.objectStore(MOST_RECENT_STORE_NAME);
-    let self = this;
-    let mostRecentRequest = mostRecentStore.openCursor();
-    mostRecentRequest.onsuccess = function(event) {
-      let mostRecentCursor = event.target.result;
-      if (!mostRecentCursor) {
-        db.deleteObjectStore(MOST_RECENT_STORE_NAME);
-
-        // No longer need the "number" index in messageStore, use
-        // "participantIds" index instead.
-        messageStore.deleteIndex("number");
-        next();
-        return;
-      }
-
-      let mostRecentRecord = mostRecentCursor.value;
-
-      // Each entry in mostRecentStore is supposed to be a unique thread, so we
-      // retrieve the records out and insert its "senderOrReceiver" column as a
-      // new record in participantStore.
-      let number = mostRecentRecord.senderOrReceiver;
-      self.findParticipantRecordByAddress(participantStore, number, true,
-                                          function (participantRecord) {
-        // Also create a new record in threadStore.
-        let threadRecord = {
-          participantIds: [participantRecord.id],
-          participantAddresses: [number],
-          lastMessageId: mostRecentRecord.id,
-          lastTimestamp: mostRecentRecord.timestamp,
-          subject: mostRecentRecord.body,
-          unreadCount: mostRecentRecord.unreadCount,
-        };
-        let addThreadRequest = threadStore.add(threadRecord);
-        addThreadRequest.onsuccess = function (event) {
-          threadRecord.id = event.target.result;
-
-          let numberRange = IDBKeyRange.bound([number, 0], [number, ""]);
-          let messageRequest = messageStore.index("number")
-                                           .openCursor(numberRange, NEXT);
-          messageRequest.onsuccess = function (event) {
-            let messageCursor = event.target.result;
-            if (!messageCursor) {
-              // No more message records, check next most recent record.
-              mostRecentCursor.continue();
-              return;
-            }
-
-            let messageRecord = messageCursor.value;
-            // Check whether the message really belongs to this thread.
-            let matchSenderOrReceiver = false;
-            if (messageRecord.delivery == DELIVERY_RECEIVED) {
-              if (messageRecord.sender == number) {
-                matchSenderOrReceiver = true;
-              }
-            } else if (messageRecord.receiver == number) {
-              matchSenderOrReceiver = true;
-            }
-            if (!matchSenderOrReceiver) {
-              // Check next message record.
-              messageCursor.continue();
-              return;
-            }
-
-            messageRecord.threadId = threadRecord.id;
-            messageRecord.threadIdIndex = [threadRecord.id,
-                                           messageRecord.timestamp];
-            messageRecord.participantIdsIndex = [
-              [participantRecord.id, messageRecord.timestamp]
-            ];
-            messageCursor.update(messageRecord);
-            // Check next message record.
-            messageCursor.continue();
-          };
-          messageRequest.onerror = function () {
-            // Error in fetching message records, check next most recent record.
-            mostRecentCursor.continue();
-          };
-        };
-        addThreadRequest.onerror = function () {
-          // Error in fetching message records, check next most recent record.
-          mostRecentCursor.continue();
-        };
-      });
-    };
-  },
-
-  /**
-   * Add transactionId index for MMS.
-   */
-  upgradeSchema8: function upgradeSchema8(transaction, next) {
-    let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-
-    // Delete "transactionId" index.
-    if (messageStore.indexNames.contains("transactionId")) {
-      messageStore.deleteIndex("transactionId");
-    }
-
-    // Create new "transactionId" indexes.
-    messageStore.createIndex("transactionId", "transactionIdIndex", { unique: true });
-
-    // Populate new "transactionIdIndex" attributes.
-    messageStore.openCursor().onsuccess = function(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        next();
-        return;
-      }
-
-      let messageRecord = cursor.value;
-      if ("mms" == messageRecord.type &&
-          (DELIVERY_NOT_DOWNLOADED == messageRecord.delivery ||
-           DELIVERY_RECEIVED == messageRecord.delivery)) {
-        messageRecord.transactionIdIndex =
-          messageRecord.headers["x-mms-transaction-id"];
-        cursor.update(messageRecord);
-      }
-      cursor.continue();
-    };
-  },
-
-  upgradeSchema9: function upgradeSchema9(transaction, next) {
-    let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-
-    // Update type attributes.
-    messageStore.openCursor().onsuccess = function(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        next();
-        return;
-      }
-
-      let messageRecord = cursor.value;
-      if (messageRecord.type == undefined) {
-        messageRecord.type = "sms";
-        cursor.update(messageRecord);
-      }
-      cursor.continue();
-    };
-  },
-
-  upgradeSchema10: function upgradeSchema10(transaction, next) {
-    let threadStore = transaction.objectStore(THREAD_STORE_NAME);
-
-    // Add 'lastMessageType' to each thread record.
-    threadStore.openCursor().onsuccess = function(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        next();
-        return;
-      }
-
-      let threadRecord = cursor.value;
-      let lastMessageId = threadRecord.lastMessageId;
-      let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-      let request = messageStore.mozGetAll(lastMessageId);
-
-      request.onsuccess = function onsuccess() {
-        let messageRecord = request.result[0];
-        if (!messageRecord) {
-          if (DEBUG) debug("Message ID " + lastMessageId + " not found");
-          return;
-        }
-        if (messageRecord.id != lastMessageId) {
-          if (DEBUG) {
-            debug("Requested message ID (" + lastMessageId + ") is different from" +
-                  " the one we got");
-          }
-          return;
-        }
-        threadRecord.lastMessageType = messageRecord.type;
-        cursor.update(threadRecord);
-        cursor.continue();
-      };
-
-      request.onerror = function onerror(event) {
-        if (DEBUG) {
-          if (event.target) {
-            debug("Caught error on transaction", event.target.errorCode);
-          }
-        }
-        cursor.continue();
-      };
-    };
-  },
-
-  /**
-   * Add envelopeId index for MMS.
-   */
-  upgradeSchema11: function upgradeSchema11(transaction, next) {
-    let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-
-    // Delete "envelopeId" index.
-    if (messageStore.indexNames.contains("envelopeId")) {
-      messageStore.deleteIndex("envelopeId");
-    }
-
-    // Create new "envelopeId" indexes.
-    messageStore.createIndex("envelopeId", "envelopeIdIndex", { unique: true });
-
-    // Populate new "envelopeIdIndex" attributes.
-    messageStore.openCursor().onsuccess = function(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        next();
-        return;
-      }
-
-      let messageRecord = cursor.value;
-      if (messageRecord.type == "mms" &&
-          messageRecord.delivery == DELIVERY_SENT) {
-        messageRecord.envelopeIdIndex = messageRecord.headers["message-id"];
-        cursor.update(messageRecord);
-      }
-      cursor.continue();
-    };
-  },
-
-  /**
-   * Replace deliveryStatus by deliveryInfo.
-   */
-  upgradeSchema12: function upgradeSchema12(transaction, next) {
-    let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-
-    messageStore.openCursor().onsuccess = function(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        next();
-        return;
-      }
-
-      let messageRecord = cursor.value;
-      if (messageRecord.type == "mms") {
-        messageRecord.deliveryInfo = [];
-
-        if (messageRecord.deliveryStatus.length == 1 &&
-            (messageRecord.delivery == DELIVERY_NOT_DOWNLOADED ||
-             messageRecord.delivery == DELIVERY_RECEIVED)) {
-          messageRecord.deliveryInfo.push({
-            receiver: null,
-            deliveryStatus: messageRecord.deliveryStatus[0] });
-        } else {
-          for (let i = 0; i < messageRecord.deliveryStatus.length; i++) {
-            messageRecord.deliveryInfo.push({
-              receiver: messageRecord.receivers[i],
-              deliveryStatus: messageRecord.deliveryStatus[i] });
-          }
-        }
-        delete messageRecord.deliveryStatus;
-        cursor.update(messageRecord);
-      }
-      cursor.continue();
-    };
-  },
-
-  /**
-   * Fix the wrong participants.
-   */
-  upgradeSchema13: function upgradeSchema13(transaction, next) {
-    let participantStore = transaction.objectStore(PARTICIPANT_STORE_NAME);
-    let threadStore = transaction.objectStore(THREAD_STORE_NAME);
-    let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-    let self = this;
-
-    let isInvalid = function (participantRecord) {
-      let entries = [];
-      for (let addr of participantRecord.addresses) {
-        entries.push({
-          normalized: addr,
-          parsed: PhoneNumberUtils.parseWithMCC(addr, null)
-        })
-      }
-      for (let ix = 0 ; ix < entries.length - 1; ix++) {
-        let entry1 = entries[ix];
-        for (let iy = ix + 1 ; iy < entries.length; iy ++) {
-          let entry2 = entries[iy];
-          if (!self.matchPhoneNumbers(entry1.normalized, entry1.parsed,
-                                      entry2.normalized, entry2.parsed)) {
-            return true;
-          }
-        }
-      }
-      return false;
-    };
-
-    let invalidParticipantIds = [];
-    participantStore.openCursor().onsuccess = function(event) {
-      let cursor = event.target.result;
-      if (cursor) {
-        let participantRecord = cursor.value;
-        // Check if this participant record is valid
-        if (isInvalid(participantRecord)) {
-          invalidParticipantIds.push(participantRecord.id);
-          cursor.delete();
-        }
-        cursor.continue();
-        return;
-      }
-
-      // Participant store cursor iteration done.
-      if (!invalidParticipantIds.length) {
-        next();
-        return;
-      }
-
-      // Find affected thread.
-      let wrongThreads = [];
-      threadStore.openCursor().onsuccess = function(event) {
-        let threadCursor = event.target.result;
-        if (threadCursor) {
-          let threadRecord = threadCursor.value;
-          let participantIds = threadRecord.participantIds;
-          let foundInvalid = false;
-          for (let invalidParticipantId of invalidParticipantIds) {
-            if (participantIds.indexOf(invalidParticipantId) != -1) {
-              foundInvalid = true;
-              break;
-            }
-          }
-          if (foundInvalid) {
-            wrongThreads.push(threadRecord.id);
-            threadCursor.delete();
-          }
-          threadCursor.continue();
-          return;
-        }
-
-        if (!wrongThreads.length) {
-          next();
-          return;
-        }
-        // Use recursive function to avoid we add participant twice.
-        (function createUpdateThreadAndParticipant(ix) {
-          let threadId = wrongThreads[ix];
-          let range = IDBKeyRange.bound([threadId, 0], [threadId, ""]);
-          messageStore.index("threadId").openCursor(range).onsuccess = function(event) {
-            let messageCursor = event.target.result;
-            if (!messageCursor) {
-              ix++;
-              if (ix === wrongThreads.length) {
-                next();
-                return;
-              }
-              createUpdateThreadAndParticipant(ix);
-              return;
-            }
-
-            let messageRecord = messageCursor.value;
-            let timestamp = messageRecord.timestamp;
-            let threadParticipants = [];
-            // Recaculate the thread participants of received message.
-            if (messageRecord.delivery === DELIVERY_RECEIVED ||
-                messageRecord.delivery === DELIVERY_NOT_DOWNLOADED) {
-              threadParticipants.push(messageRecord.sender);
-              if (messageRecord.type == "mms") {
-                this.fillReceivedMmsThreadParticipants(messageRecord, threadParticipants);
-              }
-            }
-            // Recaculate the thread participants of sent messages and error
-            // messages. In error sms messages, we don't have error received sms.
-            // In received MMS, we don't update the error to deliver field but
-            // deliverStatus. So we only consider sent message in DELIVERY_ERROR.
-            else if (messageRecord.delivery === DELIVERY_SENT ||
-                messageRecord.delivery === DELIVERY_ERROR) {
-              if (messageRecord.type == "sms") {
-                threadParticipants = [messageRecord.receiver];
-              } else if (messageRecord.type == "mms") {
-                threadParticipants = messageRecord.receivers;
-              }
-            }
-            self.findThreadRecordByParticipants(threadStore, participantStore,
-                                                threadParticipants, true,
-                                                function (threadRecord,
-                                                          participantIds) {
-              if (!participantIds) {
-                debug("participantIds is empty!");
-                return;
-              }
-
-              let timestamp = messageRecord.timestamp;
-              // Setup participantIdsIndex.
-              messageRecord.participantIdsIndex = [];
-              for each (let id in participantIds) {
-                messageRecord.participantIdsIndex.push([id, timestamp]);
-              }
-              if (threadRecord) {
-                let needsUpdate = false;
-
-                if (threadRecord.lastTimestamp <= timestamp) {
-                  threadRecord.lastTimestamp = timestamp;
-                  threadRecord.subject = messageRecord.body;
-                  threadRecord.lastMessageId = messageRecord.id;
-                  threadRecord.lastMessageType = messageRecord.type;
-                  needsUpdate = true;
-                }
-
-                if (!messageRecord.read) {
-                  threadRecord.unreadCount++;
-                  needsUpdate = true;
-                }
-
-                if (needsUpdate) {
-                  threadStore.put(threadRecord);
-                }
-                messageRecord.threadId = threadRecord.id;
-                messageRecord.threadIdIndex = [threadRecord.id, timestamp];
-                messageCursor.update(messageRecord);
-                messageCursor.continue();
-                return;
-              }
-
-              let threadRecord = {
-                participantIds: participantIds,
-                participantAddresses: threadParticipants,
-                lastMessageId: messageRecord.id,
-                lastTimestamp: timestamp,
-                subject: messageRecord.body,
-                unreadCount: messageRecord.read ? 0 : 1,
-                lastMessageType: messageRecord.type
-              };
-              threadStore.add(threadRecord).onsuccess = function (event) {
-                let threadId = event.target.result;
-                // Setup threadId & threadIdIndex.
-                messageRecord.threadId = threadId;
-                messageRecord.threadIdIndex = [threadId, timestamp];
-                messageCursor.update(messageRecord);
-                messageCursor.continue();
-              };
-            });
-          };
-        })(0);
-      };
-    };
-  },
-
-  /**
-   * Add deliveryTimestamp.
-   */
-  upgradeSchema14: function upgradeSchema14(transaction, next) {
-    let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-
-    messageStore.openCursor().onsuccess = function(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        next();
-        return;
-      }
-
-      let messageRecord = cursor.value;
-      if (messageRecord.type == "sms") {
-        messageRecord.deliveryTimestamp = 0;
-      } else if (messageRecord.type == "mms") {
-        let deliveryInfo = messageRecord.deliveryInfo;
-        for (let i = 0; i < deliveryInfo.length; i++) {
-          deliveryInfo[i].deliveryTimestamp = 0;
-        }
-      }
-      cursor.update(messageRecord);
-      cursor.continue();
-    };
-  },
-
-  /**
-   * Add ICC ID.
-   */
-  upgradeSchema15: function upgradeSchema15(transaction, next) {
-    let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-    messageStore.openCursor().onsuccess = function(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        next();
-        return;
-      }
-
-      let messageRecord = cursor.value;
-      messageRecord.iccId = null;
-      cursor.update(messageRecord);
-      cursor.continue();
-    };
-  },
-
-  /**
-   * Add isReadReportSent for incoming MMS.
-   */
-  upgradeSchema16: function upgradeSchema16(transaction, next) {
-    let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-
-    // Update type attributes.
-    messageStore.openCursor().onsuccess = function(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        next();
-        return;
-      }
-
-      let messageRecord = cursor.value;
-      if (messageRecord.type == "mms") {
-        messageRecord.isReadReportSent = false;
-        cursor.update(messageRecord);
-      }
-      cursor.continue();
-    };
-  },
-
-  upgradeSchema17: function upgradeSchema17(transaction, next) {
-    let threadStore = transaction.objectStore(THREAD_STORE_NAME);
-    let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-
-    // Add 'lastMessageSubject' to each thread record.
-    threadStore.openCursor().onsuccess = function(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        next();
-        return;
-      }
-
-      let threadRecord = cursor.value;
-      // We have defined 'threadRecord.subject' in upgradeSchema7(), but it
-      // actually means 'threadRecord.body'.  Swap the two values first.
-      threadRecord.body = threadRecord.subject;
-      delete threadRecord.subject;
-
-      // Only MMS supports subject so assign null for non-MMS one.
-      if (threadRecord.lastMessageType != "mms") {
-        threadRecord.lastMessageSubject = null;
-        cursor.update(threadRecord);
-
-        cursor.continue();
-        return;
-      }
-
-      messageStore.get(threadRecord.lastMessageId).onsuccess = function(event) {
-        let messageRecord = event.target.result;
-        let subject = messageRecord.headers.subject;
-        threadRecord.lastMessageSubject = subject || null;
-        cursor.update(threadRecord);
-
-        cursor.continue();
-      };
-    };
-  },
-
-  /**
-   * Add pid for incoming SMS.
-   */
-  upgradeSchema18: function upgradeSchema18(transaction, next) {
-    let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-
-    messageStore.openCursor().onsuccess = function(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        next();
-        return;
-      }
-
-      let messageRecord = cursor.value;
-      if (messageRecord.type == "sms") {
-        messageRecord.pid = RIL.PDU_PID_DEFAULT;
-        cursor.update(messageRecord);
-      }
-      cursor.continue();
-    };
-  },
-
-  /**
-   * Add readStatus and readTimestamp.
-   */
-  upgradeSchema19: function upgradeSchema19(transaction, next) {
-    let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
-    messageStore.openCursor().onsuccess = function(event) {
-      let cursor = event.target.result;
-      if (!cursor) {
-        next();
-        return;
-      }
-
-      let messageRecord = cursor.value;
-      if (messageRecord.type == "sms") {
-        cursor.continue();
-        return;
-      }
-
-      // We can always retrieve transaction id from
-      // |messageRecord.headers["x-mms-transaction-id"]|.
-      if (messageRecord.hasOwnProperty("transactionId")) {
-        delete messageRecord.transactionId;
-      }
-
-      // xpconnect gives "undefined" for an unassigned argument of an interface
-      // method.
-      if (messageRecord.envelopeIdIndex === "undefined") {
-        delete messageRecord.envelopeIdIndex;
-      }
-
-      // Convert some header fields that were originally decoded as BooleanValue
-      // to numeric enums.
-      for (let field of ["x-mms-cancel-status",
-                         "x-mms-sender-visibility",
-                         "x-mms-read-status"]) {
-        let value = messageRecord.headers[field];
-        if (value !== undefined) {
-          messageRecord.headers[field] = value ? 128 : 129;
-        }
-      }
-
-      // For all sent and received MMS messages, we have to add their
-      // |readStatus| and |readTimestamp| attributes in |deliveryInfo| array.
-      let readReportRequested =
-        messageRecord.headers["x-mms-read-report"] || false;
-      for (let element of messageRecord.deliveryInfo) {
-        element.readStatus = readReportRequested
-                           ? MMS.DOM_READ_STATUS_PENDING
-                           : MMS.DOM_READ_STATUS_NOT_APPLICABLE;
-        element.readTimestamp = 0;
-      }
-
-      cursor.update(messageRecord);
-      cursor.continue();
-    };
-  },
-
-  matchParsedPhoneNumbers: function matchParsedPhoneNumbers(addr1, parsedAddr1,
-                                                            addr2, parsedAddr2) {
-    if ((parsedAddr1.internationalNumber &&
-         parsedAddr1.internationalNumber === parsedAddr2.internationalNumber) ||
-        (parsedAddr1.nationalNumber &&
-         parsedAddr1.nationalNumber === parsedAddr2.nationalNumber)) {
-      return true;
-    }
-
-    if (parsedAddr1.countryName != parsedAddr2.countryName) {
-      return false;
-    }
-
-    let ssPref = "dom.phonenumber.substringmatching." + parsedAddr1.countryName;
-    if (Services.prefs.getPrefType(ssPref) != Ci.nsIPrefBranch.PREF_INT) {
-      return false;
-    }
-
-    let val = Services.prefs.getIntPref(ssPref);
-    return addr1.length > val &&
-           addr2.length > val &&
-           addr1.slice(-val) === addr2.slice(-val);
-  },
-
-  matchPhoneNumbers: function matchPhoneNumbers(addr1, parsedAddr1, addr2, parsedAddr2) {
-    if (parsedAddr1 && parsedAddr2) {
-      return this.matchParsedPhoneNumbers(addr1, parsedAddr1, addr2, parsedAddr2);
-    }
-
-    if (parsedAddr1) {
-      parsedAddr2 = PhoneNumberUtils.parseWithCountryName(addr2, parsedAddr1.countryName);
-      if (parsedAddr2) {
-        return this.matchParsedPhoneNumbers(addr1, parsedAddr1, addr2, parsedAddr2);
-      }
-
-      return false;
-    }
-
-    if (parsedAddr2) {
-      parsedAddr1 = PhoneNumberUtils.parseWithCountryName(addr1, parsedAddr2.countryName);
-      if (parsedAddr1) {
-        return this.matchParsedPhoneNumbers(addr1, parsedAddr1, addr2, parsedAddr2);
-      }
-    }
-
-    return false;
-  },
-
-  createDomMessageFromRecord: function createDomMessageFromRecord(aMessageRecord) {
-    if (DEBUG) {
-      debug("createDomMessageFromRecord: " + JSON.stringify(aMessageRecord));
-    }
-    if (aMessageRecord.type == "sms") {
-      return gMobileMessageService.createSmsMessage(aMessageRecord.id,
-                                                    aMessageRecord.threadId,
-                                                    aMessageRecord.iccId,
-                                                    aMessageRecord.delivery,
-                                                    aMessageRecord.deliveryStatus,
-                                                    aMessageRecord.sender,
-                                                    aMessageRecord.receiver,
-                                                    aMessageRecord.body,
-                                                    aMessageRecord.messageClass,
-                                                    aMessageRecord.timestamp,
-                                                    aMessageRecord.deliveryTimestamp,
-                                                    aMessageRecord.read);
-    } else if (aMessageRecord.type == "mms") {
-      let headers = aMessageRecord["headers"];
-      if (DEBUG) {
-        debug("MMS: headers: " + JSON.stringify(headers));
-      }
-
-      let subject = headers["subject"];
-      if (subject == undefined) {
-        subject = "";
-      }
-
-      let smil = "";
-      let attachments = [];
-      let parts = aMessageRecord.parts;
-      if (parts) {
-        for (let i = 0; i < parts.length; i++) {
-          let part = parts[i];
-          if (DEBUG) {
-            debug("MMS: part[" + i + "]: " + JSON.stringify(part));
-          }
-          // Sometimes the part is incomplete because the device reboots when
-          // downloading MMS. Don't need to expose this part to the content.
-          if (!part) {
-            continue;
-          }
-
-          let partHeaders = part["headers"];
-          let partContent = part["content"];
-          // Don't need to make the SMIL part if it's present.
-          if (partHeaders["content-type"]["media"] == "application/smil") {
-            smil = partContent;
-            continue;
-          }
-          attachments.push({
-            "id": partHeaders["content-id"],
-            "location": partHeaders["content-location"],
-            "content": partContent
-          });
-        }
-      }
-      let expiryDate = 0;
-      if (headers["x-mms-expiry"] != undefined) {
-        expiryDate = aMessageRecord.timestamp + headers["x-mms-expiry"] * 1000;
-      }
-      let readReportRequested = headers["x-mms-read-report"] || false;
-      return gMobileMessageService.createMmsMessage(aMessageRecord.id,
-                                                    aMessageRecord.threadId,
-                                                    aMessageRecord.iccId,
-                                                    aMessageRecord.delivery,
-                                                    aMessageRecord.deliveryInfo,
-                                                    aMessageRecord.sender,
-                                                    aMessageRecord.receivers,
-                                                    aMessageRecord.timestamp,
-                                                    aMessageRecord.read,
-                                                    subject,
-                                                    smil,
-                                                    attachments,
-                                                    expiryDate,
-                                                    readReportRequested);
-    }
-  },
-
-  findParticipantRecordByAddress: function findParticipantRecordByAddress(
-      aParticipantStore, aAddress, aCreate, aCallback) {
-    if (DEBUG) {
-      debug("findParticipantRecordByAddress("
-            + JSON.stringify(aAddress) + ", " + aCreate + ")");
-    }
-
-    // Two types of input number to match here, international(+886987654321),
-    // and local(0987654321) types. The "nationalNumber" parsed from
-    // phonenumberutils will be "987654321" in this case.
-
-    // Normalize address before searching for participant record.
-    let normalizedAddress = PhoneNumberUtils.normalize(aAddress, false);
-    let allPossibleAddresses = [normalizedAddress];
-    let parsedAddress = PhoneNumberUtils.parse(normalizedAddress);
-    if (parsedAddress && parsedAddress.internationalNumber &&
-        allPossibleAddresses.indexOf(parsedAddress.internationalNumber) < 0) {
-      // We only stores international numbers into participant store because
-      // the parsed national number doesn't contain country info and may
-      // duplicate in different country.
-      allPossibleAddresses.push(parsedAddress.internationalNumber);
-    }
-    if (DEBUG) {
-      debug("findParticipantRecordByAddress: allPossibleAddresses = " +
-            JSON.stringify(allPossibleAddresses));
-    }
-
-    // Make a copy here because we may need allPossibleAddresses again.
-    let needles = allPossibleAddresses.slice(0);
-    let request = aParticipantStore.index("addresses").get(needles.pop());
-    request.onsuccess = (function onsuccess(event) {
-      let participantRecord = event.target.result;
-      // 1) First try matching through "addresses" index of participant store.
-      //    If we're lucky, return the fetched participant record.
-      if (participantRecord) {
-        if (DEBUG) {
-          debug("findParticipantRecordByAddress: got "
-                + JSON.stringify(participantRecord));
-        }
-        aCallback(participantRecord);
-        return;
-      }
-
-      // Try next possible address again.
-      if (needles.length) {
-        let request = aParticipantStore.index("addresses").get(needles.pop());
-        request.onsuccess = onsuccess.bind(this);
-        return;
-      }
-
-      // 2) Traverse throught all participants and check all alias addresses.
-      aParticipantStore.openCursor().onsuccess = (function (event) {
-        let cursor = event.target.result;
-        if (!cursor) {
-          // Have traversed whole object store but still in vain.
-          if (!aCreate) {
-            aCallback(null);
-            return;
-          }
-
-          let participantRecord = { addresses: [normalizedAddress] };
-          let addRequest = aParticipantStore.add(participantRecord);
-          addRequest.onsuccess = function (event) {
-            participantRecord.id = event.target.result;
-            if (DEBUG) {
-              debug("findParticipantRecordByAddress: created "
-                    + JSON.stringify(participantRecord));
-            }
-            aCallback(participantRecord);
-          };
-          return;
-        }
-
-        let participantRecord = cursor.value;
-        for (let storedAddress of participantRecord.addresses) {
-          let parsedStoredAddress = PhoneNumberUtils.parseWithMCC(storedAddress, null);
-          let match = this.matchPhoneNumbers(normalizedAddress, parsedAddress,
-                                             storedAddress, parsedStoredAddress);
-          if (!match) {
-            // 3) Else we fail to match current stored participant record.
-            continue;
-          }
-          // Match!
-          if (aCreate) {
-            // In a READ-WRITE transaction, append one more possible address for
-            // this participant record.
-            participantRecord.addresses =
-              participantRecord.addresses.concat(allPossibleAddresses);
-            cursor.update(participantRecord);
-          }
-
-          if (DEBUG) {
-            debug("findParticipantRecordByAddress: match "
-                  + JSON.stringify(cursor.value));
-          }
-          aCallback(participantRecord);
-          return;
-        }
-
-        // Check next participant record if available.
-        cursor.continue();
-      }).bind(this);
-    }).bind(this);
-  },
-
-  findParticipantIdsByAddresses: function findParticipantIdsByAddresses(
-      aParticipantStore, aAddresses, aCreate, aSkipNonexistent, aCallback) {
-    if (DEBUG) {
-      debug("findParticipantIdsByAddresses("
-            + JSON.stringify(aAddresses) + ", "
-            + aCreate + ", " + aSkipNonexistent + ")");
-    }
-
-    if (!aAddresses || !aAddresses.length) {
-      if (DEBUG) debug("findParticipantIdsByAddresses: returning null");
-      aCallback(null);
-      return;
-    }
-
-    let self = this;
-    (function findParticipantId(index, result) {
-      if (index >= aAddresses.length) {
-        // Sort numerically.
-        result.sort(function (a, b) {
-          return a - b;
-        });
-        if (DEBUG) debug("findParticipantIdsByAddresses: returning " + result);
-        aCallback(result);
-        return;
-      }
-
-      self.findParticipantRecordByAddress(aParticipantStore,
-                                          aAddresses[index++], aCreate,
-                                          function (participantRecord) {
-        if (!participantRecord) {
-          if (!aSkipNonexistent) {
-            if (DEBUG) debug("findParticipantIdsByAddresses: returning null");
-            aCallback(null);
-            return;
-          }
-        } else if (result.indexOf(participantRecord.id) < 0) {
-          result.push(participantRecord.id);
-        }
-        findParticipantId(index, result);
-      });
-    }) (0, []);
-  },
-
-  findThreadRecordByParticipants: function findThreadRecordByParticipants(
-      aThreadStore, aParticipantStore, aAddresses,
-      aCreateParticipants, aCallback) {
-    if (DEBUG) {
-      debug("findThreadRecordByParticipants(" + JSON.stringify(aAddresses)
-            + ", " + aCreateParticipants + ")");
-    }
-    this.findParticipantIdsByAddresses(aParticipantStore, aAddresses,
-                                       aCreateParticipants, false,
-                                       function (participantIds) {
-      if (!participantIds) {
-        if (DEBUG) debug("findThreadRecordByParticipants: returning null");
-        aCallback(null, null);
-        return;
-      }
-      // Find record from thread store.
-      let request = aThreadStore.index("participantIds").get(participantIds);
-      request.onsuccess = function (event) {
-        let threadRecord = event.target.result;
-        if (DEBUG) {
-          debug("findThreadRecordByParticipants: return "
-                + JSON.stringify(threadRecord));
-        }
-        aCallback(threadRecord, participantIds);
-      };
-    });
-  },
-
-  newTxnWithCallback: function newTxnWithCallback(aCallback, aFunc, aStoreNames) {
-    let self = this;
-    this.newTxn(READ_WRITE, function(aError, aTransaction, aStores) {
-      let notifyResult = function(aRv, aMessageRecord) {
-        if (!aCallback) {
-          return;
-        }
-        let domMessage =
-          aMessageRecord && self.createDomMessageFromRecord(aMessageRecord);
-        aCallback.notify(aRv, domMessage);
-      };
-
-      if (aError) {
-        // TODO bug 832140 check event.target.errorCode
-        notifyResult(Cr.NS_ERROR_FAILURE, null);
-        return;
-      }
-
-      let capture = {};
-      aTransaction.oncomplete = function(event) {
-        notifyResult(Cr.NS_OK, capture.messageRecord);
-      };
-      aTransaction.onabort = function(event) {
-        // TODO bug 832140 check event.target.errorCode
-        notifyResult(Cr.NS_ERROR_FAILURE, null);
-      };
-
-      aFunc(capture, aStores);
-    }, aStoreNames);
-  },
-
-  saveRecord: function saveRecord(aMessageRecord, aAddresses, aCallback) {
-    if (DEBUG) debug("Going to store " + JSON.stringify(aMessageRecord));
-
-    let self = this;
-    this.newTxn(READ_WRITE, function(error, txn, stores) {
-      let notifyResult = function(aRv, aMessageRecord) {
-        if (!aCallback) {
-          return;
-        }
-        let domMessage =
-          aMessageRecord && self.createDomMessageFromRecord(aMessageRecord);
-        aCallback.notify(aRv, domMessage);
-      };
-
-      if (error) {
-        // TODO bug 832140 check event.target.errorCode
-        notifyResult(Cr.NS_ERROR_FAILURE, null);
-        return;
-      }
-
-      txn.oncomplete = function oncomplete(event) {
-        if (aMessageRecord.id > self.lastMessageId) {
-          self.lastMessageId = aMessageRecord.id;
-        }
-        notifyResult(Cr.NS_OK, aMessageRecord);
-      };
-      txn.onabort = function onabort(event) {
-        // TODO bug 832140 check event.target.errorCode
-        notifyResult(Cr.NS_ERROR_FAILURE, null);
-      };
-
-      let messageStore = stores[0];
-      let participantStore = stores[1];
-      let threadStore = stores[2];
-      self.replaceShortMessageOnSave(txn, messageStore, participantStore,
-                                     threadStore, aMessageRecord, aAddresses);
-    }, [MESSAGE_STORE_NAME, PARTICIPANT_STORE_NAME, THREAD_STORE_NAME]);
-  },
-
-  replaceShortMessageOnSave:
-    function replaceShortMessageOnSave(aTransaction, aMessageStore,
-                                       aParticipantStore, aThreadStore,
-                                       aMessageRecord, aAddresses) {
-    let isReplaceTypePid = (aMessageRecord.pid) &&
-                           ((aMessageRecord.pid >= RIL.PDU_PID_REPLACE_SHORT_MESSAGE_TYPE_1 &&
-                             aMessageRecord.pid <= RIL.PDU_PID_REPLACE_SHORT_MESSAGE_TYPE_7) ||
-                            aMessageRecord.pid == RIL.PDU_PID_RETURN_CALL_MESSAGE);
-
-    if (aMessageRecord.type != "sms" ||
-        aMessageRecord.delivery != DELIVERY_RECEIVED ||
-        !isReplaceTypePid) {
-      this.realSaveRecord(aTransaction, aMessageStore, aParticipantStore,
-                          aThreadStore, aMessageRecord, aAddresses);
-      return;
-    }
-
-    // 3GPP TS 23.040 subclause 9.2.3.9 "TP-Protocol-Identifier (TP-PID)":
-    //
-    //   ... the MS shall check the originating address and replace any
-    //   existing stored message having the same Protocol Identifier code
-    //   and originating address with the new short message and other
-    //   parameter values. If there is no message to be replaced, the MS
-    //   shall store the message in the normal way. ... it is recommended
-    //   that the SC address should not be checked by the MS."
-    let self = this;
-    this.findParticipantRecordByAddress(aParticipantStore,
-                                        aMessageRecord.sender, false,
-                                        function(participantRecord) {
-      if (!participantRecord) {
-        self.realSaveRecord(aTransaction, aMessageStore, aParticipantStore,
-                            aThreadStore, aMessageRecord, aAddresses);
-        return;
-      }
-
-      let participantId = participantRecord.id;
-      let range = IDBKeyRange.bound([participantId, 0], [participantId, ""]);
-      let request = aMessageStore.index("participantIds").openCursor(range);
-      request.onsuccess = function onsuccess(event) {
-        let cursor = event.target.result;
-        if (!cursor) {
-          self.realSaveRecord(aTransaction, aMessageStore, aParticipantStore,
-                              aThreadStore, aMessageRecord, aAddresses);
-          return;
-        }
-
-        // A message record with same participantId found.
-        // Verify matching criteria.
-        let foundMessageRecord = cursor.value;
-        if (foundMessageRecord.type != "sms" ||
-            foundMessageRecord.sender != aMessageRecord.sender ||
-            foundMessageRecord.pid != aMessageRecord.pid) {
-          cursor.continue();
-          return;
-        }
-
-        // Match! Now replace that found message record with current one.
-        aMessageRecord.id = foundMessageRecord.id;
-        self.realSaveRecord(aTransaction, aMessageStore, aParticipantStore,
-                            aThreadStore, aMessageRecord, aAddresses);
-      };
-    });
-  },
-
-  realSaveRecord: function realSaveRecord(aTransaction, aMessageStore,
-                                          aParticipantStore, aThreadStore,
-                                          aMessageRecord, aAddresses) {
-    let self = this;
-    this.findThreadRecordByParticipants(aThreadStore, aParticipantStore,
-                                        aAddresses, true,
-                                        function(threadRecord, participantIds) {
-      if (!participantIds) {
-        aTransaction.abort();
-        return;
-      }
-
-      let isOverriding = (aMessageRecord.id !== undefined);
-      if (!isOverriding) {
-        // |self.lastMessageId| is only updated in |txn.oncomplete|.
-        aMessageRecord.id = self.lastMessageId + 1;
-      }
-
-      let timestamp = aMessageRecord.timestamp;
-      let insertMessageRecord = function(threadId) {
-        // Setup threadId & threadIdIndex.
-        aMessageRecord.threadId = threadId;
-        aMessageRecord.threadIdIndex = [threadId, timestamp];
-        // Setup participantIdsIndex.
-        aMessageRecord.participantIdsIndex = [];
-        for each (let id in participantIds) {
-          aMessageRecord.participantIdsIndex.push([id, timestamp]);
-        }
-
-        if (!isOverriding) {
-          // Really add to message store.
-          aMessageStore.put(aMessageRecord);
-          return;
-        }
-
-        // If we're going to override an old message, we need to update the
-        // info of the original thread containing the overridden message.
-        // To get the original thread ID and read status of the overridden
-        // message record, we need to retrieve it before overriding it.
-        aMessageStore.get(aMessageRecord.id).onsuccess = function(event) {
-          let oldMessageRecord = event.target.result;
-          aMessageStore.put(aMessageRecord);
-          if (oldMessageRecord) {
-            self.updateThreadByMessageChange(aMessageStore,
-                                             aThreadStore,
-                                             oldMessageRecord.threadId,
-                                             aMessageRecord.id,
-                                             oldMessageRecord.read);
-          }
-        };
-      };
-
-      if (threadRecord) {
-        let needsUpdate = false;
-
-        if (threadRecord.lastTimestamp <= timestamp) {
-          let lastMessageSubject;
-          if (aMessageRecord.type == "mms") {
-            lastMessageSubject = aMessageRecord.headers.subject;
-          }
-          threadRecord.lastMessageSubject = lastMessageSubject || null;
-          threadRecord.lastTimestamp = timestamp;
-          threadRecord.body = aMessageRecord.body;
-          threadRecord.lastMessageId = aMessageRecord.id;
-          threadRecord.lastMessageType = aMessageRecord.type;
-          needsUpdate = true;
-        }
-
-        if (!aMessageRecord.read) {
-          threadRecord.unreadCount++;
-          needsUpdate = true;
-        }
-
-        if (needsUpdate) {
-          aThreadStore.put(threadRecord);
-        }
-
-        insertMessageRecord(threadRecord.id);
-        return;
-      }
-
-      let lastMessageSubject;
-      if (aMessageRecord.type == "mms") {
-        lastMessageSubject = aMessageRecord.headers.subject;
-      }
-
-      threadRecord = {
-        participantIds: participantIds,
-        participantAddresses: aAddresses,
-        lastMessageId: aMessageRecord.id,
-        lastTimestamp: timestamp,
-        lastMessageSubject: lastMessageSubject || null,
-        body: aMessageRecord.body,
-        unreadCount: aMessageRecord.read ? 0 : 1,
-        lastMessageType: aMessageRecord.type,
-      };
-      aThreadStore.add(threadRecord).onsuccess = function(event) {
-        let threadId = event.target.result;
-        insertMessageRecord(threadId);
-      };
-    });
-  },
-
-  forEachMatchedMmsDeliveryInfo:
-    function forEachMatchedMmsDeliveryInfo(aDeliveryInfo, aNeedle, aCallback) {
-
-    let typedAddress = {
-      type: MMS.Address.resolveType(aNeedle),
-      address: aNeedle
-    };
-    let normalizedAddress, parsedAddress;
-    if (typedAddress.type === "PLMN") {
-      normalizedAddress = PhoneNumberUtils.normalize(aNeedle, false);
-      parsedAddress = PhoneNumberUtils.parse(normalizedAddress);
-    }
-
-    for (let element of aDeliveryInfo) {
-      let typedStoredAddress = {
-        type: MMS.Address.resolveType(element.receiver),
-        address: element.receiver
-      };
-      if (typedAddress.type !== typedStoredAddress.type) {
-        // Not even my type.  Skip.
-        continue;
-      }
-
-      if (typedAddress.address == typedStoredAddress.address) {
-        // Have a direct match.
-        aCallback(element);
-        continue;
-      }
-
-      if (typedAddress.type !== "PLMN") {
-        // Address type other than "PLMN" must have direct match.  Or, skip.
-        continue;
-      }
-
-      // Both are of "PLMN" type.
-      let normalizedStoredAddress =
-        PhoneNumberUtils.normalize(element.receiver, false);
-      let parsedStoredAddress =
-        PhoneNumberUtils.parseWithMCC(normalizedStoredAddress, null);
-      if (this.matchPhoneNumbers(normalizedAddress, parsedAddress,
-                                 normalizedStoredAddress, parsedStoredAddress)) {
-        aCallback(element);
-      }
-    }
-  },
-
-  updateMessageDeliveryById: function updateMessageDeliveryById(
-      id, type, receiver, delivery, deliveryStatus, envelopeId, callback) {
-    if (DEBUG) {
-      debug("Setting message's delivery by " + type + " = "+ id
-            + " receiver: " + receiver
-            + " delivery: " + delivery
-            + " deliveryStatus: " + deliveryStatus
-            + " envelopeId: " + envelopeId);
-    }
-
-    let self = this;
-    this.newTxnWithCallback(callback, function(aCapture, aMessageStore) {
-      let getRequest;
-      if (type === "messageId") {
-        getRequest = aMessageStore.get(id);
-      } else if (type === "envelopeId") {
-        getRequest = aMessageStore.index("envelopeId").get(id);
-      }
-
-      getRequest.onsuccess = function onsuccess(event) {
-        let messageRecord = event.target.result;
-        if (!messageRecord) {
-          if (DEBUG) debug("type = " + id + " is not found");
-          throw Cr.NS_ERROR_FAILURE;
-        }
-
-        let isRecordUpdated = false;
-
-        // Update |messageRecord.delivery| if needed.
-        if (delivery && messageRecord.delivery != delivery) {
-          messageRecord.delivery = delivery;
-          messageRecord.deliveryIndex = [delivery, messageRecord.timestamp];
-          isRecordUpdated = true;
-        }
-
-        // Attempt to update |deliveryStatus| and |deliveryTimestamp| of:
-        // - the |messageRecord| for SMS.
-        // - the element(s) in |messageRecord.deliveryInfo| for MMS.
-        if (deliveryStatus) {
-          // A callback for updating the deliveyStatus/deliveryTimestamp of
-          // each target.
-          let updateFunc = function(aTarget) {
-            if (aTarget.deliveryStatus == deliveryStatus) {
-              return;
-            }
-
-            aTarget.deliveryStatus = deliveryStatus;
-
-            // Update |deliveryTimestamp| if it's successfully delivered.
-            if (deliveryStatus == DELIVERY_STATUS_SUCCESS) {
-              aTarget.deliveryTimestamp = Date.now();
-            }
-
-            isRecordUpdated = true;
-          };
-
-          if (messageRecord.type == "sms") {
-            updateFunc(messageRecord);
-          } else if (messageRecord.type == "mms") {
-            if (!receiver) {
-              // If the receiver is specified, we only need to update the
-              // element(s) in deliveryInfo that match the same receiver.
-              messageRecord.deliveryInfo.forEach(updateFunc);
-            } else {
-              self.forEachMatchedMmsDeliveryInfo(messageRecord.deliveryInfo,
-                                                 receiver, updateFunc);
-            }
-          }
-        }
-
-        // Update |messageRecord.envelopeIdIndex| if needed.
-        if (envelopeId) {
-          if (messageRecord.envelopeIdIndex != envelopeId) {
-            messageRecord.envelopeIdIndex = envelopeId;
-            isRecordUpdated = true;
-          }
-        }
-
-        aCapture.messageRecord = messageRecord;
-        if (!isRecordUpdated) {
-          if (DEBUG) {
-            debug("The values of delivery, deliveryStatus and envelopeId " +
-                  "don't need to be updated.");
-          }
-          return;
-        }
-
-        if (DEBUG) {
-          debug("The delivery, deliveryStatus or envelopeId are updated.");
-        }
-        aMessageStore.put(messageRecord);
-      };
-    });
-  },
-
-  fillReceivedMmsThreadParticipants: function fillReceivedMmsThreadParticipants(aMessage, threadParticipants) {
-    let receivers = aMessage.receivers;
-    // If we don't want to disable the MMS grouping for receiving, we need to
-    // add the receivers (excluding the user's own number) to the participants
-    // for creating the thread. Some cases might be investigated as below:
-    //
-    // 1. receivers.length == 0
-    //    This usually happens when receiving an MMS notification indication
-    //    which doesn't carry any receivers.
-    // 2. receivers.length == 1
-    //    If the receivers contain single phone number, we don't need to
-    //    add it into participants because we know that number is our own.
-    // 3. receivers.length >= 2
-    //    If the receivers contain multiple phone numbers, we need to add all
-    //    of them but not the user's own number into participants.
-    if (DISABLE_MMS_GROUPING_FOR_RECEIVING || receivers.length < 2) {
-      return;
-    }
-    let isSuccess = false;
-    let slicedReceivers = receivers.slice();
-    if (aMessage.msisdn) {
-      let found = slicedReceivers.indexOf(aMessage.msisdn);
-      if (found !== -1) {
-        isSuccess = true;
-        slicedReceivers.splice(found, 1);
-      }
-    }
-
-    if (!isSuccess) {
-      // For some SIMs we cannot retrieve the vaild MSISDN (i.e. the user's
-      // own phone number), so we cannot correcly exclude the user's own
-      // number from the receivers, thus wrongly building the thread index.
-      if (DEBUG) debug("Error! Cannot strip out user's own phone number!");
-    }
-
-    threadParticipants = threadParticipants.concat(slicedReceivers);
-  },
-
-  updateThreadByMessageChange: function updateThreadByMessageChange(messageStore,
-                                                                    threadStore,
-                                                                    threadId,
-                                                                    messageId,
-                                                                    messageRead) {
-    threadStore.get(threadId).onsuccess = function(event) {
-      // This must exist.
-      let threadRecord = event.target.result;
-      if (DEBUG) debug("Updating thread record " + JSON.stringify(threadRecord));
-
-      if (!messageRead) {
-        threadRecord.unreadCount--;
-      }
-
-      if (threadRecord.lastMessageId == messageId) {
-        // Check most recent sender/receiver.
-        let range = IDBKeyRange.bound([threadId, 0], [threadId, ""]);
-        let request = messageStore.index("threadId")
-                                  .openCursor(range, PREV);
-        request.onsuccess = function(event) {
-          let cursor = event.target.result;
-          if (!cursor) {
-            if (DEBUG) {
-              debug("Deleting mru entry for thread id " + threadId);
-            }
-            threadStore.delete(threadId);
-            return;
-          }
-
-          let nextMsg = cursor.value;
-          let lastMessageSubject;
-          if (nextMsg.type == "mms") {
-            lastMessageSubject = nextMsg.headers.subject;
-          }
-          threadRecord.lastMessageSubject = lastMessageSubject || null;
-          threadRecord.lastMessageId = nextMsg.id;
-          threadRecord.lastTimestamp = nextMsg.timestamp;
-          threadRecord.body = nextMsg.body;
-          threadRecord.lastMessageType = nextMsg.type;
-          if (DEBUG) {
-            debug("Updating mru entry: " +
-                  JSON.stringify(threadRecord));
-          }
-          threadStore.put(threadRecord);
-        };
-      } else if (!messageRead) {
-        // Shortcut, just update the unread count.
-        if (DEBUG) {
-          debug("Updating unread count for thread id " + threadId + ": " +
-                (threadRecord.unreadCount + 1) + " -> " +
-                threadRecord.unreadCount);
-        }
-        threadStore.put(threadRecord);
-      }
-    };
-  },
-
-  /**
    * nsIRilMobileMessageDatabaseService API
    */
 
-  saveReceivedMessage: function saveReceivedMessage(aMessage, aCallback) {
-    if ((aMessage.type != "sms" && aMessage.type != "mms") ||
-        (aMessage.type == "sms" && (aMessage.messageClass == undefined ||
-                                    aMessage.sender == undefined)) ||
-        (aMessage.type == "mms" && (aMessage.delivery == undefined ||
-                                    aMessage.deliveryStatus == undefined ||
-                                    !Array.isArray(aMessage.receivers))) ||
-        aMessage.timestamp == undefined) {
-      if (aCallback) {
-        aCallback.notify(Cr.NS_ERROR_FAILURE, null);
-      }
-      return;
-    }
-
-    let threadParticipants;
-    if (aMessage.type == "mms") {
-      if (aMessage.headers.from) {
-        aMessage.sender = aMessage.headers.from.address;
-      } else {
-        aMessage.sender = "anonymous";
-      }
-
-      threadParticipants = [aMessage.sender];
-      this.fillReceivedMmsThreadParticipants(aMessage, threadParticipants);
-    } else { // SMS
-      threadParticipants = [aMessage.sender];
-    }
-
-    let timestamp = aMessage.timestamp;
-
-    // Adding needed indexes and extra attributes for internal use.
-    // threadIdIndex & participantIdsIndex are filled in saveRecord().
-    aMessage.readIndex = [FILTER_READ_UNREAD, timestamp];
-    aMessage.read = FILTER_READ_UNREAD;
-
-    if (aMessage.type == "mms") {
-      aMessage.transactionIdIndex = aMessage.headers["x-mms-transaction-id"];
-      aMessage.isReadReportSent = false;
-
-      // As a receiver, we don't need to care about the delivery status of
-      // others, so we put a single element with self's phone number in the
-      // |deliveryInfo| array.
-      aMessage.deliveryInfo = [{
-        receiver: aMessage.phoneNumber,
-        deliveryStatus: aMessage.deliveryStatus,
-        deliveryTimestamp: 0,
-        readStatus: MMS.DOM_READ_STATUS_NOT_APPLICABLE,
-        readTimestamp: 0,
-      }];
-
-      delete aMessage.deliveryStatus;
-    }
-
-    if (aMessage.type == "sms") {
-      aMessage.delivery = DELIVERY_RECEIVED;
-      aMessage.deliveryStatus = DELIVERY_STATUS_SUCCESS;
-      aMessage.deliveryTimestamp = 0;
-
-      if (aMessage.pid == undefined) {
-        aMessage.pid = RIL.PDU_PID_DEFAULT;
-      }
-    }
-    aMessage.deliveryIndex = [aMessage.delivery, timestamp];
-
-    this.saveRecord(aMessage, threadParticipants, aCallback);
+  saveReceivedMessage: function(aMessage, aCallback) {
+    this.mmdb.saveReceivedMessage(aMessage, aCallback);
   },
 
-  saveSendingMessage: function saveSendingMessage(aMessage, aCallback) {
-    if ((aMessage.type != "sms" && aMessage.type != "mms") ||
-        (aMessage.type == "sms" && aMessage.receiver == undefined) ||
-        (aMessage.type == "mms" && !Array.isArray(aMessage.receivers)) ||
-        aMessage.deliveryStatusRequested == undefined ||
-        aMessage.timestamp == undefined) {
-      if (aCallback) {
-        aCallback.notify(Cr.NS_ERROR_FAILURE, null);
-      }
-      return;
-    }
+  saveSendingMessage: function(aMessage, aCallback) {
+    this.mmdb.saveSendingMessage(aMessage, aCallback);
+  },
 
-    // Set |aMessage.deliveryStatus|. Note that for MMS record
-    // it must be an array of strings; For SMS, it's a string.
-    let deliveryStatus = aMessage.deliveryStatusRequested
-                       ? DELIVERY_STATUS_PENDING
-                       : DELIVERY_STATUS_NOT_APPLICABLE;
-    if (aMessage.type == "sms") {
-      aMessage.deliveryStatus = deliveryStatus;
-      // If |deliveryTimestamp| is not specified, use 0 as default.
-      if (aMessage.deliveryTimestamp == undefined) {
-        aMessage.deliveryTimestamp = 0;
-      }
-    } else if (aMessage.type == "mms") {
-      let receivers = aMessage.receivers
-      if (!Array.isArray(receivers)) {
-        if (DEBUG) {
-          debug("Need receivers for MMS. Fail to save the sending message.");
-        }
-        if (aCallback) {
-          aCallback.notify(Cr.NS_ERROR_FAILURE, null);
-        }
-        return;
-      }
-      let readStatus = aMessage.headers["x-mms-read-report"]
-                     ? MMS.DOM_READ_STATUS_PENDING
-                     : MMS.DOM_READ_STATUS_NOT_APPLICABLE;
-      aMessage.deliveryInfo = [];
-      for (let i = 0; i < receivers.length; i++) {
-        aMessage.deliveryInfo.push({
-          receiver: receivers[i],
-          deliveryStatus: deliveryStatus,
-          deliveryTimestamp: 0,
-          readStatus: readStatus,
-          readTimestamp: 0,
-        });
-      }
-    }
-
-    let timestamp = aMessage.timestamp;
-
-    // Adding needed indexes and extra attributes for internal use.
-    // threadIdIndex & participantIdsIndex are filled in saveRecord().
-    aMessage.deliveryIndex = [DELIVERY_SENDING, timestamp];
-    aMessage.readIndex = [FILTER_READ_READ, timestamp];
-    aMessage.delivery = DELIVERY_SENDING;
-    aMessage.messageClass = MESSAGE_CLASS_NORMAL;
-    aMessage.read = FILTER_READ_READ;
-
-    let addresses;
-    if (aMessage.type == "sms") {
-      addresses = [aMessage.receiver];
-    } else if (aMessage.type == "mms") {
-      addresses = aMessage.receivers;
-    }
-    this.saveRecord(aMessage, addresses, aCallback);
+  setMessageDeliveryByMessageId: function(aMessageId, aReceiver, aDelivery,
+                                          aDeliveryStatus, aEnvelopeId,
+                                          aCallback) {
+    this.mmdb.updateMessageDeliveryById(aMessageId, "messageId", aReceiver,
+                                        aDelivery, aDeliveryStatus,
+                                        aEnvelopeId, aCallback);
   },
 
-  setMessageDeliveryByMessageId: function setMessageDeliveryByMessageId(
-      messageId, receiver, delivery, deliveryStatus, envelopeId, callback) {
-    this.updateMessageDeliveryById(messageId, "messageId",
-                                   receiver, delivery, deliveryStatus,
-                                   envelopeId, callback);
-
-  },
-
-  setMessageDeliveryStatusByEnvelopeId:
-    function setMessageDeliveryStatusByEnvelopeId(aEnvelopeId, aReceiver,
-                                                  aDeliveryStatus, aCallback) {
-    this.updateMessageDeliveryById(aEnvelopeId, "envelopeId", aReceiver, null,
-                                   aDeliveryStatus, null, aCallback);
-  },
-
-  setMessageReadStatusByEnvelopeId:
-    function setMessageReadStatusByEnvelopeId(aEnvelopeId, aReceiver,
-                                              aReadStatus, aCallback) {
-    if (DEBUG) {
-      debug("Setting message's read status by envelopeId = " + aEnvelopeId +
-            ", receiver: " + aReceiver + ", readStatus: " + aReadStatus);
-    }
-
-    let self = this;
-    this.newTxnWithCallback(aCallback, function(aCapture, aMessageStore) {
-      let getRequest = aMessageStore.index("envelopeId").get(aEnvelopeId);
-      getRequest.onsuccess = function onsuccess(event) {
-        let messageRecord = event.target.result;
-        if (!messageRecord) {
-          if (DEBUG) debug("envelopeId '" + aEnvelopeId + "' not found");
-          throw Cr.NS_ERROR_FAILURE;
-        }
-
-        aCapture.messageRecord = messageRecord;
-
-        let isRecordUpdated = false;
-        self.forEachMatchedMmsDeliveryInfo(messageRecord.deliveryInfo,
-                                           aReceiver, function(aEntry) {
-          if (aEntry.readStatus == aReadStatus) {
-            return;
-          }
-
-          aEntry.readStatus = aReadStatus;
-          if (aReadStatus == MMS.DOM_READ_STATUS_SUCCESS) {
-            aEntry.readTimestamp = Date.now();
-          } else {
-            aEntry.readTimestamp = 0;
-          }
-          isRecordUpdated = true;
-        });
-
-        if (!isRecordUpdated) {
-          if (DEBUG) {
-            debug("The values of readStatus don't need to be updated.");
-          }
-          return;
-        }
-
-        if (DEBUG) {
-          debug("The readStatus is updated.");
-        }
-        aMessageStore.put(messageRecord);
-      };
-    });
+  setMessageDeliveryStatusByEnvelopeId: function(aEnvelopeId, aReceiver,
+                                                 aDeliveryStatus, aCallback) {
+    this.mmdb.updateMessageDeliveryById(aEnvelopeId, "envelopeId", aReceiver,
+                                        null, aDeliveryStatus, null, aCallback);
   },
 
-  getMessageRecordByTransactionId: function getMessageRecordByTransactionId(aTransactionId, aCallback) {
-    if (DEBUG) debug("Retrieving message with transaction ID " + aTransactionId);
-    let self = this;
-    this.newTxn(READ_ONLY, function (error, txn, messageStore) {
-      if (error) {
-        if (DEBUG) debug(error);
-        aCallback.notify(Ci.nsIMobileMessageCallback.INTERNAL_ERROR, null, null);
-        return;
-      }
-      let request = messageStore.index("transactionId").get(aTransactionId);
-
-      txn.oncomplete = function oncomplete(event) {
-        if (DEBUG) debug("Transaction " + txn + " completed.");
-        let messageRecord = request.result;
-        if (!messageRecord) {
-          if (DEBUG) debug("Transaction ID " + aTransactionId + " not found");
-          aCallback.notify(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR, null, null);
-          return;
-        }
-        // In this case, we don't need a dom message. Just pass null to the
-        // third argument.
-        aCallback.notify(Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR,
-                         messageRecord, null);
-      };
-
-      txn.onerror = function onerror(event) {
-        if (DEBUG) {
-          if (event.target) {
-            debug("Caught error on transaction", event.target.errorCode);
-          }
-        }
-        aCallback.notify(Ci.nsIMobileMessageCallback.INTERNAL_ERROR, null, null);
-      };
-    });
+  setMessageReadStatusByEnvelopeId: function(aEnvelopeId, aReceiver,
+                                             aReadStatus, aCallback) {
+    this.mmdb.setMessageReadStatusByEnvelopeId(aEnvelopeId, aReceiver,
+                                               aReadStatus, aCallback);
   },
 
-  getMessageRecordById: function getMessageRecordById(aMessageId, aCallback) {
-    if (DEBUG) debug("Retrieving message with ID " + aMessageId);
-    let self = this;
-    this.newTxn(READ_ONLY, function (error, txn, messageStore) {
-      if (error) {
-        if (DEBUG) debug(error);
-        aCallback.notify(Ci.nsIMobileMessageCallback.INTERNAL_ERROR, null, null);
-        return;
-      }
-      let request = messageStore.mozGetAll(aMessageId);
+  getMessageRecordByTransactionId: function(aTransactionId, aCallback) {
+    this.mmdb.getMessageRecordByTransactionId(aTransactionId, aCallback);
+  },
 
-      txn.oncomplete = function oncomplete() {
-        if (DEBUG) debug("Transaction " + txn + " completed.");
-        if (request.result.length > 1) {
-          if (DEBUG) debug("Got too many results for id " + aMessageId);
-          aCallback.notify(Ci.nsIMobileMessageCallback.UNKNOWN_ERROR, null, null);
-          return;
-        }
-        let messageRecord = request.result[0];
-        if (!messageRecord) {
-          if (DEBUG) debug("Message ID " + aMessageId + " not found");
-          aCallback.notify(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR, null, null);
-          return;
-        }
-        if (messageRecord.id != aMessageId) {
-          if (DEBUG) {
-            debug("Requested message ID (" + aMessageId + ") is " +
-                  "different from the one we got");
-          }
-          aCallback.notify(Ci.nsIMobileMessageCallback.UNKNOWN_ERROR, null, null);
-          return;
-        }
-        let domMessage = self.createDomMessageFromRecord(messageRecord);
-        aCallback.notify(Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR,
-                         messageRecord, domMessage);
-      };
-
-      txn.onerror = function onerror(event) {
-        if (DEBUG) {
-          if (event.target) {
-            debug("Caught error on transaction", event.target.errorCode);
-          }
-        }
-        aCallback.notify(Ci.nsIMobileMessageCallback.INTERNAL_ERROR, null, null);
-      };
-    });
+  getMessageRecordById: function(aMessageId, aCallback) {
+    this.mmdb.getMessageRecordById(aMessageId, aCallback);
   },
 
   /**
    * nsIMobileMessageDatabaseService API
    */
 
-  getMessage: function getMessage(aMessageId, aRequest) {
-    if (DEBUG) debug("Retrieving message with ID " + aMessageId);
-    let notifyCallback = {
-      notify: function notify(aRv, aMessageRecord, aDomMessage) {
-        if (Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR == aRv) {
-          aRequest.notifyMessageGot(aDomMessage);
-          return;
-        }
-        aRequest.notifyGetMessageFailed(aRv, null);
-      }
-    };
-    this.getMessageRecordById(aMessageId, notifyCallback);
-  },
-
-  deleteMessage: function deleteMessage(messageIds, length, aRequest) {
-    if (DEBUG) debug("deleteMessage: message ids " + JSON.stringify(messageIds));
-    let deleted = [];
-    let self = this;
-    this.newTxn(READ_WRITE, function (error, txn, stores) {
-      if (error) {
-        if (DEBUG) debug("deleteMessage: failed to open transaction");
-        aRequest.notifyDeleteMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
-        return;
-      }
-      txn.onerror = function onerror(event) {
-        if (DEBUG) debug("Caught error on transaction", event.target.errorCode);
-        //TODO look at event.target.errorCode, pick appropriate error constant
-        aRequest.notifyDeleteMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
-      };
-
-      const messageStore = stores[0];
-      const threadStore = stores[1];
-
-      txn.oncomplete = function oncomplete(event) {
-        if (DEBUG) debug("Transaction " + txn + " completed.");
-        aRequest.notifyMessageDeleted(deleted, length);
-      };
-
-      for (let i = 0; i < length; i++) {
-        let messageId = messageIds[i];
-        deleted[i] = false;
-        messageStore.get(messageId).onsuccess = function(messageIndex, event) {
-          let messageRecord = event.target.result;
-          let messageId = messageIds[messageIndex];
-          if (messageRecord) {
-            if (DEBUG) debug("Deleting message id " + messageId);
-
-            // First actually delete the message.
-            messageStore.delete(messageId).onsuccess = function(event) {
-              if (DEBUG) debug("Message id " + messageId + " deleted");
-              deleted[messageIndex] = true;
-
-              // Then update unread count and most recent message.
-              self.updateThreadByMessageChange(messageStore,
-                                               threadStore,
-                                               messageRecord.threadId,
-                                               messageId,
-                                               messageRecord.read);
-
-              Services.obs.notifyObservers(null,
-                                           "mobile-message-deleted",
-                                           JSON.stringify({ id: messageId }));
-            };
-          } else if (DEBUG) {
-            debug("Message id " + messageId + " does not exist");
-          }
-        }.bind(null, i);
-      }
-    }, [MESSAGE_STORE_NAME, THREAD_STORE_NAME]);
+  getMessage: function(aMessageId, aRequest) {
+    this.mmdb.getMessage(aMessageId, aRequest);
   },
 
-  createMessageCursor: function createMessageCursor(filter, reverse, callback) {
-    if (DEBUG) {
-      debug("Creating a message cursor. Filters:" +
-            " startDate: " + filter.startDate +
-            " endDate: " + filter.endDate +
-            " delivery: " + filter.delivery +
-            " numbers: " + filter.numbers +
-            " read: " + filter.read +
-            " threadId: " + filter.threadId +
-            " reverse: " + reverse);
-    }
-
-    let cursor = new GetMessagesCursor(this, callback);
-
-    let self = this;
-    self.newTxn(READ_ONLY, function (error, txn, stores) {
-      let collector = cursor.collector;
-      let collect = collector.collect.bind(collector);
-      FilterSearcherHelper.transact(self, txn, error, filter, reverse, collect);
-    }, [MESSAGE_STORE_NAME, PARTICIPANT_STORE_NAME]);
-
-    return cursor;
-  },
-
-  markMessageRead: function markMessageRead(messageId, value, aSendReadReport, aRequest) {
-    if (DEBUG) debug("Setting message " + messageId + " read to " + value);
-    this.newTxn(READ_WRITE, function (error, txn, stores) {
-      if (error) {
-        if (DEBUG) debug(error);
-        aRequest.notifyMarkMessageReadFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
-        return;
-      }
-
-      txn.onerror = function onerror(event) {
-        if (DEBUG) debug("Caught error on transaction ", event.target.errorCode);
-        aRequest.notifyMarkMessageReadFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
-      };
-
-      let messageStore = stores[0];
-      let threadStore = stores[1];
-      messageStore.get(messageId).onsuccess = function onsuccess(event) {
-        let messageRecord = event.target.result;
-        if (!messageRecord) {
-          if (DEBUG) debug("Message ID " + messageId + " not found");
-          aRequest.notifyMarkMessageReadFailed(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR);
-          return;
-        }
-
-        if (messageRecord.id != messageId) {
-          if (DEBUG) {
-            debug("Retrieve message ID (" + messageId + ") is " +
-                  "different from the one we got");
-          }
-          aRequest.notifyMarkMessageReadFailed(Ci.nsIMobileMessageCallback.UNKNOWN_ERROR);
-          return;
-        }
-
-        // If the value to be set is the same as the current message `read`
-        // value, we just notify successfully.
-        if (messageRecord.read == value) {
-          if (DEBUG) debug("The value of messageRecord.read is already " + value);
-          aRequest.notifyMessageMarkedRead(messageRecord.read);
-          return;
-        }
-
-        messageRecord.read = value ? FILTER_READ_READ : FILTER_READ_UNREAD;
-        messageRecord.readIndex = [messageRecord.read, messageRecord.timestamp];
-        let readReportMessageId, readReportTo;
-        if (aSendReadReport &&
-            messageRecord.type == "mms" &&
-            messageRecord.delivery == DELIVERY_RECEIVED &&
-            messageRecord.read == FILTER_READ_READ &&
-            !messageRecord.isReadReportSent) {
-          messageRecord.isReadReportSent = true;
-
-          let from = messageRecord.headers["from"];
-          readReportTo = from && from.address;
-          readReportMessageId = messageRecord.headers["message-id"];
-        }
-
-        if (DEBUG) debug("Message.read set to: " + value);
-        messageStore.put(messageRecord).onsuccess = function onsuccess(event) {
-          if (DEBUG) {
-            debug("Update successfully completed. Message: " +
-                  JSON.stringify(event.target.result));
-          }
-
-          // Now update the unread count.
-          let threadId = messageRecord.threadId;
-
-          threadStore.get(threadId).onsuccess = function(event) {
-            let threadRecord = event.target.result;
-            threadRecord.unreadCount += value ? -1 : 1;
-            if (DEBUG) {
-              debug("Updating unreadCount for thread id " + threadId + ": " +
-                    (value ?
-                     threadRecord.unreadCount + 1 :
-                     threadRecord.unreadCount - 1) +
-                     " -> " + threadRecord.unreadCount);
-            }
-            threadStore.put(threadRecord).onsuccess = function(event) {
-              if(readReportMessageId && readReportTo) {
-                gMMSService.sendReadReport(readReportMessageId,
-                                           readReportTo,
-                                           messageRecord.iccId);
-              }
-              aRequest.notifyMessageMarkedRead(messageRecord.read);
-            };
-          };
-        };
-      };
-    }, [MESSAGE_STORE_NAME, THREAD_STORE_NAME]);
+  deleteMessage: function(aMessageIds, aLength, aRequest) {
+    this.mmdb.deleteMessage(aMessageIds, aLength, aRequest);
   },
 
-  createThreadCursor: function createThreadCursor(callback) {
-    if (DEBUG) debug("Getting thread list");
-
-    let cursor = new GetThreadsCursor(this, callback);
-    this.newTxn(READ_ONLY, function (error, txn, threadStore) {
-      let collector = cursor.collector;
-      if (error) {
-        if (DEBUG) debug(error);
-        collector.collect(null, COLLECT_ID_ERROR, COLLECT_TIMESTAMP_UNUSED);
-        return;
-      }
-      txn.onerror = function onerror(event) {
-        if (DEBUG) debug("Caught error on transaction ", event.target.errorCode);
-        collector.collect(null, COLLECT_ID_ERROR, COLLECT_TIMESTAMP_UNUSED);
-      };
-      let request = threadStore.index("lastTimestamp").openKeyCursor();
-      request.onsuccess = function(event) {
-        let cursor = event.target.result;
-        if (cursor) {
-          if (collector.collect(txn, cursor.primaryKey, cursor.key)) {
-            cursor.continue();
-          }
-        } else {
-          collector.collect(txn, COLLECT_ID_END, COLLECT_TIMESTAMP_UNUSED);
-        }
-      };
-    }, [THREAD_STORE_NAME]);
-
-    return cursor;
-  }
-};
-
-let FilterSearcherHelper = {
-
-  /**
-   * @param index
-   *        The name of a message store index to filter on.
-   * @param range
-   *        A IDBKeyRange.
-   * @param direction
-   *        NEXT or PREV.
-   * @param txn
-   *        Ongoing IDBTransaction context object.
-   * @param collect
-   *        Result colletor function. It takes three parameters -- txn, message
-   *        id, and message timestamp.
-   */
-  filterIndex: function filterIndex(index, range, direction, txn, collect) {
-    let messageStore = txn.objectStore(MESSAGE_STORE_NAME);
-    let request = messageStore.index(index).openKeyCursor(range, direction);
-    request.onsuccess = function onsuccess(event) {
-      let cursor = event.target.result;
-      // Once the cursor has retrieved all keys that matches its key range,
-      // the filter search is done.
-      if (cursor) {
-        let timestamp = Array.isArray(cursor.key) ? cursor.key[1] : cursor.key;
-        if (collect(txn, cursor.primaryKey, timestamp)) {
-          cursor.continue();
-        }
-      } else {
-        collect(txn, COLLECT_ID_END, COLLECT_TIMESTAMP_UNUSED);
-      }
-    };
-    request.onerror = function onerror(event) {
-      if (DEBUG && event) debug("IDBRequest error " + event.target.errorCode);
-      collect(txn, COLLECT_ID_ERROR, COLLECT_TIMESTAMP_UNUSED);
-    };
-  },
-
-  /**
-   * Explicitly fiter message on the timestamp index.
-   *
-   * @param startDate
-   *        Timestamp of the starting date.
-   * @param endDate
-   *        Timestamp of the ending date.
-   * @param direction
-   *        NEXT or PREV.
-   * @param txn
-   *        Ongoing IDBTransaction context object.
-   * @param collect
-   *        Result colletor function. It takes three parameters -- txn, message
-   *        id, and message timestamp.
-   */
-  filterTimestamp: function filterTimestamp(startDate, endDate, direction, txn,
-                                            collect) {
-    let range = null;
-    if (startDate != null && endDate != null) {
-      range = IDBKeyRange.bound(startDate.getTime(), endDate.getTime());
-    } else if (startDate != null) {
-      range = IDBKeyRange.lowerBound(startDate.getTime());
-    } else if (endDate != null) {
-      range = IDBKeyRange.upperBound(endDate.getTime());
-    }
-    this.filterIndex("timestamp", range, direction, txn, collect);
+  createMessageCursor: function(aFilter, aReverse, aCallback) {
+    return this.mmdb.createMessageCursor(aFilter, aReverse, aCallback);
   },
 
-  /**
-   * Instanciate a filtering transaction.
-   *
-   * @param service
-   *        A MobileMessageDatabaseService. Used to create
-   * @param txn
-   *        Ongoing IDBTransaction context object.
-   * @param error
-   *        Previous error while creating the transaction.
-   * @param filter
-   *        A SmsFilter object.
-   * @param reverse
-   *        A boolean value indicating whether we should filter message in
-   *        reversed order.
-   * @param collect
-   *        Result colletor function. It takes three parameters -- txn, message
-   *        id, and message timestamp.
-   */
-  transact: function transact(service, txn, error, filter, reverse, collect) {
-    if (error) {
-      //TODO look at event.target.errorCode, pick appropriate error constant.
-      if (DEBUG) debug("IDBRequest error " + error.target.errorCode);
-      collect(txn, COLLECT_ID_ERROR, COLLECT_TIMESTAMP_UNUSED);
-      return;
-    }
-
-    let direction = reverse ? PREV : NEXT;
-
-    // We support filtering by date range only (see `else` block below) or by
-    // number/delivery status/read status with an optional date range.
-    if (filter.delivery == null &&
-        filter.numbers == null &&
-        filter.read == null &&
-        filter.threadId == null) {
-      // Filtering by date range only.
-      if (DEBUG) {
-        debug("filter.timestamp " + filter.startDate + ", " + filter.endDate);
-      }
-
-      this.filterTimestamp(filter.startDate, filter.endDate, direction, txn,
-                           collect);
-      return;
-    }
-
-    // Numeric 0 is smaller than any time stamp, and empty string is larger
-    // than all numeric values.
-    let startDate = 0, endDate = "";
-    if (filter.startDate != null) {
-      startDate = filter.startDate.getTime();
-    }
-    if (filter.endDate != null) {
-      endDate = filter.endDate.getTime();
-    }
-
-    let single, intersectionCollector;
-    {
-      let num = 0;
-      if (filter.delivery) num++;
-      if (filter.numbers) num++;
-      if (filter.read != undefined) num++;
-      if (filter.threadId != undefined) num++;
-      single = (num == 1);
-    }
-
-    if (!single) {
-      intersectionCollector = new IntersectionResultsCollector(collect, reverse);
-    }
+  markMessageRead: function(aMessageId, aValue, aSendReadReport, aRequest) {
+    this.mmdb.markMessageRead(aMessageId, aValue, aSendReadReport, aRequest);
+  },
 
-    // Retrieve the keys from the 'delivery' index that matches the value of
-    // filter.delivery.
-    if (filter.delivery) {
-      if (DEBUG) debug("filter.delivery " + filter.delivery);
-      let delivery = filter.delivery;
-      let range = IDBKeyRange.bound([delivery, startDate], [delivery, endDate]);
-      this.filterIndex("delivery", range, direction, txn,
-                       single ? collect : intersectionCollector.newContext());
-    }
-
-    // Retrieve the keys from the 'read' index that matches the value of
-    // filter.read.
-    if (filter.read != undefined) {
-      if (DEBUG) debug("filter.read " + filter.read);
-      let read = filter.read ? FILTER_READ_READ : FILTER_READ_UNREAD;
-      let range = IDBKeyRange.bound([read, startDate], [read, endDate]);
-      this.filterIndex("read", range, direction, txn,
-                       single ? collect : intersectionCollector.newContext());
-    }
-
-    // Retrieve the keys from the 'threadId' index that matches the value of
-    // filter.threadId.
-    if (filter.threadId != undefined) {
-      if (DEBUG) debug("filter.threadId " + filter.threadId);
-      let threadId = filter.threadId;
-      let range = IDBKeyRange.bound([threadId, startDate], [threadId, endDate]);
-      this.filterIndex("threadId", range, direction, txn,
-                       single ? collect : intersectionCollector.newContext());
-    }
-
-    // Retrieve the keys from the 'sender' and 'receiver' indexes that
-    // match the values of filter.numbers
-    if (filter.numbers) {
-      if (DEBUG) debug("filter.numbers " + filter.numbers.join(", "));
-
-      if (!single) {
-        collect = intersectionCollector.newContext();
-      }
-
-      let participantStore = txn.objectStore(PARTICIPANT_STORE_NAME);
-      service.findParticipantIdsByAddresses(participantStore, filter.numbers,
-                                            false, true,
-                                            (function (participantIds) {
-        if (!participantIds || !participantIds.length) {
-          // Oops! No such participant at all.
-
-          collect(txn, COLLECT_ID_END, COLLECT_TIMESTAMP_UNUSED);
-          return;
-        }
-
-        if (participantIds.length == 1) {
-          let id = participantIds[0];
-          let range = IDBKeyRange.bound([id, startDate], [id, endDate]);
-          this.filterIndex("participantIds", range, direction, txn, collect);
-          return;
-        }
-
-        let unionCollector = new UnionResultsCollector(collect);
-
-        this.filterTimestamp(filter.startDate, filter.endDate, direction, txn,
-                             unionCollector.newTimestampContext());
-
-        for (let i = 0; i < participantIds.length; i++) {
-          let id = participantIds[i];
-          let range = IDBKeyRange.bound([id, startDate], [id, endDate]);
-          this.filterIndex("participantIds", range, direction, txn,
-                           unionCollector.newContext());
-        }
-      }).bind(this));
-    }
+  createThreadCursor: function(aCallback) {
+    return this.mmdb.createThreadCursor(aCallback);
   }
 };
 
-function ResultsCollector() {
-  this.results = [];
-  this.done = false;
-}
-ResultsCollector.prototype = {
-  results: null,
-  requestWaiting: null,
-  done: null,
-
-  /**
-   * Queue up passed id, reply if necessary.
-   *
-   * @param txn
-   *        Ongoing IDBTransaction context object.
-   * @param id
-   *        COLLECT_ID_END(0) for no more results, COLLECT_ID_ERROR(-1) for
-   *        errors and valid otherwise.
-   * @param timestamp
-   *        We assume this function is always called in timestamp order. So
-   *        this parameter is actually unused.
-   *
-   * @return true if expects more. false otherwise.
-   */
-  collect: function collect(txn, id, timestamp) {
-    if (this.done) {
-      return false;
-    }
-
-    if (DEBUG) {
-      debug("collect: message ID = " + id);
-    }
-    if (id) {
-      // Queue up any id but '0' and replies later accordingly.
-      this.results.push(id);
-    }
-    if (id <= 0) {
-      // No more processing on '0' or negative values passed.
-      this.done = true;
-    }
-
-    if (!this.requestWaiting) {
-      if (DEBUG) debug("Cursor.continue() not called yet");
-      return !this.done;
-    }
-
-    // We assume there is only one request waiting throughout the message list
-    // retrieving process. So we don't bother continuing to process further
-    // waiting requests here. This assumption comes from DOMCursor::Continue()
-    // implementation.
-    let callback = this.requestWaiting;
-    this.requestWaiting = null;
-
-    this.drip(txn, callback);
-
-    return !this.done;
-  },
-
-  /**
-   * Callback right away with the first queued result entry if the filtering is
-   * done. Or queue up the request and callback when a new entry is available.
-   *
-   * @param callback
-   *        A callback function that accepts a numeric id.
-   */
-  squeeze: function squeeze(callback) {
-    if (this.requestWaiting) {
-      throw new Error("Already waiting for another request!");
-    }
-
-    if (!this.done) {
-      // Database transaction ongoing, let it reply for us so that we won't get
-      // blocked by the existing transaction.
-      this.requestWaiting = callback;
-      return;
-    }
-
-    this.drip(null, callback);
-  },
-
-  /**
-   * @param txn
-   *        Ongoing IDBTransaction context object or null.
-   * @param callback
-   *        A callback function that accepts a numeric id.
-   */
-  drip: function drip(txn, callback) {
-    if (!this.results.length) {
-      if (DEBUG) debug("No messages matching the filter criteria");
-      callback(txn, COLLECT_ID_END);
-      return;
-    }
-
-    if (this.results[0] < 0) {
-      // An previous error found. Keep the answer in results so that we can
-      // reply INTERNAL_ERROR for further requests.
-      if (DEBUG) debug("An previous error found");
-      callback(txn, COLLECT_ID_ERROR);
-      return;
-    }
-
-    let firstMessageId = this.results.shift();
-    callback(txn, firstMessageId);
-  }
-};
-
-function IntersectionResultsCollector(collect, reverse) {
-  this.cascadedCollect = collect;
-  this.reverse = reverse;
-  this.contexts = [];
-}
-IntersectionResultsCollector.prototype = {
-  cascadedCollect: null,
-  reverse: false,
-  contexts: null,
-
-  /**
-   * Queue up {id, timestamp} pairs, find out intersections and report to
-   * |cascadedCollect|. Return true if it is still possible to have another match.
-   */
-  collect: function collect(contextIndex, txn, id, timestamp) {
-    if (DEBUG) {
-      debug("IntersectionResultsCollector: "
-            + contextIndex + ", " + id + ", " + timestamp);
-    }
-
-    let contexts = this.contexts;
-    let context = contexts[contextIndex];
-
-    if (id < 0) {
-      // Act as no more matched records.
-      id = 0;
-    }
-    if (!id) {
-      context.done = true;
-
-      if (!context.results.length) {
-        // Already empty, can't have further intersection results.
-        return this.cascadedCollect(txn, COLLECT_ID_END, COLLECT_TIMESTAMP_UNUSED);
-      }
-
-      for (let i = 0; i < contexts.length; i++) {
-        if (!contexts[i].done) {
-          // Don't call |this.cascadedCollect| because |context.results| might not
-          // be empty, so other contexts might still have a chance here.
-          return false;
-        }
-      }
-
-      // It was the last processing context and is no more processing.
-      return this.cascadedCollect(txn, COLLECT_ID_END, COLLECT_TIMESTAMP_UNUSED);
-    }
-
-    // Search id in other existing results. If no other results has it,
-    // and A) the last timestamp is smaller-equal to current timestamp,
-    // we wait for further results; either B) record timestamp is larger
-    // then current timestamp or C) no more processing for a filter, then we
-    // drop this id because there can't be a match anymore.
-    for (let i = 0; i < contexts.length; i++) {
-      if (i == contextIndex) {
-        continue;
-      }
-
-      let ctx = contexts[i];
-      let results = ctx.results;
-      let found = false;
-      for (let j = 0; j < results.length; j++) {
-        let result = results[j];
-        if (result.id == id) {
-          found = true;
-          break;
-        }
-        if ((!this.reverse && (result.timestamp > timestamp)) ||
-            (this.reverse && (result.timestamp < timestamp))) {
-          // B) Cannot find a match anymore. Drop.
-          return true;
-        }
-      }
-
-      if (!found) {
-        if (ctx.done) {
-          // C) Cannot find a match anymore. Drop.
-          if (results.length) {
-            let lastResult = results[results.length - 1];
-            if ((!this.reverse && (lastResult.timestamp >= timestamp)) ||
-                (this.reverse && (lastResult.timestamp <= timestamp))) {
-              // Still have a chance to get another match. Return true.
-              return true;
-            }
-          }
-
-          // Impossible to find another match because all results in ctx have
-          // timestamps smaller than timestamp.
-          context.done = true;
-          return this.cascadedCollect(txn, COLLECT_ID_END, COLLECT_TIMESTAMP_UNUSED);
-        }
-
-        // A) Pending.
-        context.results.push({
-          id: id,
-          timestamp: timestamp
-        });
-        return true;
-      }
-    }
-
-    // Now id is found in all other results. Report it.
-    return this.cascadedCollect(txn, id, timestamp);
-  },
-
-  newContext: function newContext() {
-    let contextIndex = this.contexts.length;
-    this.contexts.push({
-      results: [],
-      done: false
-    });
-    return this.collect.bind(this, contextIndex);
-  }
-};
-
-function UnionResultsCollector(collect) {
-  this.cascadedCollect = collect;
-  this.contexts = [{
-    // Timestamp.
-    processing: 1,
-    results: []
-  }, {
-    processing: 0,
-    results: []
-  }];
-}
-UnionResultsCollector.prototype = {
-  cascadedCollect: null,
-  contexts: null,
-
-  collect: function collect(contextIndex, txn, id, timestamp) {
-    if (DEBUG) {
-      debug("UnionResultsCollector: "
-            + contextIndex + ", " + id + ", " + timestamp);
-    }
-
-    let contexts = this.contexts;
-    let context = contexts[contextIndex];
-
-    if (id < 0) {
-      // Act as no more matched records.
-      id = 0;
-    }
-    if (id) {
-      if (!contextIndex) {
-        // Timestamp.
-        context.results.push({
-          id: id,
-          timestamp: timestamp
-        });
-      } else {
-        context.results.push(id);
-      }
-      return true;
-    }
-
-    context.processing -= 1;
-    if (contexts[0].processing || contexts[1].processing) {
-      // At least one queue is still processing, but we got here because
-      // current cursor gives 0 as id meaning no more messages are
-      // available. Return false here to stop further cursor.continue() calls.
-      return false;
-    }
-
-    let tres = contexts[0].results;
-    let qres = contexts[1].results;
-    tres = tres.filter(function (element) {
-      return qres.indexOf(element.id) != -1;
-    });
-
-    for (let i = 0; i < tres.length; i++) {
-      this.cascadedCollect(txn, tres[i].id, tres[i].timestamp);
-    }
-    this.cascadedCollect(txn, COLLECT_ID_END, COLLECT_TIMESTAMP_UNUSED);
-
-    return false;
-  },
-
-  newTimestampContext: function newTimestampContext() {
-    return this.collect.bind(this, 0);
-  },
-
-  newContext: function newContext() {
-    this.contexts[1].processing++;
-    return this.collect.bind(this, 1);
-  }
-};
-
-function GetMessagesCursor(service, callback) {
-  this.service = service;
-  this.callback = callback;
-  this.collector = new ResultsCollector();
-
-  this.handleContinue(); // Trigger first run.
-}
-GetMessagesCursor.prototype = {
-  classID: RIL_GETMESSAGESCURSOR_CID,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsICursorContinueCallback]),
-
-  service: null,
-  callback: null,
-  collector: null,
-
-  getMessageTxn: function getMessageTxn(messageStore, messageId) {
-    if (DEBUG) debug ("Fetching message " + messageId);
-
-    let getRequest = messageStore.get(messageId);
-    let self = this;
-    getRequest.onsuccess = function onsuccess(event) {
-      if (DEBUG) {
-        debug("notifyNextMessageInListGot - messageId: " + messageId);
-      }
-      let domMessage =
-        self.service.createDomMessageFromRecord(event.target.result);
-      self.callback.notifyCursorResult(domMessage);
-    };
-    getRequest.onerror = function onerror(event) {
-      if (DEBUG) {
-        debug("notifyCursorError - messageId: " + messageId);
-      }
-      self.callback.notifyCursorError(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
-    };
-  },
-
-  notify: function notify(txn, messageId) {
-    if (!messageId) {
-      this.callback.notifyCursorDone();
-      return;
-    }
-
-    if (messageId < 0) {
-      this.callback.notifyCursorError(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
-      return;
-    }
-
-    // When filter transaction is not yet completed, we're called with current
-    // ongoing transaction object.
-    if (txn) {
-      let messageStore = txn.objectStore(MESSAGE_STORE_NAME);
-      this.getMessageTxn(messageStore, messageId);
-      return;
-    }
-
-    // Or, we have to open another transaction ourselves.
-    let self = this;
-    this.service.newTxn(READ_ONLY, function (error, txn, messageStore) {
-      if (error) {
-        self.callback.notifyCursorError(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
-        return;
-      }
-      self.getMessageTxn(messageStore, messageId);
-    }, [MESSAGE_STORE_NAME]);
-  },
-
-  // nsICursorContinueCallback
-
-  handleContinue: function handleContinue() {
-    if (DEBUG) debug("Getting next message in list");
-    this.collector.squeeze(this.notify.bind(this));
-  }
-};
-
-function GetThreadsCursor(service, callback) {
-  this.service = service;
-  this.callback = callback;
-  this.collector = new ResultsCollector();
-
-  this.handleContinue(); // Trigger first run.
-}
-GetThreadsCursor.prototype = {
-  classID: RIL_GETTHREADSCURSOR_CID,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsICursorContinueCallback]),
-
-  service: null,
-  callback: null,
-  collector: null,
-
-  getThreadTxn: function getThreadTxn(threadStore, threadId) {
-    if (DEBUG) debug ("Fetching thread " + threadId);
-
-    let getRequest = threadStore.get(threadId);
-    let self = this;
-    getRequest.onsuccess = function onsuccess(event) {
-      let threadRecord = event.target.result;
-      if (DEBUG) {
-        debug("notifyCursorResult: " + JSON.stringify(threadRecord));
-      }
-      let thread =
-        gMobileMessageService.createThread(threadRecord.id,
-                                           threadRecord.participantAddresses,
-                                           threadRecord.lastTimestamp,
-                                           threadRecord.lastMessageSubject || "",
-                                           threadRecord.body,
-                                           threadRecord.unreadCount,
-                                           threadRecord.lastMessageType);
-      self.callback.notifyCursorResult(thread);
-    };
-    getRequest.onerror = function onerror(event) {
-      if (DEBUG) {
-        debug("notifyCursorError - threadId: " + threadId);
-      }
-      self.callback.notifyCursorError(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
-    };
-  },
-
-  notify: function notify(txn, threadId) {
-    if (!threadId) {
-      this.callback.notifyCursorDone();
-      return;
-    }
-
-    if (threadId < 0) {
-      this.callback.notifyCursorError(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
-      return;
-    }
-
-    // When filter transaction is not yet completed, we're called with current
-    // ongoing transaction object.
-    if (txn) {
-      let threadStore = txn.objectStore(THREAD_STORE_NAME);
-      this.getThreadTxn(threadStore, threadId);
-      return;
-    }
-
-    // Or, we have to open another transaction ourselves.
-    let self = this;
-    this.service.newTxn(READ_ONLY, function (error, txn, threadStore) {
-      if (error) {
-        self.callback.notifyCursorError(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
-        return;
-      }
-      self.getThreadTxn(threadStore, threadId);
-    }, [THREAD_STORE_NAME]);
-  },
-
-  // nsICursorContinueCallback
-
-  handleContinue: function handleContinue() {
-    if (DEBUG) debug("Getting next thread in list");
-    this.collector.squeeze(this.notify.bind(this));
-  }
-}
-
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MobileMessageDatabaseService]);
-
-function debug() {
-  dump("MobileMessageDatabaseService: " + Array.slice(arguments).join(" ") + "\n");
-}
--- a/dom/mobilemessage/src/moz.build
+++ b/dom/mobilemessage/src/moz.build
@@ -17,16 +17,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'andr
     SOURCES += [
         'android/MobileMessageDatabaseService.cpp',
         'android/SmsService.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_B2G_RIL']:
     EXTRA_JS_MODULES = [
         'gonk/mms_consts.js',
         'gonk/MmsPduHelper.jsm',
+        'gonk/MobileMessageDB.jsm',
         'gonk/wap_consts.js',
         'gonk/WspPduHelper.jsm',
     ]
     EXTRA_COMPONENTS += [
         'gonk/MmsService.js',
         'gonk/MmsService.manifest',
         'gonk/MobileMessageDatabaseService.js',
         'gonk/MobileMessageDatabaseService.manifest',
--- a/dom/mobilemessage/tests/marionette/head.js
+++ b/dom/mobilemessage/tests/marionette/head.js
@@ -3,17 +3,17 @@
 
 const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers;
 
 let Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise;
 
 /* Push required permissions and test if |navigator.mozMobileMessage| exists.
  * Resolve if it does, reject otherwise.
  *
- * Forfill params:
+ * Fulfill params:
  *   manager -- an reference to navigator.mozMobileMessage.
  *
  * Reject params: (none)
  *
  * @return A deferred promise.
  */
 let manager;
 function ensureMobileMessage() {
@@ -42,17 +42,17 @@ function ensureMobileMessage() {
   });
 
   return deferred.promise;
 }
 
 /* Send a SMS message to a single receiver.  Resolve if it succeeds, reject
  * otherwise.
  *
- * Forfill params:
+ * Fulfill params:
  *   message -- the sent SmsMessage.
  *
  * Reject params:
  *   error -- a DOMError.
  *
  * @param aReceiver the address of the receiver.
  * @param aText the text body of the message.
  *
@@ -70,17 +70,17 @@ function sendSmsWithSuccess(aReceiver, a
   };
 
   return deferred.promise;
 }
 
 /* Send a MMS message with specified parameters.  Resolve if it fails, reject
  * otherwise.
  *
- * Forfill params:
+ * Fulfill params:
  *   message -- the failed MmsMessage
  *
  * Reject params: (none)
  *
  * @param aMmsParameters a MmsParameters instance.
  *
  * @return A deferred promise.
  */
@@ -97,17 +97,17 @@ function sendMmsWithFailure(aMmsParamete
     deferred.reject();
   };
 
   return deferred.promise;
 }
 
 /* Retrieve messages from database.
  *
- * Forfill params:
+ * Fulfill params:
  *   messages -- an array of {Sms,Mms}Message instances.
  *
  * Reject params:
  *   event -- a DOMEvent
  *
  * @param aFilter an optional MozSmsFilter instance.
  * @param aReverse a boolean value indicating whether the order of the messages
  *                 should be reversed.
@@ -133,31 +133,31 @@ function getMessages(aFilter, aReverse) 
   };
   cursor.onerror = deferred.reject.bind(deferred);
 
   return deferred.promise;
 }
 
 /* Retrieve all messages from database.
  *
- * Forfill params:
+ * Fulfill params:
  *   messages -- an array of {Sms,Mms}Message instances.
  *
  * Reject params:
  *   event -- a DOMEvent
  *
  * @return A deferred promise.
  */
 function getAllMessages() {
   return getMessages(null, false);
 }
 
 /* Retrieve all threads from database.
  *
- * Forfill params:
+ * Fulfill params:
  *   threads -- an array of MozMobileMessageThread instances.
  *
  * Reject params:
  *   event -- a DOMEvent
  *
  * @return A deferred promise.
  */
 function getAllThreads() {
@@ -176,17 +176,17 @@ function getAllThreads() {
   };
   cursor.onerror = deferred.reject.bind(deferred);
 
   return deferred.promise;
 }
 
 /* Retrieve a single specified thread from database.
  *
- * Forfill params:
+ * Fulfill params:
  *   thread -- a MozMobileMessageThread instance.
  *
  * Reject params:
  *   event -- a DOMEvent if an error occurs in the retrieving process, or
  *            undefined if there's no such thread.
  *
  * @aThreadId a numeric value identifying the target thread.
  *
@@ -201,17 +201,17 @@ function getThreadById(aThreadId) {
         }
       }
       throw undefined;
     });
 }
 
 /* Delete messages specified from database.
  *
- * Forfill params:
+ * Fulfill params:
  *   result -- an array of boolean values indicating whether delesion was
  *             actually performed on the message record with corresponding id.
  *
  * Reject params:
  *   event -- a DOMEvent.
  *
  * @aMessageId an array of numeric values identifying the target messages.
  *
@@ -231,17 +231,17 @@ function deleteMessagesById(aMessageIds)
   };
   request.onerror = deferred.reject.bind(deferred);
 
   return deferred.promise;
 }
 
 /* Delete messages specified from database.
  *
- * Forfill params:
+ * Fulfill params:
  *   result -- an array of boolean values indicating whether delesion was
  *             actually performed on the message record with corresponding id.
  *
  * Reject params:
  *   event -- a DOMEvent.
  *
  * @aMessages an array of {Sms,Mms}Message instances.
  *
@@ -249,17 +249,17 @@ function deleteMessagesById(aMessageIds)
  */
 function deleteMessages(aMessages) {
   let ids = messagesToIds(aMessages);
   return deleteMessagesById(ids);
 }
 
 /* Delete all messages from database.
  *
- * Forfill params:
+ * Fulfill params:
  *   ids -- an array of numeric values identifying those deleted
  *          {Sms,Mms}Messages.
  *
  * Reject params:
  *   event -- a DOMEvent.
  *
  * @return A deferred promise.
  */
@@ -270,17 +270,17 @@ function deleteAllMessages() {
 let pendingEmulatorCmdCount = 0;
 
 /* Send emulator command with safe guard.
  *
  * We should only call |finish()| after all emulator command transactions
  * end, so here comes with the pending counter.  Resolve when the emulator
  * gives positive response, and reject otherwise.
  *
- * Forfill params:
+ * Fulfill params:
  *   result -- an array of emulator response lines.
  *
  * Reject params:
  *   result -- an array of emulator response lines.
  *
  * @return A deferred promise.
  */
 function runEmulatorCmdSafe(aCommand) {
@@ -298,75 +298,155 @@ function runEmulatorCmdSafe(aCommand) {
     }
   });
 
   return deferred.promise;
 }
 
 /* Send simple text SMS to emulator.
  *
- * Forfill params:
+ * Fulfill params:
  *   result -- an array of emulator response lines.
  *
  * Reject params:
  *   result -- an array of emulator response lines.
  *
  * @return A deferred promise.
  */
 function sendTextSmsToEmulator(aFrom, aText) {
   let command = "sms send " + aFrom + " " + aText;
   return runEmulatorCmdSafe(command);
 }
 
 /* Send raw SMS TPDU to emulator.
  *
- * Forfill params:
+ * Fulfill params:
  *   result -- an array of emulator response lines.
  *
  * Reject params:
  *   result -- an array of emulator response lines.
  *
  * @return A deferred promise.
  */
 function sendRawSmsToEmulator(aPdu) {
   let command = "sms pdu " + aPdu;
   return runEmulatorCmdSafe(command);
 }
 
+/* Name space for MobileMessageDB.jsm.  Only initialized after first call to
+ * newMobileMessageDB.
+ */
+let MMDB;
+
+// Create a new MobileMessageDB instance.
+function newMobileMessageDB() {
+  if (!MMDB) {
+    MMDB = Cu.import("resource://gre/modules/MobileMessageDB.jsm", {});
+    is(typeof MMDB.MobileMessageDB, "function", "MMDB.MobileMessageDB");
+  }
+
+  let mmdb = new MMDB.MobileMessageDB();
+  ok(mmdb, "MobileMessageDB instance");
+  return mmdb;
+}
+
+/* Initialize a MobileMessageDB.  Resolve if initialized with success, reject
+ * otherwise.
+ *
+ * Fulfill params: a MobileMessageDB instance.
+ * Reject params: a MobileMessageDB instance.
+ *
+ * @param aMmdb
+ *        A MobileMessageDB instance.
+ * @param aDbName
+ *        A string name for that database.
+ * @param aDbVersion
+ *        The version that MobileMessageDB should upgrade to. 0 for the lastest
+ *        version.
+ *
+ * @return A deferred promise.
+ */
+function initMobileMessageDB(aMmdb, aDbName, aDbVersion) {
+  let deferred = Promise.defer();
+
+  aMmdb.init(aDbName, aDbVersion, function(aError) {
+    if (aError) {
+      deferred.reject(aMmdb);
+    } else {
+      deferred.resolve(aMmdb);
+    }
+  });
+
+  return deferred.promise;
+}
+
+/* Close a MobileMessageDB.
+ *
+ * @return The passed MobileMessageDB instance.
+ */
+function closeMobileMessageDB(aMmdb) {
+  aMmdb.close();
+  return aMmdb;
+}
+
 /* Create a new array of id attribute of input messages.
  *
  * @param aMessages an array of {Sms,Mms}Message instances.
  *
  * @return an array of numeric values.
  */
 function messagesToIds(aMessages) {
   let ids = [];
   for (let message of aMessages) {
     ids.push(message.id);
   }
   return ids;
 }
 
+// A reference to a nsIUUIDGenerator service.
+let uuidGenerator;
+
+/* Generate a new UUID.
+ *
+ * @return A UUID string.
+ */
+function newUUID() {
+  if (!uuidGenerator) {
+    uuidGenerator = Cc["@mozilla.org/uuid-generator;1"]
+                    .getService(Ci.nsIUUIDGenerator);
+    ok(uuidGenerator, "uuidGenerator");
+  }
+
+  return uuidGenerator.generateUUID().toString();
+}
+
 /* Flush permission settings and call |finish()|.
  */
 function cleanUp() {
   waitFor(function() {
     SpecialPowers.flushPermissions(function() {
       // Use ok here so that we have at least one test run.
       ok(true, "permissions flushed");
 
       finish();
     });
   }, function() {
     return pendingEmulatorCmdCount === 0;
   });
 }
 
+function startTestBase(aTestCaseMain) {
+  Promise.resolve()
+         .then(aTestCaseMain)
+         .then(cleanUp, function() {
+           ok(false, 'promise rejects during test.');
+           cleanUp();
+         });
+}
+
 function startTestCommon(aTestCaseMain) {
-  ensureMobileMessage()
-    .then(deleteAllMessages)
-    .then(aTestCaseMain)
-    .then(deleteAllMessages)
-    .then(cleanUp, function() {
-      ok(false, 'promise rejects during test.');
-      cleanUp();
-    });
+  startTestBase(function() {
+    return ensureMobileMessage()
+      .then(deleteAllMessages)
+      .then(aTestCaseMain)
+      .then(deleteAllMessages);
+  });
 }
--- a/dom/mobilemessage/tests/marionette/manifest.ini
+++ b/dom/mobilemessage/tests/marionette/manifest.ini
@@ -34,10 +34,11 @@ qemu = true
 [test_getsegmentinfofortext.js]
 [test_phone_number_normalization.js]
 [test_invalid_address.js]
 [test_mmsmessage_attachments.js]
 [test_getthreads.js]
 [test_smsc_address.js]
 [test_dsds_default_service_id.js]
 [test_thread_subject.js]
+[test_mmdb_new.js]
 [test_mmdb_setmessagedeliverybyid_sms.js]
 [test_replace_short_message_type.js]
new file mode 100644
--- /dev/null
+++ b/dom/mobilemessage/tests/marionette/test_mmdb_new.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+startTestBase(function testCaseMain() {
+  log("Test init MobileMessageDB");
+
+  let mmdb = newMobileMessageDB();
+  let dbName = "test_mmdb_new";
+  let dbVersion = 0;
+  let check = function() {
+    is(mmdb.dbName, dbName, "dbName");
+    if (!dbVersion) {
+      ok(mmdb.dbVersion, "dbVersion");
+      dbVersion = mmdb.dbVersion;
+    } else {
+      is(mmdb.dbVersion, dbVersion, "dbVersion");
+    }
+  };
+
+  return initMobileMessageDB(mmdb, dbName, dbVersion)
+    .then(check)
+    .then(closeMobileMessageDB.bind(null, mmdb))
+    .then(check)
+    .then(function() {
+      log("Test re-init and close.");
+      return initMobileMessageDB(mmdb, dbName, dbVersion);
+    })
+    .then(check)
+    .then(closeMobileMessageDB.bind(null, mmdb))
+    .then(check);
+});
--- a/dom/plugins/base/android/ANPSystem.cpp
+++ b/dom/plugins/base/android/ANPSystem.cpp
@@ -45,43 +45,30 @@ anp_system_getApplicationDataDirectory(N
 }
 
 const char*
 anp_system_getApplicationDataDirectory()
 {
   return anp_system_getApplicationDataDirectory(nullptr);
 }
 
-jclass anp_system_loadJavaClass(NPP instance, const char* className)
+jclass anp_system_loadJavaClass(NPP instance, const char* classNameStr)
 {
   LOG("%s", __PRETTY_FUNCTION__);
 
-  JNIEnv* env = GetJNIForThread();
-  if (!env)
-    return nullptr;
-
-  jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell");
-  jmethodID method = env->GetStaticMethodID(cls,
-                                            "loadPluginClass",
-                                            "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Class;");
-
-  // pass libname and classname, gotta create java strings
   nsNPAPIPluginInstance* pinst = static_cast<nsNPAPIPluginInstance*>(instance->ndata);
   mozilla::PluginPRLibrary* lib = static_cast<mozilla::PluginPRLibrary*>(pinst->GetPlugin()->GetLibrary());
 
-  nsCString libName;
-  lib->GetLibraryPath(libName);
+  NS_ConvertUTF8toUTF16 className(classNameStr);
 
-  jstring jclassName = env->NewStringUTF(className);
-  jstring jlibName = env->NewStringUTF(libName.get());
-  jobject obj = env->CallStaticObjectMethod(cls, method, jclassName, jlibName);
-  env->DeleteLocalRef(jlibName);
-  env->DeleteLocalRef(jclassName);
-  env->DeleteLocalRef(cls);
-  return reinterpret_cast<jclass>(obj);
+  nsCString libNameUtf8;
+  lib->GetLibraryPath(libNameUtf8);
+  NS_ConvertUTF8toUTF16 libName(libNameUtf8);
+
+  return GeckoAppShell::LoadPluginClass(className, libName);
 }
 
 void anp_system_setPowerState(NPP instance, ANPPowerState powerState)
 {
   nsNPAPIPluginInstance* pinst = nsNPAPIPluginInstance::GetFromNPP(instance);
 
   if (pinst) {
     pinst->SetWakeLock(powerState == kScreenOn_ANPPowerState);
--- a/dom/src/geolocation/nsGeolocation.cpp
+++ b/dom/src/geolocation/nsGeolocation.cpp
@@ -10,17 +10,16 @@
 #include "nsISettingsService.h"
 
 #include "nsGeolocation.h"
 #include "nsDOMClassInfoID.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsContentPermissionHelper.h"
-#include "nsCxPusher.h"
 #include "nsIDocument.h"
 #include "nsIObserverService.h"
 #include "nsPIDOMWindow.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ClearOnShutdown.h"
@@ -290,20 +289,16 @@ JSObject*
 PositionError::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return PositionErrorBinding::Wrap(aCx, aScope, this);
 }
 
 void
 PositionError::NotifyCallback(const GeoPositionErrorCallback& aCallback)
 {
-  // Ensure that the proper context is on the stack (bug 452762)
-  nsCxPusher pusher;
-  pusher.PushNull();
-
   nsAutoMicroTask mt;
   if (aCallback.HasWebIDLCallback()) {
     PositionErrorCallback* callback = aCallback.GetWebIDLCallback();
 
     if (callback) {
       ErrorResult err;
       callback->Call(*this, err);
     }
@@ -525,19 +520,16 @@ nsGeolocationRequest::SendLocation(nsIDO
 
   mLocator->SetCachedPosition(wrapped);
   if (!mIsWatchPositionRequest) {
     // Cancel timer and position updates in case the position
     // callback spins the event loop
     Shutdown();
   }
 
-  // Ensure that the proper context is on the stack (bug 452762)
-  nsCxPusher pusher;
-  pusher.PushNull();
   nsAutoMicroTask mt;
   if (mCallback.HasWebIDLCallback()) {
     ErrorResult err;
     PositionCallback* callback = mCallback.GetWebIDLCallback();
 
     MOZ_ASSERT(callback);
     callback->Call(*wrapped, err);
   } else {
deleted file mode 100644
--- a/extensions/widgetutils/Makefile.in
+++ /dev/null
@@ -1,18 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-
-INSTALL_EXTENSION_ID = widgetutils@extensions.mozilla.org
-DIST_FILES = install.rdf
-USE_EXTENSION_MANIFEST = 1
-XPI_PKGNAME            = widgetutils-$(MOZ_APP_VERSION)
-
-XULAPP_DEFINES = \
-	-DTHUNDERBIRD_VERSION=$(THUNDERBIRD_VERSION) \
-	-DEXTENSION_VERSION=$(MOZILLA_VERSION) \
-	$(NULL)
-
-ifdef TARGET_XPCOM_ABI
-XULAPP_DEFINES += -DEM_ABI=$(OS_TARGET)_$(TARGET_XPCOM_ABI)
-endif
deleted file mode 100644
--- a/extensions/widgetutils/install.rdf
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-
-#filter substitution
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>widgetutils@extensions.mozilla.org</em:id>
-    <em:version>@EXTENSION_VERSION@</em:version>
-
-#ifdef EM_ABI
-    <em:targetPlatform>@EM_ABI@</em:targetPlatform>
-#endif
-
-    <em:targetApplication>
-      <!-- Firefox -->
-      <Description>
-        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
-        <em:minVersion>@FIREFOX_VERSION@</em:minVersion>
-        <em:maxVersion>@FIREFOX_VERSION@</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-    <em:targetApplication>
-      <!-- Thunderbird -->
-      <Description>
-        <em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id>
-        <em:minVersion>@THUNDERBIRD_VERSION@</em:minVersion>
-        <em:maxVersion>@THUNDERBIRD_VERSION@</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-    <!-- front-end metadata -->
-    <em:name>WidgetUtils service</em:name>
-    <em:description>Provide scrolling by mouse feature.</em:description>
-    <em:creator>mozilla.org</em:creator>
-    <em:contributor>Oleg Romashin &lt;romaxa@gmail.com&gt;</em:contributor>
-    <em:homepageURL>http://hg.mozilla.org</em:homepageURL>
-  </Description>
-</RDF>
deleted file mode 100644
--- a/extensions/widgetutils/moz.build
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-DIRS += ['src']
-
-XPI_NAME = 'widgetutils'
deleted file mode 100644
--- a/extensions/widgetutils/src/Makefile.in
+++ /dev/null
@@ -1,10 +0,0 @@
-# vim:set ts=8 sw=8 sts=8 noet:
-# 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/.
-
-EXTRA_DSO_LDOPTS = \
-  $(XPCOM_GLUE_LDOPTS) \
-  $(NSPR_LIBS) \
-  $(MOZ_COMPONENT_LIBS) \
-  $(NULL)
deleted file mode 100644
--- a/extensions/widgetutils/src/moz.build
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-SOURCES += [
-    'nsWidgetUtils.cpp',
-]
-
-LIBRARY_NAME = 'widgetutils'
-
-IS_COMPONENT = True
-
-XPI_NAME = 'widgetutils'
-
-if CONFIG['TARGET_XPCOM_ABI']:
-    FINAL_TARGET += '/platform/%(OS_TARGET)s_%(TARGET_XPCOM_ABI)s' % CONFIG
-
-FORCE_SHARED_LIB = True
deleted file mode 100644
--- a/extensions/widgetutils/src/nsWidgetUtils.cpp
+++ /dev/null
@@ -1,537 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 tw=80 et cindent: */
-/* 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/. */
-
-#include "nsCURILoader.h"
-#include "nsICategoryManager.h"
-#include "nsIDOMDocument.h"
-#include "nsIDOMHTMLElement.h"
-#include "nsIDOMHTMLIFrameElement.h"
-#include "nsIDOMNode.h"
-#include "nsIDOMNodeList.h"
-#include "nsIDOMWindow.h"
-#include "nsIDOMWindowCollection.h"
-#include "nsIDocument.h"
-#include "nsIGenericFactory.h"
-#include "nsIObserver.h"
-#include "nsIPresShell.h"
-#include "nsIStyleSheetService.h"
-#include "nsIWebProgress.h"
-#include "nsIWebProgressListener.h"
-#include "nsIWindowWatcher.h"
-#include "nsNetUtil.h"
-#include "nsRect.h"
-#include "nsString.h"
-#include "nsWeakReference.h"
-#include "nsIWebBrowser.h"
-#include "nsIObserverService.h"
-#include "nsPIDOMWindow.h"
-#include "nsIDOMWindow.h"
-#include "nsIDOMCompositionListener.h"
-#include "nsIDOMTextListener.h"
-#include "nsIDOMMouseEvent.h"
-#include "nsIDOMWheelEvent.h"
-#include "nsView.h"
-#include "nsViewManager.h"
-#include "nsIContentPolicy.h"
-#include "nsIDocShellTreeItem.h"
-#include "nsIContent.h"
-#include "nsITimer.h"
-#include "mozilla/MouseEvents.h"
-
-using namespace mozilla;
-
-const int MIN_INT =((int) (1 << (sizeof(int) * 8 - 1)));
-
-static int g_lastX=MIN_INT;
-static int g_lastY=MIN_INT;
-static int32_t g_panning = 0;
-static bool g_is_scrollable = false;
-
-#define EM_MULT 16.
-#define NS_FRAME_HAS_RELATIVE_SIZE 0x01000000
-#define NS_FRAME_HAS_OPTIMIZEDVIEW 0x02000000
-#define BEHAVIOR_ACCEPT nsIPermissionManager::ALLOW_ACTION
-#define BEHAVIOR_REJECT nsIPermissionManager::DENY_ACTION
-#define BEHAVIOR_NOFOREIGN 3
-#define NUMBER_OF_TYPES 13
-
-// TODO auto reload nsWidgetUtils in C.
-class nsWidgetUtils : public nsIObserver,
-                      public nsIDOMEventListener,
-                      public nsIContentPolicy,
-                      public nsSupportsWeakReference
-{
-public:
-  nsWidgetUtils();
-  virtual ~nsWidgetUtils();
-
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIDOMEVENTLISTENER
-  NS_DECL_NSIOBSERVER 
-  NS_DECL_NSICONTENTPOLICY
-
-private:
-  nsresult Init(void);
-  void RemoveWindowListeners(nsIDOMWindow *aDOMWin);
-  EventTarget* GetChromeEventHandler(nsIDOMWindow *aDOMWin);
-  void AttachWindowListeners(nsIDOMWindow *aDOMWin);
-  bool IsXULNode(nsIDOMNode *aNode, uint32_t *aType = 0);
-  nsresult GetDOMWindowByNode(nsIDOMNode *aNode, nsIDOMWindow * *aDOMWindow);
-  nsresult UpdateFromEvent(nsIDOMEvent *aDOMEvent);
-  nsresult MouseDown(nsIDOMEvent* aDOMEvent);
-  nsresult MouseUp(nsIDOMEvent* aDOMEvent);
-  nsresult MouseMove(nsIDOMEvent* aDOMEvent);
-
-  static void StopPanningCallback(nsITimer *timer, void *closure);
-
-  nsCOMPtr<nsIWidget> mWidget;
-  nsRefPtr<nsViewManager> mViewManager;
-  nsCOMPtr<nsITimer> mTimer;
-};
-
-nsWidgetUtils::nsWidgetUtils()
-{
-  Init();
-}
-
-NS_IMETHODIMP
-nsWidgetUtils::Init()
-{
-  nsresult rv;
-  nsCOMPtr<nsIObserverService> obsSvc =
-    do_GetService("@mozilla.org/observer-service;1");
-  NS_ENSURE_STATE(obsSvc);
-
-  rv = obsSvc->AddObserver(this, "domwindowopened", false);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = obsSvc->AddObserver(this, "domwindowclosed", false);
-  NS_ENSURE_SUCCESS(rv, rv);
-  mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
-}
-
-nsresult
-nsWidgetUtils::UpdateFromEvent(nsIDOMEvent *aDOMEvent)
-{
-  nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aDOMEvent);
-  if (!mouseEvent)
-    return NS_OK;
-
-  mouseEvent->GetScreenX(&g_lastX);
-  mouseEvent->GetScreenY(&g_lastY);
-
-  nsCOMPtr<nsIDOMWindow> mWindow;
-  nsCOMPtr<nsIDOMNode> mNode;
-  nsCOMPtr<nsIDOMNode> mOrigNode;
-
-  uint32_t type = 0;
-  nsDOMEvent* event = aDOMEvent->InternalDOMEvent();
-  bool isXul = false;
-  {
-    nsCOMPtr<EventTarget> eventOrigTarget = event->GetOriginalTarget();
-    if (eventOrigTarget)
-      mOrigNode = do_QueryInterface(eventOrigTarget);
-    isXul = IsXULNode(mOrigNode, &type);
-
-  }
-  if (isXul)
-    return NS_ERROR_FAILURE;
-
-  nsCOMPtr<EventTarget> eventTarget = event->GetTarget();
-  if (eventTarget)
-    mNode = do_QueryInterface(eventTarget);
-
-  if (!mNode)
-    return NS_OK;
-
-  GetDOMWindowByNode(mNode, getter_AddRefs(mWindow));
-  if (!mWindow)
-    return NS_OK;
-  nsCOMPtr<nsIDocument> doc;
-  nsCOMPtr<nsIDOMDocument> domDoc;
-  mWindow->GetDocument(getter_AddRefs(domDoc));
-  doc = do_QueryInterface(domDoc);
-  if (!doc) return NS_OK;
-  // the only case where there could be more shells in printpreview
-  nsIPresShell *shell = doc->GetShell();
-  NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
-  mViewManager = shell->GetViewManager();
-  NS_ENSURE_TRUE(mViewManager, NS_ERROR_FAILURE);
-  mViewManager->GetRootWidget(getter_AddRefs(mWidget));
-  NS_ENSURE_TRUE(mWidget, NS_ERROR_FAILURE);
-  return NS_OK;
-}
-
-nsresult
-nsWidgetUtils::MouseDown(nsIDOMEvent* aDOMEvent)
-{
-  g_is_scrollable = false;
-  // Return TRUE from your signal handler to mark the event as consumed.
-  if (NS_FAILED(UpdateFromEvent(aDOMEvent)))
-    return NS_OK;
-  g_is_scrollable = true;
-  if (g_is_scrollable) {
-     aDOMEvent->StopPropagation();
-     aDOMEvent->PreventDefault();
-  }
-  return NS_OK;
-}
-
-/* static */ void
-nsWidgetUtils::StopPanningCallback(nsITimer *timer, void *closure)
-{
-  g_panning = false;
-}
-
-nsresult
-nsWidgetUtils::MouseUp(nsIDOMEvent* aDOMEvent)
-{
-  nsCOMPtr <nsIDOMMouseEvent> mouseEvent;
-  mouseEvent = do_QueryInterface(aDOMEvent);
-  if (!mouseEvent)
-    return NS_OK;
-  // Return TRUE from your signal handler to mark the event as consumed.
-  g_lastX = MIN_INT;
-  g_lastY = MIN_INT;
-  g_is_scrollable = false;
-  if (g_panning) {
-     aDOMEvent->StopPropagation();
-     aDOMEvent->PreventDefault();
-     nsresult rv;
-     if (mTimer) {
-       rv = mTimer->InitWithFuncCallback(nsWidgetUtils::StopPanningCallback,
-                                        nullptr, 500, nsITimer::TYPE_ONE_SHOT);
-       if (NS_SUCCEEDED(rv))
-         return NS_OK;
-     }
-     g_panning = false;
-  }
-  return NS_OK;
-}
-
-nsresult
-nsWidgetUtils::MouseMove(nsIDOMEvent* aDOMEvent)
-{
-  if (!g_is_scrollable) return NS_OK;
-
-  nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aDOMEvent);
-  if (!mouseEvent)
-    return NS_OK;
-  int x, y;
-  ((nsIDOMMouseEvent*)mouseEvent)->GetScreenX(&x);
-  ((nsIDOMMouseEvent*)mouseEvent)->GetScreenY(&y);
-
-  int dx = g_lastX - x;
-  int dy = g_lastY - y;
-  if(g_lastX == MIN_INT || g_lastY == MIN_INT)
-    return NS_OK;
-
-  nsView* aView = mViewManager->GetRootView();
-  if (!aView)
-    if (NS_FAILED(UpdateFromEvent(aDOMEvent)))
-      return NS_OK;
-
-  nsEventStatus status;
-  WidgetWheelEvent wheelEvent(true, NS_WHEEL_WHEEL, mWidget);
-  wheelEvent.deltaMode = nsIDOMWheelEvent::DOM_DELTA_LINE;
-  wheelEvent.deltaX = wheelEvent.lineOrPageDeltaX = dx;
-  wheelEvent.deltaY = wheelEvent.lineOrPageDeltaY = dy;
-  mViewManager->DispatchEvent(&wheelEvent, aView, &status);
-  if (status != nsEventStatus_eIgnore) {
-    if (dx > 5 || dy > 5) {
-      g_panning = true;
-    }
-    g_lastX = x;
-    g_lastY = y;
-  }
-
-  if (g_panning) {
-     aDOMEvent->StopPropagation();
-     aDOMEvent->PreventDefault();
-  }
-
-  return NS_OK;
-}
-
-// nsIContentPolicy Implementation
-NS_IMETHODIMP
-nsWidgetUtils::ShouldLoad(uint32_t          aContentType,
-                          nsIURI           *aContentLocation,
-                          nsIURI           *aRequestingLocation,
-                          nsISupports      *aRequestingContext,
-                          const nsACString &aMimeGuess,
-                          nsISupports      *aExtra,
-                          int16_t          *aDecision)
-{
-    *aDecision = nsIContentPolicy::ACCEPT;
-    nsresult rv;
-
-    if (aContentType != nsIContentPolicy::TYPE_DOCUMENT)
-        return NS_OK;
-
-    // we can't do anything without this
-    if (!aContentLocation)
-        return NS_OK;
-
-    nsAutoCString scheme;
-    rv = aContentLocation->GetScheme(scheme);
-    nsAutoCString lscheme;
-    ToLowerCase(scheme, lscheme);
-    if (!lscheme.EqualsLiteral("ftp") &&
-        !lscheme.EqualsLiteral("http") &&
-        !lscheme.EqualsLiteral("https"))
-        return NS_OK;
-    if (g_panning > 0)
-      *aDecision = nsIContentPolicy::REJECT_REQUEST;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsWidgetUtils::HandleEvent(nsIDOMEvent* aDOMEvent)
-{
-  nsAutoString eventType;
-  aEvent->GetType(eventType);
-
-  if (eventType.EqualsLiteral("mousedown")) {
-    return MouseDown(aEvent);
-  }
-  if (eventType.EqualsLiteral("mouseup")) {
-    return MouseUp(aEvent);
-  }
-  if (eventType.EqualsLiteral("mousemove")) {
-    return MouseMove(aEvent);
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsWidgetUtils::ShouldProcess(uint32_t          aContentType,
-                             nsIURI           *aContentLocation,
-                             nsIURI           *aRequestingLocation,
-                             nsISupports      *aRequestingContext,
-                             const nsACString &aMimeGuess,
-                             nsISupports      *aExtra,
-                             int16_t          *aDecision)
-{
-    *aDecision = nsIContentPolicy::ACCEPT;
-    return NS_OK;
-}
-
-bool
-nsWidgetUtils::IsXULNode(nsIDOMNode *aNode, uint32_t *aType)
-{
-  bool retval = false;
-  if (!aNode) return retval;
-
-  nsString sorigNode;
-  aNode->GetNodeName(sorigNode);
-  if (sorigNode.EqualsLiteral("#document"))
-    return retval;
-  retval = StringBeginsWith(sorigNode, NS_LITERAL_STRING("xul:"));
-
-  if (!aType) return retval;
-
-  if (sorigNode.EqualsLiteral("xul:thumb")
-      || sorigNode.EqualsLiteral("xul:vbox")
-      || sorigNode.EqualsLiteral("xul:spacer"))
-    *aType = false; // Magic
-  else if (sorigNode.EqualsLiteral("xul:slider"))
-    *aType = 2; // Magic
-  else if (sorigNode.EqualsLiteral("xul:scrollbarbutton"))
-    *aType = 3; // Magic
-
-  return retval;
-}
-
-nsresult
-nsWidgetUtils::GetDOMWindowByNode(nsIDOMNode* aNode, nsIDOMWindow** aDOMWindow)
-{
-  nsCOMPtr<nsIDOMDocument> nodeDoc;
-  nsresult rv = aNode->GetOwnerDocument(getter_AddRefs(nodeDoc));
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_TRUE(nodeDoc, NS_ERROR_NULL_POINTER);
-
-  nsCOMPtr<nsIDOMWindow> window;
-  rv = nodeDoc->GetDefaultView(getter_AddRefs(window));
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_TRUE(window, NS_ERROR_NULL_POINTER);
-  window.forget(aDOMWindow);
-  return rv;
-}
-
-EventTarget*
-nsWidgetUtils::GetChromeEventHandler(nsIDOMWindow* aDOMWin)
-{
-  nsCOMPtr<nsPIDOMWindow> privateDOMWindow = do_QueryInterface(aDOMWin);
-  return privateDOMWindow ? privateDOMWindow->GetChromeEventHandler() : nullptr;
-}
-
-void
-nsWidgetUtils::RemoveWindowListeners(nsIDOMWindow *aDOMWin)
-{
-    nsresult rv;
-    EventTarget* chromeEventHandler = GetChromeEventHandler(aDOMWin);
-    if (!chromeEventHandler) {
-        return;
-    }
-
-    // Use capturing, otherwise the normal find next will get activated when ours should
-
-    // Remove DOM Text listener for IME text events
-    chromeEventHandler->RemoveEventListener(NS_LITERAL_STRING("mousedown"),
-                                            this, false);
-    chromeEventHandler->RemoveEventListener(NS_LITERAL_STRING("mouseup"),
-                                            this, false);
-    chromeEventHandler->RemoveEventListener(NS_LITERAL_STRING("mousemove"),
-                                            this, false);
-}
-
-void
-nsWidgetUtils::AttachWindowListeners(nsIDOMWindow *aDOMWin)
-{
-    nsresult rv;
-    EventHandler* chromeEventHandler = GetChromeEventHandler(aDOMWin);
-    if (!chromeEventHandler) {
-        return;
-    }
-
-    // Use capturing, otherwise the normal find next will get activated when ours should
-
-    // Attach menu listeners, this will help us ignore keystrokes meant for menus
-    chromeEventHandler->AddEventListener(NS_LITERAL_STRING("mousedown"), this,
-                                         false, false);
-    chromeEventHandler->AddEventListener(NS_LITERAL_STRING("mouseup"), this,
-                                         false, false);
-    chromeEventHandler->AddEventListener(NS_LITERAL_STRING("mousemove"), this,
-                                         false, false);
-}
-
-nsWidgetUtils::~nsWidgetUtils()
-{
-}
-
-NS_IMPL_ISUPPORTS4(nsWidgetUtils,
-                   nsIObserver,
-                   nsIDOMEventListener,
-                   nsIContentPolicy,
-                   nsISupportsWeakReference)
-
-NS_IMETHODIMP
-nsWidgetUtils::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData)
-{
-  nsresult rv;
-  if (!strcmp(aTopic,"domwindowopened")) 
-  {
-    nsCOMPtr<nsIDOMWindow> chromeWindow = do_QueryInterface(aSubject);
-    if (chromeWindow)
-      AttachWindowListeners(chromeWindow);
-    return NS_OK;
-  }
-
-  if (!strcmp(aTopic,"domwindowclosed")) 
-  {
-    nsCOMPtr<nsIDOMWindow> chromeWindow = do_QueryInterface(aSubject);
-    RemoveWindowListeners(chromeWindow);
-    return NS_OK;
-  }
-
-  return NS_OK;
-}
-
-//------------------------------------------------------------------------------
-//  XPCOM REGISTRATION BELOW
-//------------------------------------------------------------------------------
-
-#define WidgetUtils_CID \
-{  0x0ced17b6, 0x96ed, 0x4030, \
-{0xa1, 0x34, 0x77, 0xcb, 0x66, 0x10, 0xa8, 0xf6} }
-
-#define WidgetUtils_ContractID "@mozilla.org/extensions/widgetutils;1"
-
-static NS_METHOD WidgetUtilsRegistration(nsIComponentManager *aCompMgr,
-                                         nsIFile *aPath,
-                                         const char *registryLocation,
-                                         const char *componentType,
-                                         const nsModuleComponentInfo *info)
-{
-    nsresult rv;
-
-    nsCOMPtr<nsIServiceManager> servman = do_QueryInterface((nsISupports*)aCompMgr, &rv);
-    if (NS_FAILED(rv))
-        return rv;
-
-    nsCOMPtr<nsICategoryManager> catman;
-    servman->GetServiceByContractID(NS_CATEGORYMANAGER_CONTRACTID,
-                                    NS_GET_IID(nsICategoryManager),
-                                    getter_AddRefs(catman));
-
-    if (NS_FAILED(rv))
-        return rv;
-
-    char* previous = nullptr;
-    rv = catman->AddCategoryEntry("app-startup",
-                                  "WidgetUtils",
-                                  WidgetUtils_ContractID,
-                                  true,
-                                  true,
-                                  &previous);
-    if (previous)
-        nsMemory::Free(previous);
-    rv = catman->AddCategoryEntry("content-policy",
-                                  "WidgetUtils",
-                                  WidgetUtils_ContractID,
-                                  true,
-                                  true,
-                                  &previous);
-    if (previous)
-        nsMemory::Free(previous);
-
-    return rv;
-}
-
-static NS_METHOD WidgetUtilsUnregistration(nsIComponentManager *aCompMgr,
-                                           nsIFile *aPath,
-                                           const char *registryLocation,
-                                           const nsModuleComponentInfo *info)
-{
-    nsresult rv;
-
-    nsCOMPtr<nsIServiceManager> servman = do_QueryInterface((nsISupports*)aCompMgr, &rv);
-    if (NS_FAILED(rv))
-        return rv;
-
-    nsCOMPtr<nsICategoryManager> catman;
-    servman->GetServiceByContractID(NS_CATEGORYMANAGER_CONTRACTID,
-                                    NS_GET_IID(nsICategoryManager),
-                                    getter_AddRefs(catman));
-
-    if (NS_FAILED(rv))
-        return rv;
-
-    rv = catman->DeleteCategoryEntry("app-startup",
-                                     "WidgetUtils",
-                                     true);
-    rv = catman->DeleteCategoryEntry("content-policy",
-                                     "WidgetUtils",
-                                     true);
-
-    return rv;
-}
-
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsWidgetUtils)
-
-  static const nsModuleComponentInfo components[] =
-{
-  { "nsWidgetUtilsService",
-    WidgetUtils_CID,
-    WidgetUtils_ContractID,
-    nsWidgetUtilsConstructor,
-    WidgetUtilsRegistration,
-    WidgetUtilsUnregistration
-  }
-};
-
-NS_IMPL_NSGETMODULE(nsWidgetUtilsModule, components)
--- a/gfx/2d/DrawTargetD2D.cpp
+++ b/gfx/2d/DrawTargetD2D.cpp
@@ -759,37 +759,47 @@ DrawTargetD2D::DrawSurfaceWithShadow(Sou
 
   mDevice->Draw(4, 0);
 }
 
 void
 DrawTargetD2D::ClearRect(const Rect &aRect)
 {
   MarkChanged();
-
-  FlushTransformToRT();
+  PushClipRect(aRect);
+
   PopAllClips();
 
   AutoSaveRestoreClippedOut restoreClippedOut(this);
 
-  restoreClippedOut.Save();
-
-  bool needsClip = false;
-
-  needsClip = aRect.x > 0 || aRect.y > 0 ||
-              aRect.XMost() < mSize.width ||
-              aRect.YMost() < mSize.height;
-
-  if (needsClip) {
-    mRT->PushAxisAlignedClip(D2DRect(aRect), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+  D2D1_RECT_F clipRect;
+  bool isPixelAligned;
+  bool pushedClip = false;
+  if (mTransform.IsRectilinear() &&
+      GetDeviceSpaceClipRect(clipRect, isPixelAligned)) {
+    if (mTransformDirty ||
+        !mTransform.IsIdentity()) {
+      mRT->SetTransform(D2D1::IdentityMatrix());
+      mTransformDirty = true;
+    }
+
+    mRT->PushAxisAlignedClip(clipRect, isPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+    pushedClip = true;
+  } else {
+    FlushTransformToRT();
+    restoreClippedOut.Save();
   }
+
   mRT->Clear(D2D1::ColorF(0, 0.0f));
-  if (needsClip) {
+
+  if (pushedClip) {
     mRT->PopAxisAlignedClip();
   }
+
+  PopClip();
   return;
 }
 
 void
 DrawTargetD2D::CopySurface(SourceSurface *aSurface,
                            const IntRect &aSourceRect,
                            const IntPoint &aDestination)
 {
@@ -1768,19 +1778,50 @@ DrawTargetD2D::FinalizeRTForOperation(Co
 static D2D1_RECT_F
 IntersectRect(const D2D1_RECT_F& aRect1, const D2D1_RECT_F& aRect2)
 {
   D2D1_RECT_F result;
   result.left = max(aRect1.left, aRect2.left);
   result.top = max(aRect1.top, aRect2.top);
   result.right = min(aRect1.right, aRect2.right);
   result.bottom = min(aRect1.bottom, aRect2.bottom);
+
+  result.right = max(result.right, result.left);
+  result.bottom = max(result.bottom, result.top);
+
   return result;
 }
 
+bool
+DrawTargetD2D::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned)
+{
+  if (!mPushedClips.size()) {
+    return false;
+  }
+
+  std::vector<DrawTargetD2D::PushedClip>::iterator iter = mPushedClips.begin();
+  if (iter->mPath) {
+    return false;
+  }
+  aClipRect = iter->mBounds;
+  aIsPixelAligned = iter->mIsPixelAligned;
+
+  iter++;
+  for (;iter != mPushedClips.end(); iter++) {
+    if (iter->mPath) {
+      return false;
+    }
+    aClipRect = IntersectRect(aClipRect, iter->mBounds);
+    if (!iter->mIsPixelAligned) {
+      aIsPixelAligned = false;
+    }
+  }
+  return true;
+}
+
 TemporaryRef<ID2D1Geometry>
 DrawTargetD2D::GetClippedGeometry(IntRect *aClipBounds)
 {
   if (mCurrentClippedGeometry) {
     *aClipBounds = mCurrentClipBounds;
     return mCurrentClippedGeometry;
   }
 
--- a/gfx/2d/DrawTargetD2D.h
+++ b/gfx/2d/DrawTargetD2D.h
@@ -200,16 +200,18 @@ private:
   TemporaryRef<ID2D1RenderTarget> CreateRTForTexture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat);
 
   // This returns the clipped geometry, in addition it returns aClipBounds which
   // represents the intersection of all pixel-aligned rectangular clips that
   // are currently set. The returned clipped geometry must be clipped by these
   // bounds to correctly reflect the total clip. This is in device space.
   TemporaryRef<ID2D1Geometry> GetClippedGeometry(IntRect *aClipBounds);
 
+  bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned);
+
   TemporaryRef<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
 
   TemporaryRef<ID3D10Texture2D> CreateGradientTexture(const GradientStopsD2D *aStops);
   TemporaryRef<ID3D10Texture2D> CreateTextureForAnalysis(IDWriteGlyphRunAnalysis *aAnalysis, const IntRect &aBounds);
 
   void SetupEffectForRadialGradient(const RadialGradientPattern *aPattern);
   void SetupStateForRendering();
 
--- a/gfx/gl/GLBlitTextureImageHelper.cpp
+++ b/gfx/gl/GLBlitTextureImageHelper.cpp
@@ -3,16 +3,17 @@
 /* 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/. */
 
 #include "GLBlitTextureImageHelper.h"
 #include "GLUploadHelpers.h"
 #include "DecomposeIntoNoRepeatTriangles.h"
 #include "GLContext.h"
+#include "ScopedGLHelpers.h"
 #include "nsRect.h"
 #include "gfx2DGlue.h"
 #include "gfxUtils.h"
 
 namespace mozilla {
 namespace gl {
 
 GLBlitTextureImageHelper::GLBlitTextureImageHelper(GLContext* gl)
@@ -138,17 +139,18 @@ GLBlitTextureImageHelper::BlitTextureIma
                 RectTriangles::vert_coord* v = (RectTriangles::vert_coord*)rects.vertexPointer();
 
                 for (unsigned int i = 0; i < rects.elements(); ++i) {
                     v[i].x = (v[i].x * (dx1 - dx0)) + dx0;
                     v[i].y = (v[i].y * (dy1 - dy0)) + dy0;
                 }
             }
 
-            TextureImage::ScopedBindTexture texBind(aSrc, LOCAL_GL_TEXTURE0);
+            ScopedBindTextureUnit autoTexUnit(mGL, LOCAL_GL_TEXTURE0);
+            ScopedBindTexture autoTex(mGL, aSrc->GetTextureID());
 
             mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
 
             mGL->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, rects.vertexPointer());
             mGL->fVertexAttribPointer(1, 2, LOCAL_GL_FLOAT, LOCAL_GL_FALSE, 0, rects.texCoordPointer());
 
             mGL->fEnableVertexAttribArray(0);
             mGL->fEnableVertexAttribArray(1);
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -1184,34 +1184,16 @@ GLContext::ListHasExtension(const GLubyt
                 return true;
             }
         }
         start = terminator;
     }
     return false;
 }
 
-void GLContext::ApplyFilterToBoundTexture(GraphicsFilter aFilter)
-{
-    ApplyFilterToBoundTexture(LOCAL_GL_TEXTURE_2D, aFilter);
-}
-
-void GLContext::ApplyFilterToBoundTexture(GLuint aTarget,
-                                          GraphicsFilter aFilter)
-{
-    if (aFilter == GraphicsFilter::FILTER_NEAREST) {
-        fTexParameteri(aTarget, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
-        fTexParameteri(aTarget, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
-    } else {
-        fTexParameteri(aTarget, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
-        fTexParameteri(aTarget, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
-    }
-}
-
-
 void
 GLContext::DetermineCaps()
 {
     PixelBufferFormat format = QueryPixelFormat();
 
     SurfaceCaps caps;
     caps.color = !!format.red && !!format.green && !!format.blue;
     caps.bpp16 = caps.color && format.ColorBits() == 16;
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -2477,35 +2477,16 @@ public:
      * Defines a two-dimensional texture image for context target surface
      */
     virtual bool BindTexImage() { return false; }
     /*
      * Releases a color buffer that is being used as a texture
      */
     virtual bool ReleaseTexImage() { return false; }
 
-    /**
-     * Applies aFilter to the texture currently bound to GL_TEXTURE_2D.
-     */
-    void ApplyFilterToBoundTexture(GraphicsFilter aFilter);
-
-    /**
-     * Applies aFilter to the texture currently bound to aTarget.
-     */
-    void ApplyFilterToBoundTexture(GLuint aTarget,
-                                   GraphicsFilter aFilter);
-
-    virtual bool BindExternalBuffer(GLuint texture, void* buffer) { return false; }
-    virtual bool UnbindExternalBuffer(GLuint texture) { return false; }
-
-#ifdef MOZ_WIDGET_GONK
-    virtual EGLImage CreateEGLImageForNativeBuffer(void* buffer) = 0;
-    virtual void DestroyEGLImage(EGLImage image) = 0;
-#endif
-
     // Before reads from offscreen texture
     void GuaranteeResolve();
 
     /*
      * Resize the current offscreen buffer.  Returns true on success.
      * If it returns false, the context should be treated as unusable
      * and should be recreated.  After the resize, the viewport is not
      * changed; glViewport should be called as appropriate.
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -403,35 +403,16 @@ public:
                                                LOCAL_EGL_BACK_BUFFER);
         if (success == LOCAL_EGL_FALSE)
             return false;
 
         mBound = false;
         return true;
     }
 
-#ifdef MOZ_WIDGET_GONK
-    EGLImage CreateEGLImageForNativeBuffer(void* buffer) MOZ_OVERRIDE
-    {
-        EGLint attrs[] = {
-            LOCAL_EGL_IMAGE_PRESERVED, LOCAL_EGL_TRUE,
-            LOCAL_EGL_NONE, LOCAL_EGL_NONE
-        };
-        return sEGLLibrary.fCreateImage(EGL_DISPLAY(),
-                                        EGL_NO_CONTEXT,
-                                        LOCAL_EGL_NATIVE_BUFFER_ANDROID,
-                                        buffer, attrs);
-    }
-
-    void DestroyEGLImage(EGLImage image) MOZ_OVERRIDE
-    {
-        sEGLLibrary.fDestroyImage(EGL_DISPLAY(), image);
-    }
-#endif
-
     virtual void SetEGLSurfaceOverride(EGLSurface surf) MOZ_OVERRIDE {
         if (Screen()) {
             /* Blit `draw` to `read` if we need to, before we potentially juggle
              * `read` around. If we don't, we might attach a different `read`,
              * and *then* hit AssureBlitted, which will blit a dirty `draw` onto
              * the wrong `read`!
              */
             Screen()->AssureBlitted();
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -53,17 +53,17 @@ LibType
 GLXLibrary::SelectLibrary(const ContextFlags& aFlags)
 {
   return (aFlags & ContextFlagsMesaLLVMPipe)
           ? GLXLibrary::MESA_LLVMPIPE_LIB
           : GLXLibrary::OPENGL_LIB;
 }
 
 // Check that we have at least version aMajor.aMinor .
-bool 
+bool
 GLXLibrary::GLXVersionCheck(int aMajor, int aMinor)
 {
     return aMajor < mGLXMajorVersion ||
            (aMajor == mGLXMajorVersion && aMinor <= mGLXMinorVersion);
 }
 
 static inline bool
 HasExtension(const char* aExtensions, const char* aRequiredExtension)
@@ -239,17 +239,17 @@ GLXLibrary::EnsureInitialized(LibType li
         sym14 = symbols14;
     }
     if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, sym14)) {
         NS_WARNING("Couldn't find required entry point in OpenGL shared library");
         return false;
     }
 
     if (HasExtension(extensionsStr, "GLX_EXT_texture_from_pixmap") &&
-        GLLibraryLoader::LoadSymbols(mOGLLibrary, symbols_texturefrompixmap, 
+        GLLibraryLoader::LoadSymbols(mOGLLibrary, symbols_texturefrompixmap,
                                          (GLLibraryLoader::PlatformLookupFunction)&xGetProcAddress))
     {
 #ifdef MOZ_WIDGET_GTK
         mUseTextureFromPixmap = gfxPlatformGtk::GetPlatform()->UseXRender();
 #else
         mUseTextureFromPixmap = true;
 #endif
     } else {
@@ -273,25 +273,25 @@ GLXLibrary::EnsureInitialized(LibType li
 }
 
 bool
 GLXLibrary::SupportsTextureFromPixmap(gfxASurface* aSurface)
 {
     if (!EnsureInitialized(mLibType)) {
         return false;
     }
-    
+
     if (aSurface->GetType() != gfxSurfaceTypeXlib || !mUseTextureFromPixmap) {
         return false;
     }
 
     return true;
 }
 
-GLXPixmap 
+GLXPixmap
 GLXLibrary::CreatePixmap(gfxASurface* aSurface)
 {
     if (!SupportsTextureFromPixmap(aSurface)) {
         return None;
     }
 
     gfxXlibSurface *xs = static_cast<gfxXlibSurface*>(aSurface);
     const XRenderPictFormat *format = xs->XRenderFormat();
@@ -315,17 +315,17 @@ GLXLibrary::CreatePixmap(gfxASurface* aS
     Display *display = xs->XDisplay();
     int xscreen = DefaultScreen(display);
 
     ScopedXFree<GLXFBConfig> cfgs(xChooseFBConfig(display,
                                                   xscreen,
                                                   attribs,
                                                   &numConfigs));
 
-    // Find an fbconfig that matches the pixel format used on the Pixmap. 
+    // Find an fbconfig that matches the pixel format used on the Pixmap.
     int matchIndex = -1;
     unsigned long redMask =
         static_cast<unsigned long>(direct.redMask) << direct.red;
     unsigned long greenMask =
         static_cast<unsigned long>(direct.greenMask) << direct.green;
     unsigned long blueMask =
         static_cast<unsigned long>(direct.blueMask) << direct.blue;
     // This is true if the Pixmap has bits for alpha or unused bits.
@@ -431,17 +431,17 @@ GLXLibrary::DestroyPixmap(GLXPixmap aPix
     }
 
     Display *display = DefaultXDisplay();
     xDestroyPixmap(display, aPixmap);
 }
 
 void
 GLXLibrary::BindTexImage(GLXPixmap aPixmap)
-{    
+{
     if (!mUseTextureFromPixmap) {
         return;
     }
 
     Display *display = DefaultXDisplay();
     // Make sure all X drawing to the surface has finished before binding to a texture.
     if (mClientIsMesa) {
         // Using XSync instead of Mesa's glXWaitX, because its glxWaitX is a
@@ -503,97 +503,97 @@ GLXLibrary::AfterGLXCall()
         }
         XSetErrorHandler(sOldErrorHandler);
     }
 }
 
 #define BEFORE_GLX_CALL do {                     \
     sGLXLibrary[gCurrLib].BeforeGLXCall();       \
 } while (0)
-    
+
 #define AFTER_GLX_CALL do {                      \
     sGLXLibrary[gCurrLib].AfterGLXCall();        \
 } while (0)
 
 #else
 
 #define BEFORE_GLX_CALL do { } while(0)
 #define AFTER_GLX_CALL do { } while(0)
 
 #endif
-    
-void 
+
+void
 GLXLibrary::xDestroyContext(Display* display, GLXContext context)
 {
     BEFORE_GLX_CALL;
     xDestroyContextInternal(display, context);
     AFTER_GLX_CALL;
 }
 
-Bool 
-GLXLibrary::xMakeCurrent(Display* display, 
-                         GLXDrawable drawable, 
+Bool
+GLXLibrary::xMakeCurrent(Display* display,
+                         GLXDrawable drawable,
                          GLXContext context)
 {
     BEFORE_GLX_CALL;
     Bool result = xMakeCurrentInternal(display, drawable, context);
     AFTER_GLX_CALL;
     return result;
 }
 
-GLXContext 
+GLXContext
 GLXLibrary::xGetCurrentContext()
 {
     BEFORE_GLX_CALL;
     GLXContext result = xGetCurrentContextInternal();
     AFTER_GLX_CALL;
     return result;
 }
 
-/* static */ void* 
+/* static */ void*
 GLXLibrary::xGetProcAddress(const char *procName)
 {
     BEFORE_GLX_CALL;
     void* result = sGLXLibrary[gCurrLib].xGetProcAddressInternal(procName);
     AFTER_GLX_CALL;
     return result;
 }
 
 GLXFBConfig*
-GLXLibrary::xChooseFBConfig(Display* display, 
-                            int screen, 
-                            const int *attrib_list, 
+GLXLibrary::xChooseFBConfig(Display* display,
+                            int screen,
+                            const int *attrib_list,
                             int *nelements)
 {
     BEFORE_GLX_CALL;
     GLXFBConfig* result = xChooseFBConfigInternal(display, screen, attrib_list, nelements);
     AFTER_GLX_CALL;
     return result;
 }
 
-GLXFBConfig* 
-GLXLibrary::xGetFBConfigs(Display* display, 
-                          int screen, 
+GLXFBConfig*
+GLXLibrary::xGetFBConfigs(Display* display,
+                          int screen,
                           int *nelements)
 {
     BEFORE_GLX_CALL;
     GLXFBConfig* result = xGetFBConfigsInternal(display, screen, nelements);
     AFTER_GLX_CALL;
     return result;
 }
-    
+
 GLXContext
-GLXLibrary::xCreateNewContext(Display* display, 
-                              GLXFBConfig config, 
-                              int render_type, 
-                              GLXContext share_list, 
+GLXLibrary::xCreateNewContext(Display* display,
+                              GLXFBConfig config,
+                              int render_type,
+                              GLXContext share_list,
                               Bool direct)
 {
     BEFORE_GLX_CALL;
-    GLXContext result = xCreateNewContextInternal(display, config, 
+    GLXContext result = xCreateNewContextInternal(display, config,
                                                   render_type,
                                                   share_list, direct);
     AFTER_GLX_CALL;
     return result;
 }
 
 int
 GLXLibrary::xGetFBConfigAttrib(Display *display,
@@ -642,17 +642,17 @@ GLXLibrary::xQueryServerString(Display *
 {
     BEFORE_GLX_CALL;
     const char *result = xQueryServerStringInternal(display, screen, name);
     AFTER_GLX_CALL;
     return result;
 }
 
 GLXPixmap
-GLXLibrary::xCreatePixmap(Display *display, 
+GLXLibrary::xCreatePixmap(Display *display,
                           GLXFBConfig config,
                           Pixmap pixmap,
                           const int *attrib_list)
 {
     BEFORE_GLX_CALL;
     GLXPixmap result = xCreatePixmapInternal(display, config,
                                              pixmap, attrib_list);
     AFTER_GLX_CALL;
@@ -705,43 +705,43 @@ GLXLibrary::xReleaseTexImage(Display *di
                              GLXDrawable drawable,
                              int buffer)
 {
     BEFORE_GLX_CALL;
     xReleaseTexImageInternal(display, drawable, buffer);
     AFTER_GLX_CALL;
 }
 
-void 
+void
 GLXLibrary::xWaitGL()
 {
     BEFORE_GLX_CALL;
     xWaitGLInternal();
     AFTER_GLX_CALL;
 }
 
 void
 GLXLibrary::xWaitX()
 {
     BEFORE_GLX_CALL;
     xWaitXInternal();
     AFTER_GLX_CALL;
 }
 
 GLXContext
-GLXLibrary::xCreateContextAttribs(Display* display, 
-                                  GLXFBConfig config, 
-                                  GLXContext share_list, 
+GLXLibrary::xCreateContextAttribs(Display* display,
+                                  GLXFBConfig config,
+                                  GLXContext share_list,
                                   Bool direct,
                                   const int* attrib_list)
 {
     BEFORE_GLX_CALL;
-    GLXContext result = xCreateContextAttribsInternal(display, 
-                                                      config, 
-                                                      share_list, 
+    GLXContext result = xCreateContextAttribsInternal(display,
+                                                      config,
+                                                      share_list,
                                                       direct,
                                                       attrib_list);
     AFTER_GLX_CALL;
     return result;
 }
 
 class GLContextGLX : public GLContext
 {
@@ -900,17 +900,17 @@ TRY_AGAIN_NO_SHARING:
         return true;
     }
 
     void *GetNativeData(NativeDataType aType)
     {
         switch(aType) {
         case NativeGLContext:
             return mContext;
- 
+
         case NativeThebesSurface:
             return mPixmap;
 
         default:
             return nullptr;
         }
     }
 
@@ -1007,23 +1007,23 @@ GLContextProviderGLX::CreateForWindow(ns
 
     // Currently, we take whatever Visual the window already has, and
     // try to create an fbconfig for that visual.  This isn't
     // necessarily what we want in the long run; an fbconfig may not
     // be available for the existing visual, or if it is, the GL
     // performance might be suboptimal.  But using the existing visual
     // is a relatively safe intermediate step.
 
-    Display *display = (Display*)aWidget->GetNativeData(NS_NATIVE_DISPLAY); 
+    Display *display = (Display*)aWidget->GetNativeData(NS_NATIVE_DISPLAY);
     int xscreen = DefaultScreen(display);
     Window window = GET_NATIVE_WINDOW(aWidget);
 
     int numConfigs;
     ScopedXFree<GLXFBConfig> cfgs;
-    if (sDefGLXLib.IsATI() || 
+    if (sDefGLXLib.IsATI() ||
         !sDefGLXLib.GLXVersionCheck(1, 3)) {
         const int attribs[] = {
             LOCAL_GLX_DOUBLEBUFFER, False,
             0
         };
         cfgs = sDefGLXLib.xChooseFBConfig(display,
                                        xscreen,
                                        attribs,
--- a/gfx/gl/GLTextureImage.cpp
+++ b/gfx/gl/GLTextureImage.cpp
@@ -200,23 +200,16 @@ BasicTextureImage::EndUpdate()
 void
 BasicTextureImage::BindTexture(GLenum aTextureUnit)
 {
     mGLContext->fActiveTexture(aTextureUnit);
     mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
     mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
 }
 
-void
-BasicTextureImage::ApplyFilter()
-{
-  mGLContext->ApplyFilterToBoundTexture(mFilter);
-}
-
-
 already_AddRefed<gfxASurface>
 BasicTextureImage::GetSurfaceForUpdate(const gfxIntSize& aSize, ImageFormat aFmt)
 {
     return gfxPlatform::GetPlatform()->
         CreateOffscreenSurface(aSize, gfxASurface::ContentFromFormat(aFmt));
 }
 
 bool
@@ -632,22 +625,16 @@ void
 TiledTextureImage::BindTexture(GLenum aTextureUnit)
 {
     if (!GetTileCount()) {
         return;
     }
     mImages[mCurrentImage]->BindTexture(aTextureUnit);
 }
 
-void
-TiledTextureImage::ApplyFilter()
-{
-   mGL->ApplyFilterToBoundTexture(mFilter);
-}
-
 /*
  * Resize, trying to reuse tiles. The reuse strategy is to decide on reuse per
  * column. A tile on a column is reused if it hasn't changed size, otherwise it
  * is discarded/replaced. Extra tiles on a column are pruned after iterating
  * each column, and extra rows are pruned after iteration over the entire image
  * finishes.
  */
 void TiledTextureImage::Resize(const nsIntSize& aSize)
@@ -740,26 +727,16 @@ void TiledTextureImage::Resize(const nsI
     mCurrentImage = 0;
 }
 
 uint32_t TiledTextureImage::GetTileCount()
 {
     return mImages.Length();
 }
 
-TextureImage::ScopedBindTexture::ScopedBindTexture(TextureImage* aTexture,
-                                                   GLenum aTextureUnit)
-    : mTexture(aTexture)
-{
-    if (mTexture) {
-        MOZ_ASSERT(aTextureUnit >= LOCAL_GL_TEXTURE0);
-        mTexture->BindTexture(aTextureUnit);
-    }
-}
-
 already_AddRefed<TextureImage>
 CreateBasicTextureImage(GLContext* aGL,
                         const nsIntSize& aSize,
                         TextureImage::ContentType aContentType,
                         GLenum aWrapMode,
                         TextureImage::Flags aFlags,
                         TextureImage::ImageFormat aImageFormat)
 {
--- a/gfx/gl/GLTextureImage.h
+++ b/gfx/gl/GLTextureImage.h
@@ -180,51 +180,16 @@ public:
      */
     virtual bool DirectUpdate(gfxASurface *aSurf, const nsIntRegion& aRegion, const nsIntPoint& aFrom = nsIntPoint(0,0)) = 0;
     // Moz2D equivalent
     bool UpdateFromDataSource(gfx::DataSourceSurface *aSurf,
                               const nsIntRegion* aDstRegion = nullptr,
                               const gfx::IntPoint* aSrcOffset = nullptr);
 
     virtual void BindTexture(GLenum aTextureUnit) = 0;
-    virtual void ReleaseTexture() {}
-
-    void BindTextureAndApplyFilter(GLenum aTextureUnit) {
-        BindTexture(aTextureUnit);
-        ApplyFilter();
-    }
-
-    class ScopedBindTexture
-    {
-    public:
-        ScopedBindTexture(TextureImage *aTexture, GLenum aTextureUnit);
-
-        ~ScopedBindTexture()
-        {
-            if (mTexture) {
-                mTexture->ReleaseTexture();
-            }
-        }
-
-    protected:
-        TextureImage *mTexture;
-    };
-
-    class ScopedBindTextureAndApplyFilter
-        : public ScopedBindTexture
-    {
-    public:
-        ScopedBindTextureAndApplyFilter(TextureImage *aTexture, GLenum aTextureUnit) :
-          ScopedBindTexture(aTexture, aTextureUnit)
-        {
-            if (mTexture) {
-                mTexture->ApplyFilter();
-            }
-        }
-    };
 
     /**
      * Returns the image format of the texture. Only valid after a matching
      * BeginUpdate/EndUpdate pair have been called.
      */
     virtual gfx::SurfaceFormat GetTextureFormat() {
         return mTextureFormat;
     }
@@ -242,22 +207,16 @@ public:
     gfx::IntSize GetSize() const;
     ContentType GetContentType() const { return mContentType; }
     ImageFormat GetImageFormat() const { return mImageFormat; }
     virtual bool InUpdate() const = 0;
     GLenum GetWrapMode() const { return mWrapMode; }
 
     void SetFilter(GraphicsFilter aFilter) { mFilter = aFilter; }
 
-    /**
-     * Applies this TextureImage's filter, assuming that its texture is
-     * the currently bound texture.
-     */
-    virtual void ApplyFilter() = 0;
-
 protected:
     friend class GLContext;
 
     /**
      * After the ctor, the TextureImage is invalid.  Implementations
      * must allocate resources successfully before returning the new
      * TextureImage from GLContext::CreateTextureImage().  That is,
      * clients must not be given partially-constructed TextureImages.
@@ -340,19 +299,17 @@ public:
 
     // Call after surface data has been uploaded to a texture.
     virtual void FinishedSurfaceUpload();
 
     virtual bool InUpdate() const { return !!mUpdateSurface; }
 
     virtual void Resize(const nsIntSize& aSize);
 
-    virtual void ApplyFilter();
 protected:
-
     GLuint mTexture;
     TextureState mTextureState;
     nsRefPtr<GLContext> mGLContext;
     nsRefPtr<gfxASurface> mUpdateSurface;
     nsIntRegion mUpdateRegion;
 
     // The offset into the update surface at which the update rect is located.
     nsIntPoint mUpdateOffset;
@@ -385,17 +342,16 @@ public:
                                       void* aCallbackData);
     virtual gfx::IntRect GetTileRect();
     virtual GLuint GetTextureID() {
         return mImages[mCurrentImage]->GetTextureID();
     }
     virtual bool DirectUpdate(gfxASurface* aSurf, const nsIntRegion& aRegion, const nsIntPoint& aFrom = nsIntPoint(0,0));
     virtual bool InUpdate() const { return mInUpdate; }
     virtual void BindTexture(GLenum);
-    virtual void ApplyFilter();
 
 protected:
     virtual gfx::IntRect GetSrcTileRect();
 
     unsigned int mCurrentImage;
     TileIterationCallback mIterationCallback;
     void* mIterationCallbackData;
     nsTArray< nsRefPtr<TextureImage> > mImages;
--- a/gfx/gl/TextureImageEGL.cpp
+++ b/gfx/gl/TextureImageEGL.cpp
@@ -306,22 +306,16 @@ TextureImageEGL::DestroyEGLSurface(void)
 {
     if (!mSurface)
         return;
 
     sEGLLibrary.fDestroySurface(EGL_DISPLAY(), mSurface);
     mSurface = nullptr;
 }
 
-void
-TextureImageEGL::ApplyFilter()
-{
-    mGLContext->ApplyFilterToBoundTexture(mFilter);
-}
-
 already_AddRefed<TextureImage>
 CreateTextureImageEGL(GLContext *gl,
                       const nsIntSize& aSize,
                       TextureImage::ContentType aContentType,
                       GLenum aWrapMode,
                       TextureImage::Flags aFlags,
                       TextureImage::ImageFormat aImageFormat)
 {
@@ -352,9 +346,9 @@ TileGenFuncEGL(GLContext *gl,
   gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, texfilter);
   gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
   gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
 
   return teximage.forget();
 }
 
 }
-}
\ No newline at end of file
+}
--- a/gfx/gl/TextureImageEGL.h
+++ b/gfx/gl/TextureImageEGL.h
@@ -70,18 +70,16 @@ protected:
     nsRefPtr<gfxASurface> mUpdateSurface;
     EGLImage mEGLImage;
     GLuint mTexture;
     EGLSurface mSurface;
     EGLConfig mConfig;
     TextureState mTextureState;
 
     bool mBound;
-
-    virtual void ApplyFilter();
 };
 
 already_AddRefed<TextureImage>
 CreateTextureImageEGL(GLContext *gl,
                       const nsIntSize& aSize,
                       TextureImage::ContentType aContentType,
                       GLenum aWrapMode,
                       TextureImage::Flags aFlags,
@@ -92,9 +90,9 @@ TileGenFuncEGL(GLContext *gl,
                const nsIntSize& aSize,
                TextureImage::ContentType aContentType,
                TextureImage::Flags aFlags,
                TextureImage::ImageFormat aImageFormat);
 
 }
 }
 
-#endif // TEXTUREIMAGEEGL_H_
\ No newline at end of file
+#endif // TEXTUREIMAGEEGL_H_
new file mode 100644
--- /dev/null
+++ b/gfx/layers/AtomicRefCountedWithFinalize.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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/. */
+
+#ifndef MOZILLA_ATOMICREFCOUNTEDWITHFINALIZE_H_
+#define MOZILLA_ATOMICREFCOUNTEDWITHFINALIZE_H_
+
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+
+template<typename T>
+class AtomicRefCountedWithFinalize
+{
+  protected:
+    AtomicRefCountedWithFinalize()
+      : mRefCount(0)
+    {}
+
+    ~AtomicRefCountedWithFinalize() {}
+
+  public:
+    void AddRef() {
+      MOZ_ASSERT(mRefCount >= 0);
+      ++mRefCount;
+    }
+
+    void Release() {
+      MOZ_ASSERT(mRefCount > 0);
+      if (0 == --mRefCount) {
+#ifdef DEBUG
+        mRefCount = detail::DEAD;
+#endif
+        T* derived = static_cast<T*>(this);
+        derived->Finalize();
+        delete derived;
+      }
+    }
+
+private:
+    Atomic<int> mRefCount;
+};
+
+}
+
+#endif
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -212,16 +212,17 @@ ImageContainer::SetCurrentImage(Image *a
  void
 ImageContainer::ClearAllImages()
 {
   if (IsAsync()) {
     // Let ImageClient release all TextureClients.
     ImageBridgeChild::FlushAllImages(mImageClient, this, false);
     return;
   }
+
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   SetCurrentImageInternal(nullptr);
 }
 
 void
 ImageContainer::ClearAllImagesExceptFront()
 {
   if (IsAsync()) {
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -209,17 +209,17 @@ Layer::ClearAnimations()
   }
 
   MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ClearAnimations", this));
   mAnimations.Clear();
   mAnimationData.Clear();
   Mutated();
 }
 
-static nsCSSValueList*
+static nsCSSValueSharedList*
 CreateCSSValueList(const InfallibleTArray<TransformFunction>& aFunctions)
 {
   nsAutoPtr<nsCSSValueList> result;
   nsCSSValueList** resultTail = getter_Transfers(result);
   for (uint32_t i = 0; i < aFunctions.Length(); i++) {
     nsRefPtr<nsCSSValue::Array> arr;
     switch (aFunctions[i].type()) {
       case TransformFunction::TRotationX:
@@ -332,17 +332,17 @@ CreateCSSValueList(const InfallibleTArra
       default:
         NS_ASSERTION(false, "All functions should be implemented?");
     }
   }
   if (aFunctions.Length() == 0) {
     result = new nsCSSValueList();
     result->mValue.SetNoneValue();
   }
-  return result.forget();
+  return new nsCSSValueSharedList(result.forget());
 }
 
 void
 Layer::SetAnimations(const AnimationArray& aAnimations)
 {
   MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) SetAnimations", this));
 
   mAnimations = aAnimations;
@@ -380,23 +380,21 @@ Layer::SetAnimations(const AnimationArra
     InfallibleTArray<nsStyleAnimation::Value>& endValues = data->mEndValues;
     for (uint32_t j = 0; j < mAnimations[i].segments().Length(); j++) {
       const AnimationSegment& segment = mAnimations[i].segments()[j];
       nsStyleAnimation::Value* startValue = startValues.AppendElement();
       nsStyleAnimation::Value* endValue = endValues.AppendElement();
       if (segment.endState().type() == Animatable::TArrayOfTransformFunction) {
         const InfallibleTArray<TransformFunction>& startFunctions =
           segment.startState().get_ArrayOfTransformFunction();
-        startValue->SetAndAdoptCSSValueListValue(CreateCSSValueList(startFunctions),
-                                                 nsStyleAnimation::eUnit_Transform);
+        startValue->SetTransformValue(CreateCSSValueList(startFunctions));
 
         const InfallibleTArray<TransformFunction>& endFunctions =
           segment.endState().get_ArrayOfTransformFunction();
-        endValue->SetAndAdoptCSSValueListValue(CreateCSSValueList(endFunctions),
-                                               nsStyleAnimation::eUnit_Transform);
+        endValue->SetTransformValue(CreateCSSValueList(endFunctions));
       } else {
         NS_ASSERTION(segment.endState().type() == Animatable::Tfloat,
                      "Unknown Animatable type");
         startValue->SetFloatValue(segment.startState().get_float());
         endValue->SetFloatValue(segment.endState().get_float());
       }
     }
   }
--- a/gfx/layers/Makefile.in
+++ b/gfx/layers/Makefile.in
@@ -20,18 +20,8 @@ include $(topsrcdir)/config/rules.mk
 CXXFLAGS += \
         -I$(ANDROID_SOURCE)/frameworks/base/include/media/stagefright \
         -I$(ANDROID_SOURCE)/frameworks/base/include/media/stagefright/openmax \
         -I$(ANDROID_SOURCE)/frameworks/av/include/media/stagefright \
         -I$(ANDROID_SOURCE)/frameworks/native/include/media/openmax \
         $(NULL)
 
 CXXFLAGS += $(MOZ_CAIRO_CFLAGS) $(TK_CFLAGS)
-
-ifdef _MSC_VER
-ifeq ($(CPU_ARCH),x86_64)
-# Workaround compiler bug (Bug 795594)
-NO_PROFILE_GUIDED_OPTIMIZE := \
-  LayerTreeInvalidation.cpp \
-  Layers.cpp \
-  $(NULL)
-endif
-endif
--- a/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp
+++ b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp
@@ -30,21 +30,20 @@ MacIOSurfaceTextureSourceBasic::GetSize(
 
 gfx::SurfaceFormat
 MacIOSurfaceTextureSourceBasic::GetFormat() const
 {
   return mSurface->HasAlpha() ? gfx::FORMAT_R8G8B8A8 : gfx::FORMAT_B8G8R8X8;
 }
 
 MacIOSurfaceTextureHostBasic::MacIOSurfaceTextureHostBasic(
-    uint64_t aID,
     TextureFlags aFlags,
     const SurfaceDescriptorMacIOSurface& aDescriptor
 )
-  : TextureHost(aID, aFlags)
+  : TextureHost(aFlags)
 {
   mSurface = MacIOSurface::LookupSurface(aDescriptor.surface(),
                                          aDescriptor.scaleFactor(),
                                          aDescriptor.hasAlpha());
 }
 
 gfx::SourceSurface*
 MacIOSurfaceTextureSourceBasic::GetSurface()
--- a/gfx/layers/basic/MacIOSurfaceTextureHostBasic.h
+++ b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.h
@@ -51,19 +51,18 @@ protected:
 /**
  * A TextureHost for shared MacIOSurface
  *
  * Most of the logic actually happens in MacIOSurfaceTextureSourceBasic.
  */
 class MacIOSurfaceTextureHostBasic : public TextureHost
 {
 public:
-  MacIOSurfaceTextureHostBasic(uint64_t aID,
-                             TextureFlags aFlags,
-                             const SurfaceDescriptorMacIOSurface& aDescriptor);
+  MacIOSurfaceTextureHostBasic(TextureFlags aFlags,
+                               const SurfaceDescriptorMacIOSurface& aDescriptor);
 
   virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
 
   virtual bool Lock() MOZ_OVERRIDE;
 
   virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE;
 
   virtual NewTextureSource* GetTextureSources() MOZ_OVERRIDE
--- a/gfx/layers/basic/TextureHostBasic.cpp
+++ b/gfx/layers/basic/TextureHostBasic.cpp
@@ -8,33 +8,32 @@
 
 using namespace mozilla::gl;
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace layers {
 
 TemporaryRef<TextureHost>
-CreateTextureHostBasic(uint64_t aID,
-                       const SurfaceDescriptor& aDesc,
+CreateTextureHostBasic(const SurfaceDescriptor& aDesc,
                        ISurfaceAllocator* aDeallocator,
                        TextureFlags aFlags)
 {
   RefPtr<TextureHost> result;
   switch (aDesc.type()) {
 #ifdef XP_MACOSX
     case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface: {
       const SurfaceDescriptorMacIOSurface& desc =
         aDesc.get_SurfaceDescriptorMacIOSurface();
-      result = new MacIOSurfaceTextureHostBasic(aID, aFlags, desc);
+      result = new MacIOSurfaceTextureHostBasic(aFlags, desc);
       break;
     }
 #endif
     default: {
-      result = CreateBackendIndependentTextureHost(aID, aDesc, aDeallocator, aFlags);
+      result = CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
       break;
     }
   }
 
   return result;
 }
 
 } // namespace layers
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -53,17 +53,17 @@ CanvasClient::CreateCanvasClient(CanvasC
   return new CanvasClient2D(aForwarder, aFlags);
 }
 
 void
 CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
 {
   if (mBuffer &&
       (mBuffer->IsImmutable() || mBuffer->GetSize() != aSize)) {
-    RemoveTextureClient(mBuffer);
+    mBuffer->ForceRemove();
     mBuffer = nullptr;
   }
 
   bool bufferCreated = false;
   if (!mBuffer) {
     bool isOpaque = (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE);
     gfxContentType contentType = isOpaque
                                                 ? GFX_CONTENT_COLOR
@@ -102,24 +102,16 @@ CanvasClient2D::Update(gfx::IntSize aSiz
 TemporaryRef<BufferTextureClient>
 CanvasClient2D::CreateBufferTextureClient(gfx::SurfaceFormat aFormat, TextureFlags aFlags)
 {
   return CompositableClient::CreateBufferTextureClient(aFormat,
                                                        mTextureInfo.mTextureFlags | aFlags);
 }
 
 void
-CanvasClient2D::OnActorDestroy()
-{
-  if (mBuffer) {
-    mBuffer->OnActorDestroy();
-  }
-}
-
-void
 DeprecatedCanvasClient2D::Updated()
 {
   mForwarder->UpdateTexture(this, 1, mDeprecatedTextureClient->LockSurfaceDescriptor());
 }
 
 
 DeprecatedCanvasClient2D::DeprecatedCanvasClient2D(CompositableForwarder* aFwd,
                                                    TextureFlags aFlags)
@@ -161,24 +153,16 @@ DeprecatedCanvasClient2D::Update(gfx::In
   }
 
   gfxASurface* surface = mDeprecatedTextureClient->LockSurface();
   aLayer->UpdateSurface(surface);
   mDeprecatedTextureClient->Unlock();
 }
 
 void
-DeprecatedCanvasClient2D::OnActorDestroy()
-{
-  if (mDeprecatedTextureClient) {
-    mDeprecatedTextureClient->OnActorDestroy();
-  }
-}
-
-void
 DeprecatedCanvasClientSurfaceStream::Updated()
 {
   mForwarder->UpdateTextureNoSwap(this, 1, mDeprecatedTextureClient->LockSurfaceDescriptor());
 }
 
 
 DeprecatedCanvasClientSurfaceStream::DeprecatedCanvasClientSurfaceStream(CompositableForwarder* aFwd,
                                                                          TextureFlags aFlags)
@@ -236,18 +220,10 @@ DeprecatedCanvasClientSurfaceStream::Upd
     // Ref this so the SurfaceStream doesn't disappear unexpectedly. The
     // Compositor will need to unref it when finished.
     aLayer->mGLContext->AddRef();
   }
 
   aLayer->Painted();
 }
 
-void
-DeprecatedCanvasClientSurfaceStream::OnActorDestroy()
-{
-  if (mDeprecatedTextureClient) {
-    mDeprecatedTextureClient->OnActorDestroy();
-  }
-}
-
 }
 }
--- a/gfx/layers/client/CanvasClient.h
+++ b/gfx/layers/client/CanvasClient.h
@@ -86,18 +86,16 @@ public:
   CreateBufferTextureClient(gfx::SurfaceFormat aFormat,
                             TextureFlags aFlags = TEXTURE_FLAGS_DEFAULT) MOZ_OVERRIDE;
 
   virtual void OnDetach() MOZ_OVERRIDE
   {
     mBuffer = nullptr;
   }
 
-  virtual void OnActorDestroy() MOZ_OVERRIDE;
-
 private:
   RefPtr<TextureClient> mBuffer;
 };
 
 class DeprecatedCanvasClient2D : public CanvasClient
 {
 public:
   DeprecatedCanvasClient2D(CompositableForwarder* aLayerForwarder,
@@ -112,18 +110,16 @@ public:
   virtual void Updated() MOZ_OVERRIDE;
 
   virtual void SetDescriptorFromReply(TextureIdentifier aTextureId,
                                       const SurfaceDescriptor& aDescriptor) MOZ_OVERRIDE
   {
     mDeprecatedTextureClient->SetDescriptorFromReply(aDescriptor);
   }
 
-  virtual void OnActorDestroy() MOZ_OVERRIDE;
-
 private:
   RefPtr<DeprecatedTextureClient> mDeprecatedTextureClient;
 };
 
 // Used for GL canvases where we don't need to do any readback, i.e., with a
 // GL backend.
 class DeprecatedCanvasClientSurfaceStream : public CanvasClient
 {
@@ -140,18 +136,16 @@ public:
   virtual void Updated() MOZ_OVERRIDE;
 
   virtual void SetDescriptorFromReply(TextureIdentifier aTextureId,
                                       const SurfaceDescriptor& aDescriptor) MOZ_OVERRIDE
   {
     mDeprecatedTextureClient->SetDescriptorFromReply(aDescriptor);
   }
 
-  virtual void OnActorDestroy() MOZ_OVERRIDE;
-
 private:
   RefPtr<DeprecatedTextureClient> mDeprecatedTextureClient;
 };
 
 }
 }
 
 #endif
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -363,27 +363,16 @@ ClientLayerManager::ForwardTransaction()
         CompositableChild* compositableChild =
           static_cast<CompositableChild*>(ots.compositableChild());
         MOZ_ASSERT(compositableChild);
 
         compositableChild->GetCompositableClient()
           ->SetDescriptorFromReply(ots.textureId(), ots.image());
         break;
       }
-      case EditReply::TReplyTextureRemoved: {
-        // XXX - to manage reuse of gralloc buffers, we'll need to add some
-        // glue code here to find the TextureClient and invoke a callback to
-        // let the camera know that the gralloc buffer is not used anymore on
-        // the compositor side and that it can reuse it.
-        const ReplyTextureRemoved& rep = reply.get_ReplyTextureRemoved();
-        CompositableClient* compositable<