Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Tue, 07 Aug 2012 15:13:28 -0700
changeset 112935 97e8aecf1730e4ac12590ce33fd339ecf0aa8ec4
parent 112934 f5c5445e826b97d37c190b86c1defc47bf27a75a (current diff)
parent 107118 e55638d4037a439147a5630bd5962ddafcd4a51b (diff)
child 112936 19f9503c6019174c0488c0d61a84d2e702aaefb0
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone17.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge from mozilla-central.
browser/app/profile/firefox.js
browser/base/content/test/test_contextmenu.html
build/autoconf/mozconfig2client-mk
build/mobile/devicemanagerSUT.py
config/autoconf.mk.in
config/xterm.str
configure.in
content/base/public/FragmentOrElement.h
content/base/public/nsIContent.h
content/base/public/nsIDocument.h
content/base/public/nsINode.h
content/base/public/nsINodeInfo.h
content/base/public/nsIXMLHttpRequest.idl
content/base/src/nsEventSource.cpp
content/base/src/nsFrameMessageManager.cpp
content/base/src/nsGenericElement.h
content/base/src/nsNodeUtils.h
content/base/src/nsObjectLoadingContent.cpp
content/base/src/nsObjectLoadingContent.h
content/base/src/nsXMLHttpRequest.cpp
content/base/test/test_bug352728.html
content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
content/canvas/test/test_canvas.html
content/events/src/nsDOMEvent.cpp
content/events/src/nsDOMNotifyAudioAvailableEvent.cpp
content/html/content/src/nsDOMStringMap.cpp
content/html/content/src/nsHTMLAudioElement.cpp
content/html/content/src/nsHTMLMenuElement.cpp
content/html/content/src/nsHTMLObjectElement.cpp
content/html/content/src/nsHTMLSharedObjectElement.cpp
content/html/document/src/PluginDocument.cpp
content/smil/nsSMILTimedElement.h
content/svg/content/src/SVGAnimatedTransformList.cpp
content/svg/content/src/SVGMotionSMILAnimationFunction.cpp
content/svg/content/src/nsSVGBoolean.cpp
content/svg/content/src/nsSVGElement.cpp
content/svg/content/src/nsSVGEnum.cpp
content/svg/content/src/nsSVGInteger.cpp
content/svg/content/src/nsSVGNumber2.cpp
content/xul/content/src/nsXULElement.cpp
content/xul/templates/src/nsXULTreeBuilder.cpp
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMNavigationTiming.h
dom/base/nsFocusManager.cpp
dom/base/nsHistory.cpp
dom/base/nsJSEnvironment.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/interfaces/base/nsIDOMWindowUtils.idl
dom/ipc/ContentChild.h
dom/ipc/CrashReporterParent.cpp
dom/ipc/Makefile.in
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/plugins/base/PluginPRLibrary.cpp
dom/plugins/base/nsNPAPIPlugin.cpp
dom/plugins/base/nsNPAPIPluginInstance.cpp
dom/plugins/base/nsPluginHost.cpp
dom/plugins/base/nsPluginStreamListenerPeer.cpp
dom/plugins/ipc/PluginInstanceChild.cpp
dom/plugins/ipc/PluginInstanceParent.cpp
dom/plugins/ipc/PluginInstanceParent.h
dom/plugins/ipc/PluginModuleParent.cpp
dom/src/jsurl/nsJSProtocolHandler.h
dom/src/storage/nsDOMStorageMemoryDB.cpp
dom/workers/RuntimeService.cpp
dom/workers/ScriptLoader.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerScope.cpp
embedding/tests/winEmbed/winEmbed.cpp
gfx/gl/GLContextProviderGLX.cpp
gfx/layers/Layers.h
gfx/layers/ipc/ShadowLayers.h
gfx/layers/opengl/LayerManagerOGL.cpp
gfx/layers/opengl/LayerManagerOGL.h
gfx/thebes/gfxContext.cpp
gfx/thebes/gfxHarfBuzzShaper.cpp
image/src/SVGDocumentWrapper.cpp
js/src/Makefile.in
js/src/MemoryMetrics.cpp
js/src/builtin/Eval.cpp
js/src/config/autoconf.mk.in
js/src/configure.in
js/src/frontend/BytecodeCompiler.cpp
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsdate.cpp
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsgc.cpp
js/src/jsmath.cpp
js/src/jsnum.cpp
js/src/jsobj.cpp
js/src/json.cpp
js/src/jsproxy.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsstr.cpp
js/src/jstypedarray.cpp
js/src/jstypedarrayinlines.h
js/src/jswrapper.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/NunboxAssembler.h
js/src/methodjit/PolyIC.cpp
js/src/methodjit/PunboxAssembler.h
js/src/vm/GlobalObject.cpp
js/xpconnect/loader/mozJSComponentLoader.cpp
js/xpconnect/src/nsXPConnect.cpp
layout/base/nsDisplayList.cpp
layout/base/nsFrameIterator.cpp
layout/base/nsFrameIterator.h
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/nsCanvasFrame.cpp
layout/generic/nsFrame.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsSelection.cpp
layout/ipc/RenderFrameParent.cpp
layout/ipc/RenderFrameParent.h
layout/style/AnimationCommon.cpp
layout/style/nsCSSParser.cpp
layout/style/nsCSSProps.cpp
layout/style/nsCSSValue.cpp
layout/style/nsComputedDOMStyle.cpp
layout/style/nsFontFaceLoader.cpp
layout/style/test/property_database.js
layout/svg/base/src/nsSVGUtils.h
layout/xul/base/src/tree/src/nsTreeContentView.cpp
memory/mozjemalloc/jemalloc.c
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/Makefile.in
mobile/android/chrome/content/browser.js
modules/libpref/src/init/all.js
netwerk/base/public/nsNetUtil.h
netwerk/protocol/ftp/FTPChannelChild.cpp
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/HttpChannelParent.h
netwerk/protocol/http/PHttpChannel.ipdl
netwerk/protocol/http/nsHttp.h
netwerk/protocol/http/nsHttpHandler.cpp
netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp
netwerk/test/unit/test_header_Accept-Language.js
parser/html/nsHtml5TreeOpExecutor.cpp
security/manager/ssl/src/nsCertOverrideService.cpp
testing/mochitest/runtests.py
testing/testsuite-targets.mk
toolkit/components/typeaheadfind/nsTypeAheadFind.cpp
toolkit/webapps/WebappsInstaller.jsm
widget/android/nsLookAndFeel.cpp
widget/android/nsLookAndFeel.h
widget/cocoa/nsFilePicker.mm
widget/windows/nsWinGesture.cpp
xpcom/base/nsConsoleService.cpp
xpcom/build/BinaryPath.h
--- a/accessible/src/xul/XULSelectControlAccessible.cpp
+++ b/accessible/src/xul/XULSelectControlAccessible.cpp
@@ -65,49 +65,49 @@ XULSelectControlAccessible::SelectedItem
   // For XUL multi-select control
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> xulMultiSelect =
     do_QueryInterface(mSelectControl);
   if (xulMultiSelect) {
     PRInt32 length = 0;
     xulMultiSelect->GetSelectedCount(&length);
     for (PRInt32 index = 0; index < length; index++) {
       nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm;
-      xulMultiSelect->GetSelectedItem(index, getter_AddRefs(itemElm));
+      xulMultiSelect->MultiGetSelectedItem(index, getter_AddRefs(itemElm));
       nsCOMPtr<nsINode> itemNode(do_QueryInterface(itemElm));
       Accessible* item = mDoc->GetAccessible(itemNode);
       if (item)
         selectedItems->AppendElement(static_cast<nsIAccessible*>(item),
                                      false);
     }
   } else {  // Single select?
-      nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm;
-      mSelectControl->GetSelectedItem(getter_AddRefs(itemElm));
-      nsCOMPtr<nsINode> itemNode(do_QueryInterface(itemElm));
-      if(itemNode) {
-        Accessible* item = mDoc->GetAccessible(itemNode);
-        if (item)
-          selectedItems->AppendElement(static_cast<nsIAccessible*>(item),
-                                     false);
-      }
+    nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm;
+    mSelectControl->GetSelectedItem(getter_AddRefs(itemElm));
+    nsCOMPtr<nsINode> itemNode(do_QueryInterface(itemElm));
+    if (itemNode) {
+      Accessible* item = mDoc->GetAccessible(itemNode);
+      if (item)
+        selectedItems->AppendElement(static_cast<nsIAccessible*>(item),
+                                   false);
+    }
   }
 
   nsIMutableArray* items = nullptr;
   selectedItems.forget(&items);
   return items;
 }
 
 Accessible*
 XULSelectControlAccessible::GetSelectedItem(PRUint32 aIndex)
 {
   nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
     do_QueryInterface(mSelectControl);
 
   nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm;
   if (multiSelectControl)
-    multiSelectControl->GetSelectedItem(aIndex, getter_AddRefs(itemElm));
+    multiSelectControl->MultiGetSelectedItem(aIndex, getter_AddRefs(itemElm));
   else if (aIndex == 0)
     mSelectControl->GetSelectedItem(getter_AddRefs(itemElm));
 
   nsCOMPtr<nsINode> itemNode(do_QueryInterface(itemElm));
   return itemNode && mDoc ? mDoc->GetAccessible(itemNode) : nullptr;
 }
 
 PRUint32
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1167,11 +1167,13 @@ pref("pdfjs.previousHandler.alwaysAskBef
 
 // The maximum amount of decoded image data we'll willingly keep around (we
 // might keep around more than this, but we'll try to get down to this value).
 // (This is intentionally on the high side; see bug 746055.)
 pref("image.mem.max_decoded_image_kb", 256000);
 
 // Example social provider
 pref("social.manifest.motown", "{\"origin\":\"https://motown-dev.mozillalabs.com\",\"name\":\"MoTown\",\"workerURL\":\"https://motown-dev.mozillalabs.com/social/worker.js\",\"iconURL\":\"https://motown-dev.mozillalabs.com/images/motown-icon.png\",\"sidebarURL\":\"https://motown-dev.mozillalabs.com/social/sidebar\"}");
+// Comma-separated list of nsIURI::prePaths that are allowed to activate
+// built-in social functionality.
+pref("social.activation.whitelist", "https://motown-dev.mozillalabs.com");
 pref("social.sidebar.open", true);
-pref("browser.social.whitelist", "");
 pref("social.active", false);
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -108,17 +108,17 @@ let SocialUI = {
       return;
 
     // Ignore events fired in background tabs
     if (targetDoc.defaultView.top != content)
       return;
 
     // Check that the associated document's origin is in our whitelist
     let prePath = targetDoc.documentURIObject.prePath;
-    let whitelist = Services.prefs.getCharPref("browser.social.whitelist");
+    let whitelist = Services.prefs.getCharPref("social.activation.whitelist");
     if (whitelist.split(",").indexOf(prePath) == -1)
       return;
 
     // If the last event was received < 1s ago, ignore this one
     let now = Date.now();
     if (now - Social.lastEventReceived < 1000)
       return;
     Social.lastEventReceived = now;
--- a/build/ConfigStatus.py
+++ b/build/ConfigStatus.py
@@ -94,53 +94,58 @@ def shell_escape(s):
     '''
     return re.sub('''([ \t`#$^&*(){}\\|;'"<>?\[\]])''', r'\\\1', str(s)).replace('$', '$$')
 
 class ConfigEnvironment(object):
     '''A ConfigEnvironment is defined by a source directory and a build
     directory. It preprocesses files from the source directory and stores
     the result in the object directory.
 
-     There are two types of files: config files and config headers,
-     each treated through a different member function.
+    There are two types of files: config files and config headers,
+    each treated through a different member function.
 
-     Creating a ConfigEnvironment requires a few arguments:
-       - topsrcdir and topobjdir are, respectively, the top source and
-         the top object directory.
-       - defines is a list of (name, value) tuples. In autoconf, these are
-         set with AC_DEFINE and AC_DEFINE_UNQUOTED
-       - non_global_defines are a list of names appearing in defines above
-         that are not meant to be exported in ACDEFINES and ALLDEFINES (see
-         below)
-       - substs is a list of (name, value) tuples. In autoconf, these are
-         set with AC_SUBST.
+    Creating a ConfigEnvironment requires a few arguments:
+      - topsrcdir and topobjdir are, respectively, the top source and
+        the top object directory.
+      - defines is a list of (name, value) tuples. In autoconf, these are
+        set with AC_DEFINE and AC_DEFINE_UNQUOTED
+      - non_global_defines are a list of names appearing in defines above
+        that are not meant to be exported in ACDEFINES and ALLDEFINES (see
+        below)
+      - substs is a list of (name, value) tuples. In autoconf, these are
+        set with AC_SUBST.
 
-     ConfigEnvironment automatically defines two additional substs variables
-     from all the defines not appearing in non_global_defines:
-       - ACDEFINES contains the defines in the form -DNAME=VALUE, for use on
-         preprocessor command lines. The order in which defines were given
-         when creating the ConfigEnvironment is preserved.
-       - ALLDEFINES contains the defines in the form #define NAME VALUE, in
-         sorted order, for use in config files, for an automatic listing of
-         defines.
+    ConfigEnvironment automatically defines two additional substs variables
+    from all the defines not appearing in non_global_defines:
+      - ACDEFINES contains the defines in the form -DNAME=VALUE, for use on
+        preprocessor command lines. The order in which defines were given
+        when creating the ConfigEnvironment is preserved.
+      - ALLDEFINES contains the defines in the form #define NAME VALUE, in
+        sorted order, for use in config files, for an automatic listing of
+        defines.
+    and another additional subst variable from all the other substs:
+      - ALLSUBSTS contains the substs in the form NAME = VALUE, in sorted
+        order, for use in autoconf.mk. It includes ACDEFINES, but doesn't
+        include ALLDEFINES.
 
     ConfigEnvironment expects a "top_srcdir" subst to be set with the top
     source directory, in msys format on windows. It is used to derive a
     "srcdir" subst when treating config files. It can either be an absolute
     path or a path relative to the topobjdir.
     '''
 
     def __init__(self, topobjdir = '.', topsrcdir = '.',
                  defines = [], non_global_defines = [], substs = []):
         self.defines = dict(defines)
         self.substs = dict(substs)
         self.topsrcdir = topsrcdir
         self.topobjdir = topobjdir
         global_defines = [name for name, value in defines if not name in non_global_defines]
         self.substs['ACDEFINES'] = ' '.join(["-D%s=%s" % (name, shell_escape(self.defines[name])) for name in global_defines])
+        self.substs['ALLSUBSTS'] = '\n'.join(sorted(["%s = %s" % (name, self.substs[name]) for name in self.substs]))
         self.substs['ALLDEFINES'] = '\n'.join(sorted(["#define %s %s" % (name, self.defines[name]) for name in global_defines]))
 
     def get_relative_srcdir(self, file):
         '''Returns the relative source directory for the given file, always
         using / as a path separator.
         '''
         assert(isinstance(file, basestring))
         dir = posixpath.dirname(relpath(file, self.topobjdir).replace(os.sep, '/'))
--- a/build/autoconf/mozconfig2client-mk
+++ b/build/autoconf/mozconfig2client-mk
@@ -27,17 +27,17 @@ ac_add_app_options() {
   echo "# $* is used by configure (not client.mk)"
 }
 
 mk_add_options() {
   for _opt
   do
     # Escape shell characters, space, tab, dollar, quote, backslash,
     # and substitute '@<word>@' with '$(<word>)'.
-    _opt=`echo "$_opt" | sed -e 's/\([\"\\]\)/\\\1/g; s/@\([^@]*\)@/\$(\1)/g;'`
+    _opt=`echo "$_opt" | sed -e 's/\([\"\\]\)/\\\\\1/g; s/@\([^@]*\)@/\$(\1)/g;'`
     echo $_opt;
     opts="${opts:+$opts^}$_opt";
   done
 }
 
 mk_echo_options() {
   echo "Adding client.mk options from $FOUND_MOZCONFIG:" >&2
   IFS=^
--- a/build/mobile/devicemanagerSUT.py
+++ b/build/mobile/devicemanagerSUT.py
@@ -802,17 +802,18 @@ class DeviceManagerSUT(DeviceManager):
     try:
       data = self.runCmds([{ 'cmd': 'hash ' + filename }])
     except AgentError:
       return None
 
     retVal = None
     if data:
       retVal = data.strip()
-    if (self.debug >= 3): print "remote hash returned: '" + retVal + "'"
+    if self.debug >= 3:
+      print "remote hash returned: '%s'" % retVal
     return retVal
 
   # Gets the device root for the testing area on the device
   # For all devices we will use / type slashes and depend on the device-agent
   # to sort those out.  The agent will return us the device location where we
   # should store things, we will then create our /tests structure relative to
   # that returned path.
   # Structure on the device is as follows:
new file mode 100644
--- /dev/null
+++ b/build/mozconfig.vs2010-common
@@ -0,0 +1,8 @@
+# Pymake needs Windows-style paths. Use cmd.exe to hack around this.
+mk_export_correct_style() {
+  if test -n "${_PYMAKE}"; then
+    mk_add_options "export $1=$(cmd.exe //c echo %$1%)"
+  else
+    mk_add_options "export $1=$(eval echo \$$1)"
+  fi
+}
new file mode 100755
--- /dev/null
+++ b/build/pymake/mkformat.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+import sys
+import pymake.parser
+
+filename = sys.argv[1]
+source = None
+
+with open(filename, 'rU') as fh:
+    source = fh.read()
+
+statements = pymake.parser.parsestring(source, filename)
+print statements.to_source()
--- a/build/pymake/pymake/data.py
+++ b/build/pymake/pymake/data.py
@@ -57,20 +57,94 @@ def stripdotslashes(sl):
 def getindent(stack):
     return ''.ljust(len(stack) - 1)
 
 def _if_else(c, t, f):
     if c:
         return t()
     return f()
 
-class StringExpansion(object):
+
+class BaseExpansion(object):
+    """Base class for expansions.
+
+    A make expansion is the parsed representation of a string, which may
+    contain references to other elements.
+    """
+
+    @property
+    def is_static_string(self):
+        """Returns whether the expansion is composed of static string content.
+
+        This is always True for StringExpansion. It will be True for Expansion
+        only if all elements of that Expansion are static strings.
+        """
+        raise Exception('Must be implemented in child class.')
+
+    def functions(self, descend=False):
+        """Obtain all functions inside this expansion.
+
+        This is a generator for pymake.functions.Function instances.
+
+        By default, this only returns functions existing as the primary
+        elements of this expansion. If `descend` is True, it will descend into
+        child expansions and extract all functions in the tree.
+        """
+        # An empty generator. Yeah, it's weird.
+        for x in []:
+            yield x
+
+    def variable_references(self, descend=False):
+        """Obtain all variable references in this expansion.
+
+        This is a generator for pymake.functionsVariableRef instances.
+
+        To retrieve the names of variables, simply query the `vname` field on
+        the returned instances. Most of the time these will be StringExpansion
+        instances.
+        """
+        for f in self.functions(descend=descend):
+            if not isinstance(f, functions.VariableRef):
+                continue
+
+            yield f
+
+    @property
+    def is_filesystem_dependent(self):
+        """Whether this expansion may query the filesystem for evaluation.
+
+        This effectively asks "is any function in this expansion dependent on
+        the filesystem.
+        """
+        for f in self.functions(descend=True):
+            if f.is_filesystem_dependent:
+                return True
+
+        return False
+
+    @property
+    def is_shell_dependent(self):
+        """Whether this expansion may invoke a shell for evaluation."""
+
+        for f in self.functions(descend=True):
+            if isinstance(f, functions.ShellFunction):
+                return True
+
+        return False
+
+
+class StringExpansion(BaseExpansion):
+    """An Expansion representing a static string.
+
+    This essentially wraps a single str instance.
+    """
+
     __slots__ = ('loc', 's',)
     simple = True
-    
+
     def __init__(self, s, loc):
         assert isinstance(s, str)
         self.s = s
         self.loc = loc
 
     def lstrip(self):
         self.s = self.s.lstrip()
 
@@ -89,29 +163,55 @@ class StringExpansion(object):
     def resolvesplit(self, i, j, k=None):
         return self.s.split()
 
     def clone(self):
         e = Expansion(self.loc)
         e.appendstr(self.s)
         return e
 
+    @property
+    def is_static_string(self):
+        return True
+
     def __len__(self):
         return 1
 
     def __getitem__(self, i):
         assert i == 0
         return self.s, False
 
     def __repr__(self):
         return "Exp<%s>(%r)" % (self.loc, self.s)
 
-class Expansion(list):
-    """
-    A representation of expanded data, such as that for a recursively-expanded variable, a command, etc.
+    def __eq__(self, other):
+        """We only compare the string contents."""
+        return self.s == other
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def to_source(self, escape_variables=False, escape_comments=False):
+        s = self.s
+
+        if escape_comments:
+            s = s.replace('#', '\\#')
+
+        if escape_variables:
+            return s.replace('$', '$$')
+
+        return s
+
+
+class Expansion(BaseExpansion, list):
+    """A representation of expanded data.
+
+    This is effectively an ordered list of StringExpansion and
+    pymake.function.Function instances. Every item in the collection appears in
+    the same context in a make file.
     """
 
     __slots__ = ('loc',)
     simple = False
 
     def __init__(self, loc=None):
         # A list of (element, isfunc) tuples
         # element is either a string or a function
@@ -227,19 +327,101 @@ class Expansion(list):
     def resolvestr(self, makefile, variables, setting=[]):
         fd = StringIO()
         self.resolve(makefile, variables, fd, setting)
         return fd.getvalue()
 
     def resolvesplit(self, makefile, variables, setting=[]):
         return self.resolvestr(makefile, variables, setting).split()
 
+    @property
+    def is_static_string(self):
+        """An Expansion is static if all its components are strings, not
+        functions."""
+        for e, is_func in self:
+            if is_func:
+                return False
+
+        return True
+
+    def functions(self, descend=False):
+        for e, is_func in self:
+            if is_func:
+                yield e
+
+            if descend:
+                for exp in e.expansions(descend=True):
+                    for f in exp.functions(descend=True):
+                        yield f
+
     def __repr__(self):
         return "<Expansion with elements: %r>" % ([e for e, isfunc in self],)
 
+    def to_source(self, escape_variables=False, escape_comments=False):
+        parts = []
+        for e, is_func in self:
+            if is_func:
+                parts.append(e.to_source())
+                continue
+
+            if escape_variables:
+                parts.append(e.replace('$', '$$'))
+                continue
+
+            parts.append(e)
+
+        return ''.join(parts)
+
+    def __eq__(self, other):
+        if not isinstance(other, (Expansion, StringExpansion)):
+            return False
+
+        # Expansions are equivalent if adjacent string literals normalize to
+        # the same value. So, we must normalize before any comparisons are
+        # made.
+        a = self.clone().finish()
+
+        if isinstance(other, StringExpansion):
+            if isinstance(a, StringExpansion):
+                return a == other
+
+            # A normalized Expansion != StringExpansion.
+            return False
+
+        b = other.clone().finish()
+
+        # b could be a StringExpansion now.
+        if isinstance(b, StringExpansion):
+            if isinstance(a, StringExpansion):
+                return a == b
+
+            # Our normalized Expansion != normalized StringExpansion.
+            return False
+
+        if len(a) != len(b):
+            return False
+
+        for i in xrange(len(self)):
+            e1, is_func1 = a[i]
+            e2, is_func2 = b[i]
+
+            if is_func1 != is_func2:
+                return False
+
+            if type(e1) != type(e2):
+                return False
+
+            if e1 != e2:
+                return False
+
+        return True
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
 class Variables(object):
     """
     A mapping from variable names to variables. Variables have flavor, source, and value. The value is an 
     expansion object.
     """
 
     __slots__ = ('parent', '_map')
 
@@ -1537,16 +1719,20 @@ class Makefile(object):
             for r in t.rules:
                 for p in r.prerequisites:
                     self.gettarget(p).explicit = True
 
         np = self.gettarget('.NOTPARALLEL')
         if len(np.rules):
             self.context = process.getcontext(1)
 
+        flavor, source, value = self.variables.get('.DEFAULT_GOAL')
+        if value is not None:
+            self.defaulttarget = value.resolvestr(self, self.variables, ['.DEFAULT_GOAL']).strip()
+
         self.error = False
 
     def include(self, path, required=True, weak=False, loc=None):
         """
         Include the makefile at `path`.
         """
         self.included.append((path, required))
         fspath = util.normaljoin(self.workdir, path)
--- a/build/pymake/pymake/functions.py
+++ b/build/pymake/pymake/functions.py
@@ -4,16 +4,31 @@ Makefile functions.
 
 import parser, util
 import subprocess, os, logging
 from globrelative import glob
 from cStringIO import StringIO
 
 log = logging.getLogger('pymake.data')
 
+def emit_expansions(descend, *expansions):
+    """Helper function to emit all expansions within an input set."""
+    for expansion in expansions:
+        yield expansion
+
+        if not descend or not isinstance(expansion, list):
+            continue
+
+        for e, is_func in expansion:
+            if is_func:
+                for exp in e.expansions(True):
+                    yield exp
+            else:
+                yield e
+
 class Function(object):
     """
     An object that represents a function call. This class is always subclassed
     with the following methods and attributes:
 
     minargs = minimum # of arguments
     maxargs = maximum # of arguments (0 means unlimited)
 
@@ -39,51 +54,162 @@ class Function(object):
             raise data.DataError("Not enough arguments to function %s, requires %s" % (self.name, self.minargs), self.loc)
 
         assert self.maxargs == 0 or argc <= self.maxargs, "Parser screwed up, gave us too many args"
 
     def append(self, arg):
         assert isinstance(arg, (data.Expansion, data.StringExpansion))
         self._arguments.append(arg)
 
+    def to_source(self):
+        """Convert the function back to make file "source" code."""
+        if not hasattr(self, 'name'):
+            raise Exception("%s must implement to_source()." % self.__class__)
+
+        # The default implementation simply prints the function name and all
+        # the arguments joined by a comma.
+        # According to the GNU make manual Section 8.1, whitespace around
+        # arguments is *not* part of the argument's value. So, we trim excess
+        # white space so we have consistent behavior.
+        args = []
+        curly = False
+        for i, arg in enumerate(self._arguments):
+            arg = arg.to_source()
+
+            if i == 0:
+                arg = arg.lstrip()
+
+            # Are balanced parens even OK?
+            if arg.count('(') != arg.count(')'):
+                curly = True
+
+            args.append(arg)
+
+        if curly:
+            return '${%s %s}' % (self.name, ','.join(args))
+
+        return '$(%s %s)' % (self.name, ','.join(args))
+
+    def expansions(self, descend=False):
+        """Obtain all expansions contained within this function.
+
+        By default, only expansions directly part of this function are
+        returned. If descend is True, we will descend into child expansions and
+        return all of the composite parts.
+
+        This is a generator for pymake.data.BaseExpansion instances.
+        """
+        # Our default implementation simply returns arguments. More advanced
+        # functions like variable references may need their own implementation.
+        return emit_expansions(descend, *self._arguments)
+
+    @property
+    def is_filesystem_dependent(self):
+        """Exposes whether this function depends on the filesystem for results.
+
+        If True, the function touches the filesystem as part of evaluation.
+
+        This only tests whether the function itself uses the filesystem. If
+        this function has arguments that are functions that touch the
+        filesystem, this will return False.
+        """
+        return False
+
     def __len__(self):
         return len(self._arguments)
 
     def __repr__(self):
         return "%s<%s>(%r)" % (
             self.__class__.__name__, self.loc,
             ','.join([repr(a) for a in self._arguments]),
             )
 
+    def __eq__(self, other):
+        if not hasattr(self, 'name'):
+            raise Exception("%s must implement __eq__." % self.__class__)
+
+        if type(self) != type(other):
+            return False
+
+        if self.name != other.name:
+            return False
+
+        if len(self._arguments) != len(other._arguments):
+            return False
+
+        for i in xrange(len(self._arguments)):
+            # According to the GNU make manual Section 8.1, whitespace around
+            # arguments is *not* part of the argument's value. So, we do a
+            # whitespace-agnostic comparison.
+            if i == 0:
+                a = self._arguments[i]
+                a.lstrip()
+
+                b = other._arguments[i]
+                b.lstrip()
+
+                if a != b:
+                    return False
+
+                continue
+
+            if self._arguments[i] != other._arguments[i]:
+                return False
+
+        return True
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
 class VariableRef(Function):
+    AUTOMATIC_VARIABLES = set(['@', '%', '<', '?', '^', '+', '|', '*'])
+
     __slots__ = ('vname', 'loc')
 
     def __init__(self, loc, vname):
         self.loc = loc
         assert isinstance(vname, (data.Expansion, data.StringExpansion))
         self.vname = vname
-        
+
     def setup(self):
         assert False, "Shouldn't get here"
 
     def resolve(self, makefile, variables, fd, setting):
         vname = self.vname.resolvestr(makefile, variables, setting)
         if vname in setting:
             raise data.DataError("Setting variable '%s' recursively references itself." % (vname,), self.loc)
 
         flavor, source, value = variables.get(vname)
         if value is None:
             log.debug("%s: variable '%s' was not set" % (self.loc, vname))
             return
 
         value.resolve(makefile, variables, fd, setting + [vname])
 
+    def to_source(self):
+        if isinstance(self.vname, data.StringExpansion):
+            if self.vname.s in self.AUTOMATIC_VARIABLES:
+                return '$%s' % self.vname.s
+
+            return '$(%s)' % self.vname.s
+
+        return '$(%s)' % self.vname.to_source()
+
+    def expansions(self, descend=False):
+        return emit_expansions(descend, self.vname)
+
     def __repr__(self):
         return "VariableRef<%s>(%r)" % (self.loc, self.vname)
 
+    def __eq__(self, other):
+        if not isinstance(other, VariableRef):
+            return False
+
+        return self.vname == other.vname
+
 class SubstitutionRef(Function):
     """$(VARNAME:.c=.o) and $(VARNAME:%.c=%.o)"""
 
     __slots__ = ('loc', 'vname', 'substfrom', 'substto')
 
     def __init__(self, loc, varname, substfrom, substto):
         self.loc = loc
         self.vname = varname
@@ -109,20 +235,37 @@ class SubstitutionRef(Function):
         f = data.Pattern(substfrom)
         if not f.ispattern():
             f = data.Pattern('%' + substfrom)
             substto = '%' + substto
 
         fd.write(' '.join([f.subst(substto, word, False)
                            for word in value.resolvesplit(makefile, variables, setting + [vname])]))
 
+    def to_source(self):
+        return '$(%s:%s=%s)' % (
+            self.vname.to_source(),
+            self.substfrom.to_source(),
+            self.substto.to_source())
+
+    def expansions(self, descend=False):
+        return emit_expansions(descend, self.vname, self.substfrom,
+                self.substto)
+
     def __repr__(self):
         return "SubstitutionRef<%s>(%r:%r=%r)" % (
             self.loc, self.vname, self.substfrom, self.substto,)
 
+    def __eq__(self, other):
+        if not isinstance(other, SubstitutionRef):
+            return False
+
+        return self.vname == other.vname and self.substfrom == other.substfrom \
+                and self.substto == other.substto
+
 class SubstFunction(Function):
     name = 'subst'
     minargs = 3
     maxargs = 3
 
     __slots__ = Function.__slots__
 
     def resolve(self, makefile, variables, fd, setting):
@@ -347,29 +490,29 @@ class BasenameFunction(Function):
                 base = suffix
 
             yield dir + base
 
     def resolve(self, makefile, variables, fd, setting):
         util.joiniter(fd, self.basenames(self._arguments[0].resolvesplit(makefile, variables, setting)))
 
 class AddSuffixFunction(Function):
-    name = 'addprefix'
+    name = 'addsuffix'
     minargs = 2
     maxargs = 2
 
     __slots__ = Function.__slots__
 
     def resolve(self, makefile, variables, fd, setting):
         suffix = self._arguments[0].resolvestr(makefile, variables, setting)
 
         fd.write(' '.join([w + suffix for w in self._arguments[1].resolvesplit(makefile, variables, setting)]))
 
 class AddPrefixFunction(Function):
-    name = 'addsuffix'
+    name = 'addprefix'
     minargs = 2
     maxargs = 2
 
     def resolve(self, makefile, variables, fd, setting):
         prefix = self._arguments[0].resolvestr(makefile, variables, setting)
 
         fd.write(' '.join([prefix + w for w in self._arguments[1].resolvesplit(makefile, variables, setting)]))
 
@@ -402,27 +545,32 @@ class WildcardFunction(Function):
 
     def resolve(self, makefile, variables, fd, setting):
         patterns = self._arguments[0].resolvesplit(makefile, variables, setting)
 
         fd.write(' '.join([x.replace('\\','/')
                            for p in patterns
                            for x in glob(makefile.workdir, p)]))
 
-    __slots__ = Function.__slots__
+    @property
+    def is_filesystem_dependent(self):
+        return True
 
 class RealpathFunction(Function):
     name = 'realpath'
     minargs = 1
     maxargs = 1
 
     def resolve(self, makefile, variables, fd, setting):
         fd.write(' '.join([os.path.realpath(os.path.join(makefile.workdir, path)).replace('\\', '/')
                            for path in self._arguments[0].resolvesplit(makefile, variables, setting)]))
 
+    def is_filesystem_dependent(self):
+        return True
+
 class AbspathFunction(Function):
     name = 'abspath'
     minargs = 1
     maxargs = 1
 
     __slots__ = Function.__slots__
 
     def resolve(self, makefile, variables, fd, setting):
--- a/build/pymake/pymake/parserdata.py
+++ b/build/pymake/pymake/parserdata.py
@@ -97,21 +97,36 @@ def parsecommandlineargs(args):
 
     return stmts, r, ' '.join(overrides)
 
 class Statement(object):
     """
     Represents parsed make file syntax.
 
     This is an abstract base class. Child classes are expected to implement
-    `execute()`.
+    basic methods defined below.
     """
 
     def execute(self, makefile, context):
-        raise Exception("Must implement execute() in child classes.")
+        """Executes this Statement within a make file execution context."""
+        raise Exception("%s must implement execute()." % self.__class__)
+
+    def to_source(self):
+        """Obtain the make file "source" representation of the Statement.
+
+        This converts an individual Statement back to a string that can again
+        be parsed into this Statement.
+        """
+        raise Exception("%s must implement to_source()." % self.__class__)
+
+    def __eq__(self, other):
+        raise Exception("%s must implement __eq__." % self.__class__)
+
+    def __ne__(self, other):
+        return self.__eq__(other)
 
 class DummyRule(object):
     __slots__ = ()
 
     def addcommand(self, r):
         pass
 
 class Rule(Statement):
@@ -167,16 +182,39 @@ class Rule(Statement):
 
             makefile.foundtarget(targets[0].gettarget())
 
         context.currule = rule
 
     def dump(self, fd, indent):
         print >>fd, "%sRule %s: %s" % (indent, self.targetexp, self.depexp)
 
+    def to_source(self):
+        sep = ':'
+
+        if self.doublecolon:
+            sep = '::'
+
+        deps = self.depexp.to_source()
+        if len(deps) > 0 and not deps[0].isspace():
+            sep += ' '
+
+        return '\n%s%s%s' % (
+            self.targetexp.to_source(escape_variables=True),
+            sep,
+            deps)
+
+    def __eq__(self, other):
+        if not isinstance(other, Rule):
+            return False
+
+        return self.targetexp == other.targetexp \
+                and self.depexp == other.depexp \
+                and self.doublecolon == other.doublecolon
+
 class StaticPatternRule(Statement):
     """
     Static pattern rules are rules which specify multiple targets based on a
     string pattern.
 
     See https://www.gnu.org/software/make/manual/make.html#Static-Pattern
 
     They are like `Rule` instances except an added property, `patternexp` is
@@ -222,16 +260,43 @@ class StaticPatternRule(Statement):
             makefile.gettarget(t).addrule(data.PatternRuleInstance(rule, '', stem, pattern.ismatchany()))
 
         makefile.foundtarget(targets[0])
         context.currule = rule
 
     def dump(self, fd, indent):
         print >>fd, "%sStaticPatternRule %s: %s: %s" % (indent, self.targetexp, self.patternexp, self.depexp)
 
+    def to_source(self):
+        sep = ':'
+
+        if self.doublecolon:
+            sep = '::'
+
+        pattern = self.patternexp.to_source()
+        deps = self.depexp.to_source()
+
+        if len(pattern) > 0 and pattern[0] not in (' ', '\t'):
+            sep += ' '
+
+        return '\n%s%s%s:%s' % (
+            self.targetexp.to_source(escape_variables=True),
+            sep,
+            pattern,
+            deps)
+
+    def __eq__(self, other):
+        if not isinstance(other, StaticPatternRule):
+            return False
+
+        return self.targetexp == other.targetexp \
+                and self.patternexp == other.patternexp \
+                and self.depexp == other.depexp \
+                and self.doublecolon == other.doublecolon
+
 class Command(Statement):
     """
     Commands are things that get executed by a rule.
 
     A rule's recipe is composed of 0 or more Commands.
 
     A command is simply an expansion. Commands typically represent strings to
     be executed in a shell (e.g. via system()). Although, since make files
@@ -249,16 +314,33 @@ class Command(Statement):
         if context.weak:
             raise data.DataError("rules not allowed in includedeps", self.exp.loc)
 
         context.currule.addcommand(self.exp)
 
     def dump(self, fd, indent):
         print >>fd, "%sCommand %s" % (indent, self.exp,)
 
+    def to_source(self):
+        # Commands have some interesting quirks when it comes to source
+        # formatting. First, they can be multi-line. Second, a tab needs to be
+        # inserted at the beginning of every line. Finally, there might be
+        # variable references inside the command. This means we need to escape
+        # variable references inside command strings. Luckily, this is handled
+        # by the Expansion.
+        s = self.exp.to_source(escape_variables=True)
+
+        return '\n'.join(['\t%s' % line for line in s.split('\n')])
+
+    def __eq__(self, other):
+        if not isinstance(other, Command):
+            return False
+
+        return self.exp == other.exp
+
 class SetVariable(Statement):
     """
     Represents a variable assignment.
 
     Variable assignment comes in two different flavors.
 
     Simple assignment has the form:
 
@@ -331,27 +413,93 @@ class SetVariable(Statement):
                 e, t, o = parser.parsemakesyntax(d, 0, (), parser.iterdata)
                 value = e.resolvestr(makefile, makefile.variables)
 
             v.set(vname, flavor, self.source, value)
 
     def dump(self, fd, indent):
         print >>fd, "%sSetVariable<%s> %s %s\n%s %r" % (indent, self.valueloc, self.vnameexp, self.token, indent, self.value)
 
+    def __eq__(self, other):
+        if not isinstance(other, SetVariable):
+            return False
+
+        return self.vnameexp == other.vnameexp \
+                and self.token == other.token \
+                and self.value == other.value \
+                and self.targetexp == other.targetexp \
+                and self.source == other.source
+
+    def to_source(self):
+        chars = []
+        for i in xrange(0, len(self.value)):
+            c = self.value[i]
+
+            # Literal # is escaped in variable assignment otherwise it would be
+            # a comment.
+            if c == '#':
+                # If a backslash precedes this, we need to escape it as well.
+                if i > 0 and self.value[i-1] == '\\':
+                    chars.append('\\')
+
+                chars.append('\\#')
+                continue
+
+            chars.append(c)
+
+        value = ''.join(chars)
+
+        prefix = ''
+        if self.source == data.Variables.SOURCE_OVERRIDE:
+            prefix = 'override '
+
+        # SetVariable come in two flavors: simple and target-specific.
+
+        # We handle the target-specific syntax first.
+        if self.targetexp is not None:
+            return '%s: %s %s %s' % (
+                self.targetexp.to_source(),
+                self.vnameexp.to_source(),
+                self.token,
+                value)
+
+        # The variable could be multi-line or have leading whitespace. For
+        # regular variable assignment, whitespace after the token but before
+        # the value is ignored. If we see leading whitespace in the value here,
+        # the variable must have come from a define.
+        if value.count('\n') > 0 or (len(value) and value[0].isspace()):
+            # The parser holds the token in vnameexp for whatever reason.
+            return '%sdefine %s\n%s\nendef' % (
+                prefix,
+                self.vnameexp.to_source(),
+                value)
+
+        return '%s%s %s %s' % (
+                prefix,
+                self.vnameexp.to_source(),
+                self.token,
+                value)
+
 class Condition(object):
     """
     An abstract "condition", either ifeq or ifdef, perhaps negated.
 
     See https://www.gnu.org/software/make/manual/make.html#Conditional-Syntax
 
     Subclasses must implement:
 
     def evaluate(self, makefile)
     """
 
+    def __eq__(self, other):
+        raise Exception("%s must implement __eq__." % __class__)
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
 class EqCondition(Condition):
     """
     Represents an ifeq or ifneq conditional directive.
 
     This directive consists of two Expansions which are compared for equality.
 
     The `expected` field is a bool indicating what the condition must evaluate
     to in order for its body to be executed. If True, this is an "ifeq"
@@ -370,16 +518,24 @@ class EqCondition(Condition):
     def evaluate(self, makefile):
         r1 = self.exp1.resolvestr(makefile, makefile.variables)
         r2 = self.exp2.resolvestr(makefile, makefile.variables)
         return (r1 == r2) == self.expected
 
     def __str__(self):
         return "ifeq (expected=%s) %s %s" % (self.expected, self.exp1, self.exp2)
 
+    def __eq__(self, other):
+        if not isinstance(other, EqCondition):
+            return False
+
+        return self.exp1 == other.exp1 \
+                and self.exp2 == other.exp2 \
+                and self.expected == other.expected
+
 class IfdefCondition(Condition):
     """
     Represents an ifdef or ifndef conditional directive.
 
     This directive consists of a single expansion which represents the name of
     a variable (without the leading '$') which will be checked for definition.
 
     The `expected` field is a bool and has the same behavior as EqCondition.
@@ -399,28 +555,37 @@ class IfdefCondition(Condition):
         if value is None:
             return not self.expected
 
         return (len(value) > 0) == self.expected
 
     def __str__(self):
         return "ifdef (expected=%s) %s" % (self.expected, self.exp)
 
+    def __eq__(self, other):
+        if not isinstance(other, IfdefCondition):
+            return False
+
+        return self.exp == other.exp and self.expected == other.expected
+
 class ElseCondition(Condition):
     """
     Represents the transition between branches in a ConditionBlock.
     """
     __slots__ = ()
 
     def evaluate(self, makefile):
         return True
 
     def __str__(self):
         return "else"
 
+    def __eq__(self, other):
+        return isinstance(other, ElseCondition)
+
 class ConditionBlock(Statement):
     """
     A set of related Conditions.
 
     This is essentially a list of 2-tuples of (Condition, list(Statement)).
 
     The parser creates a ConditionBlock for all statements related to the same
     conditional group. If iterating over the parser's output, where you think
@@ -468,16 +633,116 @@ class ConditionBlock(Statement):
 
         indent2 = indent + '  '
         for c, statements in self._groups:
             print >>fd, "%s Condition %s" % (indent, c)
             statements.dump(fd, indent2)
             print >>fd, "%s ~Condition" % (indent,)
         print >>fd, "%s~ConditionBlock" % (indent,)
 
+    def to_source(self):
+        lines = []
+        index = 0
+        for condition, statements in self:
+            lines.append(ConditionBlock.condition_source(condition, index))
+            index += 1
+
+            for statement in statements:
+                lines.append(statement.to_source())
+
+        lines.append('endif')
+
+        return '\n'.join(lines)
+
+    def __eq__(self, other):
+        if not isinstance(other, ConditionBlock):
+            return False
+
+        if len(self) != len(other):
+            return False
+
+        for i in xrange(0, len(self)):
+            our_condition, our_statements = self[i]
+            other_condition, other_statements = other[i]
+
+            if our_condition != other_condition:
+                return False
+
+            if our_statements != other_statements:
+                return False
+
+        return True
+
+    @staticmethod
+    def condition_source(statement, index):
+        """Convert a condition to its source representation.
+
+        The index argument defines the index of this condition inside a
+        ConditionBlock. If it is greater than 0, an "else" will be prepended
+        to the result, if necessary.
+        """
+        prefix = ''
+        if isinstance(statement, (EqCondition, IfdefCondition)) and index > 0:
+            prefix = 'else '
+
+        if isinstance(statement, IfdefCondition):
+            s = statement.exp.s
+
+            if statement.expected:
+                return '%sifdef %s' % (prefix, s)
+
+            return '%sifndef %s' % (prefix, s)
+
+        if isinstance(statement, EqCondition):
+            args = [
+                statement.exp1.to_source(escape_comments=True),
+                statement.exp2.to_source(escape_comments=True)]
+
+            use_quotes = False
+            single_quote_present = False
+            double_quote_present = False
+            for i, arg in enumerate(args):
+                if len(arg) > 0 and (arg[0].isspace() or arg[-1].isspace()):
+                    use_quotes = True
+
+                    if "'" in arg:
+                        single_quote_present = True
+
+                    if '"' in arg:
+                        double_quote_present = True
+
+            # Quote everything if needed.
+            if single_quote_present and double_quote_present:
+                raise Exception('Cannot format condition with multiple quotes.')
+
+            if use_quotes:
+                for i, arg in enumerate(args):
+                    # Double to single quotes.
+                    if single_quote_present:
+                        args[i] = '"' + arg + '"'
+                    else:
+                        args[i] = "'" + arg + "'"
+
+            body = None
+            if use_quotes:
+                body = ' '.join(args)
+            else:
+                body = '(%s)' % ','.join(args)
+
+            if statement.expected:
+                return '%sifeq %s' % (prefix, body)
+
+            return '%sifneq %s' % (prefix, body)
+
+        if isinstance(statement, ElseCondition):
+            return 'else'
+
+        raise Exception('Unhandled Condition statement: %s' %
+                statement.__class__)
+
     def __iter__(self):
         return iter(self._groups)
 
     def __len__(self):
         return len(self._groups)
 
     def __getitem__(self, i):
         return self._groups[i]
@@ -503,16 +768,30 @@ class Include(Statement):
     def execute(self, makefile, context):
         files = self.exp.resolvesplit(makefile, makefile.variables)
         for f in files:
             makefile.include(f, self.required, loc=self.exp.loc, weak=self.weak)
 
     def dump(self, fd, indent):
         print >>fd, "%sInclude %s" % (indent, self.exp)
 
+    def to_source(self):
+        prefix = ''
+
+        if not self.required:
+            prefix = '-'
+
+        return '%sinclude %s' % (prefix, self.exp.to_source())
+
+    def __eq__(self, other):
+        if not isinstance(other, Include):
+            return False
+
+        return self.exp == other.exp and self.required == other.required
+
 class VPathDirective(Statement):
     """
     Represents the vpath directive.
 
     See https://www.gnu.org/software/make/manual/make.html#Selective-Search
     """
     __slots__ = ('exp',)
 
@@ -536,16 +815,25 @@ class VPathDirective(Statement):
                     dirs.extend((dir for dir in mpath.split(os.pathsep)
                                  if dir != ''))
                 if len(dirs):
                     makefile.addvpath(pattern, dirs)
 
     def dump(self, fd, indent):
         print >>fd, "%sVPath %s" % (indent, self.exp)
 
+    def to_source(self):
+        return 'vpath %s' % self.exp.to_source()
+
+    def __eq__(self, other):
+        if not isinstance(other, VPathDirective):
+            return False
+
+        return self.exp == other.exp
+
 class ExportDirective(Statement):
     """
     Represents the "export" directive.
 
     This is used to control exporting variables to sub makes.
 
     See https://www.gnu.org/software/make/manual/make.html#Variables_002fRecursion
 
@@ -572,16 +860,27 @@ class ExportDirective(Statement):
                 raise data.DataError("Exporting all variables is not supported", self.exp.loc)
 
         for v in vlist:
             makefile.exportedvars[v] = True
 
     def dump(self, fd, indent):
         print >>fd, "%sExport (single=%s) %s" % (indent, self.single, self.exp)
 
+    def to_source(self):
+        return ('export %s' % self.exp.to_source()).rstrip()
+
+    def __eq__(self, other):
+        if not isinstance(other, ExportDirective):
+            return False
+
+        # single is irrelevant because it just says whether the next Statement
+        # contains a variable definition.
+        return self.exp == other.exp
+
 class UnexportDirective(Statement):
     """
     Represents the "unexport" directive.
 
     This is the opposite of ExportDirective.
     """
     __slots__ = ('exp',)
 
@@ -591,16 +890,25 @@ class UnexportDirective(Statement):
     def execute(self, makefile, context):
         vlist = list(self.exp.resolvesplit(makefile, makefile.variables))
         for v in vlist:
             makefile.exportedvars[v] = False
 
     def dump(self, fd, indent):
         print >>fd, "%sUnexport %s" % (indent, self.exp)
 
+    def to_source(self):
+        return 'unexport %s' % self.exp.to_source()
+
+    def __eq__(self, other):
+        if not isinstance(other, UnexportDirective):
+            return False
+
+        return self.exp == other.exp
+
 class EmptyDirective(Statement):
     """
     Represents a standalone statement, usually an Expansion.
 
     You will encounter EmptyDirective instances if there is a function
     or similar at the top-level of a make file (e.g. outside of a rule or
     variable assignment). You can also find them as the bodies of
     ConditionBlock branches.
@@ -614,16 +922,25 @@ class EmptyDirective(Statement):
     def execute(self, makefile, context):
         v = self.exp.resolvestr(makefile, makefile.variables)
         if v.strip() != '':
             raise data.DataError("Line expands to non-empty value", self.exp.loc)
 
     def dump(self, fd, indent):
         print >>fd, "%sEmptyDirective: %s" % (indent, self.exp)
 
+    def to_source(self):
+        return self.exp.to_source()
+
+    def __eq__(self, other):
+        if not isinstance(other, EmptyDirective):
+            return False
+
+        return self.exp == other.exp
+
 class _EvalContext(object):
     __slots__ = ('currule', 'weak')
 
     def __init__(self, weak):
         self.weak = weak
 
 class StatementList(list):
     """
@@ -651,14 +968,17 @@ class StatementList(list):
         for s in self:
             s.dump(fd, indent)
 
     def __str__(self):
         fd = StringIO()
         self.dump(fd, '')
         return fd.getvalue()
 
+    def to_source(self):
+        return '\n'.join([s.to_source() for s in self])
+
 def iterstatements(stmts):
     for s in stmts:
         yield s
         if isinstance(s, ConditionBlock):
             for c, sl in s:
                 for s2 in iterstatments(sl): yield s2
--- a/build/pymake/tests/datatests.py
+++ b/build/pymake/tests/datatests.py
@@ -1,9 +1,9 @@
-import pymake.data, pymake.util
+import pymake.data, pymake.functions, pymake.util
 import unittest
 import re
 from cStringIO import StringIO
 
 def multitest(cls):
     for name in cls.testdata.iterkeys():
         def m(self, name=name):
             return self.runSingle(*self.testdata[name])
@@ -69,10 +69,169 @@ class LRUTest(unittest.TestCase):
             v = c.get(k)
             self.assertEqual(v, e)
             self.assertEqual(self.funccount, fc,
                              "funccount, iteration %i, got %i expected %i" % (i, self.funccount, fc))
             goti = tuple(c.debugitems())
             self.assertEqual(goti, di,
                              "debugitems, iteration %i, got %r expected %r" % (i, goti, di))
 
+class EqualityTest(unittest.TestCase):
+    def test_string_expansion(self):
+        s1 = pymake.data.StringExpansion('foo bar', None)
+        s2 = pymake.data.StringExpansion('foo bar', None)
+
+        self.assertEqual(s1, s2)
+
+    def test_expansion_simple(self):
+        s1 = pymake.data.Expansion(None)
+        s2 = pymake.data.Expansion(None)
+
+        self.assertEqual(s1, s2)
+
+        s1.appendstr('foo')
+        s2.appendstr('foo')
+        self.assertEqual(s1, s2)
+
+    def test_expansion_string_finish(self):
+        """Adjacent strings should normalize to same value."""
+        s1 = pymake.data.Expansion(None)
+        s2 = pymake.data.Expansion(None)
+
+        s1.appendstr('foo')
+        s2.appendstr('foo')
+
+        s1.appendstr(' bar')
+        s1.appendstr(' baz')
+        s2.appendstr(' bar baz')
+
+        self.assertEqual(s1, s2)
+
+    def test_function(self):
+        s1 = pymake.data.Expansion(None)
+        s2 = pymake.data.Expansion(None)
+
+        n1 = pymake.data.StringExpansion('FOO', None)
+        n2 = pymake.data.StringExpansion('FOO', None)
+
+        v1 = pymake.functions.VariableRef(None, n1)
+        v2 = pymake.functions.VariableRef(None, n2)
+
+        s1.appendfunc(v1)
+        s2.appendfunc(v2)
+
+        self.assertEqual(s1, s2)
+
+
+class StringExpansionTest(unittest.TestCase):
+    def test_base_expansion_interface(self):
+        s1 = pymake.data.StringExpansion('FOO', None)
+
+        self.assertTrue(s1.is_static_string)
+
+        funcs = list(s1.functions())
+        self.assertEqual(len(funcs), 0)
+
+        funcs = list(s1.functions(True))
+        self.assertEqual(len(funcs), 0)
+
+        refs = list(s1.variable_references())
+        self.assertEqual(len(refs), 0)
+
+
+class ExpansionTest(unittest.TestCase):
+    def test_is_static_string(self):
+        e1 = pymake.data.Expansion()
+        e1.appendstr('foo')
+
+        self.assertTrue(e1.is_static_string)
+
+        e1.appendstr('bar')
+        self.assertTrue(e1.is_static_string)
+
+        vname = pymake.data.StringExpansion('FOO', None)
+        func = pymake.functions.VariableRef(None, vname)
+
+        e1.appendfunc(func)
+
+        self.assertFalse(e1.is_static_string)
+
+    def test_get_functions(self):
+        e1 = pymake.data.Expansion()
+        e1.appendstr('foo')
+
+        vname1 = pymake.data.StringExpansion('FOO', None)
+        vname2 = pymake.data.StringExpansion('BAR', None)
+
+        func1 = pymake.functions.VariableRef(None, vname1)
+        func2 = pymake.functions.VariableRef(None, vname2)
+
+        e1.appendfunc(func1)
+        e1.appendfunc(func2)
+
+        funcs = list(e1.functions())
+        self.assertEqual(len(funcs), 2)
+
+        func3 = pymake.functions.SortFunction(None)
+        func3.append(vname1)
+
+        e1.appendfunc(func3)
+
+        funcs = list(e1.functions())
+        self.assertEqual(len(funcs), 3)
+
+        refs = list(e1.variable_references())
+        self.assertEqual(len(refs), 2)
+
+    def test_get_functions_descend(self):
+        e1 = pymake.data.Expansion()
+        vname1 = pymake.data.StringExpansion('FOO', None)
+        func1 = pymake.functions.VariableRef(None, vname1)
+        e2 = pymake.data.Expansion()
+        e2.appendfunc(func1)
+
+        func2 = pymake.functions.SortFunction(None)
+        func2.append(e2)
+
+        e1.appendfunc(func2)
+
+        funcs = list(e1.functions())
+        self.assertEqual(len(funcs), 1)
+
+        funcs = list(e1.functions(True))
+        self.assertEqual(len(funcs), 2)
+
+        self.assertTrue(isinstance(funcs[0], pymake.functions.SortFunction))
+
+    def test_is_filesystem_dependent(self):
+        e = pymake.data.Expansion()
+        vname1 = pymake.data.StringExpansion('FOO', None)
+        func1 = pymake.functions.VariableRef(None, vname1)
+        e.appendfunc(func1)
+
+        self.assertFalse(e.is_filesystem_dependent)
+
+        func2 = pymake.functions.WildcardFunction(None)
+        func2.append(vname1)
+        e.appendfunc(func2)
+
+        self.assertTrue(e.is_filesystem_dependent)
+
+    def test_is_filesystem_dependent_descend(self):
+        sort = pymake.functions.SortFunction(None)
+        wildcard = pymake.functions.WildcardFunction(None)
+
+        e = pymake.data.StringExpansion('foo/*', None)
+        wildcard.append(e)
+
+        e = pymake.data.Expansion(None)
+        e.appendfunc(wildcard)
+
+        sort.append(e)
+
+        e = pymake.data.Expansion(None)
+        e.appendfunc(sort)
+
+        self.assertTrue(e.is_filesystem_dependent)
+
+
 if __name__ == '__main__':
     unittest.main()
new file mode 100644
--- /dev/null
+++ b/build/pymake/tests/default-goal.mk
@@ -0,0 +1,7 @@
+not-default:
+	@echo TEST-FAIL
+
+default:
+	@echo TEST-PASS
+
+.DEFAULT_GOAL := default
new file mode 100644
--- /dev/null
+++ b/build/pymake/tests/formattingtests.py
@@ -0,0 +1,289 @@
+# This file contains test code for the formatting of parsed statements back to
+# make file "source." It essentially verifies to to_source() functions
+# scattered across the tree.
+
+import glob
+import logging
+import os.path
+import unittest
+
+from pymake.data import Expansion
+from pymake.data import StringExpansion
+from pymake.functions import BasenameFunction
+from pymake.functions import SubstitutionRef
+from pymake.functions import VariableRef
+from pymake.functions import WordlistFunction
+from pymake.parserdata import Include
+from pymake.parserdata import SetVariable
+from pymake.parser import parsestring
+from pymake.parser import SyntaxError
+
+class TestBase(unittest.TestCase):
+    pass
+
+class VariableRefTest(TestBase):
+    def test_string_name(self):
+        e = StringExpansion('foo', None)
+        v = VariableRef(None, e)
+
+        self.assertEqual(v.to_source(), '$(foo)')
+
+    def test_special_variable(self):
+        e = StringExpansion('<', None)
+        v = VariableRef(None, e)
+
+        self.assertEqual(v.to_source(), '$<')
+
+    def test_expansion_simple(self):
+        e = Expansion()
+        e.appendstr('foo')
+        e.appendstr('bar')
+
+        v = VariableRef(None, e)
+
+        self.assertEqual(v.to_source(), '$(foobar)')
+
+class StandardFunctionTest(TestBase):
+    def test_basename(self):
+        e1 = StringExpansion('foo', None)
+        v = VariableRef(None, e1)
+        e2 = Expansion(None)
+        e2.appendfunc(v)
+
+        b = BasenameFunction(None)
+        b.append(e2)
+
+        self.assertEqual(b.to_source(), '$(basename $(foo))')
+
+    def test_wordlist(self):
+        e1 = StringExpansion('foo', None)
+        e2 = StringExpansion('bar ', None)
+        e3 = StringExpansion(' baz', None)
+
+        w = WordlistFunction(None)
+        w.append(e1)
+        w.append(e2)
+        w.append(e3)
+
+        self.assertEqual(w.to_source(), '$(wordlist foo,bar , baz)')
+
+    def test_curly_brackets(self):
+        e1 = Expansion(None)
+        e1.appendstr('foo')
+
+        e2 = Expansion(None)
+        e2.appendstr('foo ( bar')
+
+        f = WordlistFunction(None)
+        f.append(e1)
+        f.append(e2)
+
+        self.assertEqual(f.to_source(), '${wordlist foo,foo ( bar}')
+
+class StringExpansionTest(TestBase):
+    def test_simple(self):
+        e = StringExpansion('foobar', None)
+        self.assertEqual(e.to_source(), 'foobar')
+
+        e = StringExpansion('$var', None)
+        self.assertEqual(e.to_source(), '$var')
+
+    def test_escaping(self):
+        e = StringExpansion('$var', None)
+        self.assertEqual(e.to_source(escape_variables=True), '$$var')
+
+        e = StringExpansion('this is # not a comment', None)
+        self.assertEqual(e.to_source(escape_comments=True),
+                         'this is \# not a comment')
+
+    def test_empty(self):
+        e = StringExpansion('', None)
+        self.assertEqual(e.to_source(), '')
+
+        e = StringExpansion(' ', None)
+        self.assertEqual(e.to_source(), ' ')
+
+class ExpansionTest(TestBase):
+    def test_single_string(self):
+        e = Expansion()
+        e.appendstr('foo')
+
+        self.assertEqual(e.to_source(), 'foo')
+
+    def test_multiple_strings(self):
+        e = Expansion()
+        e.appendstr('hello')
+        e.appendstr('world')
+
+        self.assertEqual(e.to_source(), 'helloworld')
+
+    def test_string_escape(self):
+        e = Expansion()
+        e.appendstr('$var')
+        self.assertEqual(e.to_source(), '$var')
+        self.assertEqual(e.to_source(escape_variables=True), '$$var')
+
+        e = Expansion()
+        e.appendstr('foo')
+        e.appendstr(' $bar')
+        self.assertEqual(e.to_source(escape_variables=True), 'foo $$bar')
+
+class SubstitutionRefTest(TestBase):
+    def test_simple(self):
+        name = StringExpansion('foo', None)
+        c = StringExpansion('%.c', None)
+        o = StringExpansion('%.o', None)
+        s = SubstitutionRef(None, name, c, o)
+
+        self.assertEqual(s.to_source(), '$(foo:%.c=%.o)')
+
+class SetVariableTest(TestBase):
+    def test_simple(self):
+        v = SetVariable(StringExpansion('foo', None), '=', 'bar', None, None)
+        self.assertEqual(v.to_source(), 'foo = bar')
+
+    def test_multiline(self):
+        s = 'hello\nworld'
+        foo = StringExpansion('FOO', None)
+
+        v = SetVariable(foo, '=', s, None, None)
+
+        self.assertEqual(v.to_source(), 'define FOO\nhello\nworld\nendef')
+
+    def test_multiline_immediate(self):
+        source = 'define FOO :=\nhello\nworld\nendef'
+
+        statements = parsestring(source, 'foo.mk')
+        self.assertEqual(statements.to_source(), source)
+
+    def test_target_specific(self):
+        foo = StringExpansion('FOO', None)
+        bar = StringExpansion('BAR', None)
+
+        v = SetVariable(foo, '+=', 'value', None, bar)
+
+        self.assertEqual(v.to_source(), 'BAR: FOO += value')
+
+class IncludeTest(TestBase):
+    def test_include(self):
+        e = StringExpansion('rules.mk', None)
+        i = Include(e, True, False)
+        self.assertEqual(i.to_source(), 'include rules.mk')
+
+        i = Include(e, False, False)
+        self.assertEqual(i.to_source(), '-include rules.mk')
+
+class IfdefTest(TestBase):
+    def test_simple(self):
+        source = 'ifdef FOO\nbar := $(value)\nendif'
+
+        statements = parsestring(source, 'foo.mk')
+        self.assertEqual(statements[0].to_source(), source)
+
+    def test_nested(self):
+        source = 'ifdef FOO\nifdef BAR\nhello = world\nendif\nendif'
+
+        statements = parsestring(source, 'foo.mk')
+        self.assertEqual(statements[0].to_source(), source)
+
+    def test_negation(self):
+        source = 'ifndef FOO\nbar += value\nendif'
+
+        statements = parsestring(source, 'foo.mk')
+        self.assertEqual(statements[0].to_source(), source)
+
+class IfeqTest(TestBase):
+    def test_simple(self):
+        source = 'ifeq ($(foo),bar)\nhello = $(world)\nendif'
+
+        statements = parsestring(source, 'foo.mk')
+        self.assertEqual(statements[0].to_source(), source)
+
+    def test_negation(self):
+        source = 'ifneq (foo,bar)\nhello = world\nendif'
+
+        statements = parsestring(source, 'foo.mk')
+        self.assertEqual(statements.to_source(), source)
+
+class ConditionBlocksTest(TestBase):
+    def test_mixed_conditions(self):
+        source = 'ifdef FOO\nifeq ($(FOO),bar)\nvar += $(value)\nendif\nendif'
+
+        statements = parsestring(source, 'foo.mk')
+        self.assertEqual(statements.to_source(), source)
+
+    def test_extra_statements(self):
+        source = 'ifdef FOO\nF := 1\nifdef BAR\nB += 1\nendif\nC = 1\nendif'
+
+        statements = parsestring(source, 'foo.mk')
+        self.assertEqual(statements.to_source(), source)
+
+    def test_whitespace_preservation(self):
+        source = "ifeq ' x' 'x '\n$(error stripping)\nendif"
+
+        statements = parsestring(source, 'foo.mk')
+        self.assertEqual(statements.to_source(), source)
+
+        source = 'ifneq (x , x)\n$(error stripping)\nendif'
+        statements = parsestring(source, 'foo.mk')
+        self.assertEqual(statements.to_source(),
+                'ifneq (x,x)\n$(error stripping)\nendif')
+
+class MakefileCorupusTest(TestBase):
+    """Runs the make files from the pymake corpus through the formatter.
+
+    All the above tests are child's play compared to this.
+    """
+
+    # Our reformatting isn't perfect. We ignore files with known failures until
+    # we make them work.
+    # TODO Address these formatting corner cases.
+    _IGNORE_FILES = [
+        # We are thrown off by backslashes at end of lines.
+        'comment-parsing.mk',
+        'escape-chars.mk',
+        'include-notfound.mk',
+    ]
+
+    def _get_test_files(self):
+        ourdir = os.path.dirname(os.path.abspath(__file__))
+
+        for makefile in glob.glob(os.path.join(ourdir, '*.mk')):
+            if os.path.basename(makefile) in self._IGNORE_FILES:
+                continue
+
+            source = None
+            with open(makefile, 'rU') as fh:
+                source = fh.read()
+
+            try:
+                yield (makefile, source, parsestring(source, makefile))
+            except SyntaxError:
+                continue
+
+    def test_reparse_consistency(self):
+        for filename, source, statements in self._get_test_files():
+            reformatted = statements.to_source()
+
+            # We should be able to parse the reformatted source fine.
+            new_statements = parsestring(reformatted, filename)
+
+            # If we do the formatting again, the representation shouldn't
+            # change. i.e. the only lossy change should be the original
+            # (whitespace and some semantics aren't preserved).
+            reformatted_again = new_statements.to_source()
+            self.assertEqual(reformatted, reformatted_again,
+                '%s has lossless reformat.' % filename)
+
+            self.assertEqual(len(statements), len(new_statements))
+
+            for i in xrange(0, len(statements)):
+                original = statements[i]
+                formatted = new_statements[i]
+
+                self.assertEqual(original, formatted, '%s %d: %s != %s' % (filename,
+                    i, original, formatted))
+
+if __name__ == '__main__':
+    logging.basicConfig(level=logging.DEBUG)
+    unittest.main()
new file mode 100644
--- /dev/null
+++ b/build/pymake/tests/functiontests.py
@@ -0,0 +1,54 @@
+import unittest
+
+import pymake.data
+import pymake.functions
+
+class VariableRefTest(unittest.TestCase):
+    def test_get_expansions(self):
+        e = pymake.data.StringExpansion('FOO', None)
+        f = pymake.functions.VariableRef(None, e)
+
+        exps = list(f.expansions())
+        self.assertEqual(len(exps), 1)
+
+class GetExpansionsTest(unittest.TestCase):
+    def test_get_arguments(self):
+        f = pymake.functions.SubstFunction(None)
+
+        e1 = pymake.data.StringExpansion('FOO', None)
+        e2 = pymake.data.StringExpansion('BAR', None)
+        e3 = pymake.data.StringExpansion('BAZ', None)
+
+        f.append(e1)
+        f.append(e2)
+        f.append(e3)
+
+        exps = list(f.expansions())
+        self.assertEqual(len(exps), 3)
+
+    def test_descend(self):
+        f = pymake.functions.StripFunction(None)
+
+        e = pymake.data.Expansion(None)
+
+        e1 = pymake.data.StringExpansion('FOO', None)
+        f1 = pymake.functions.VariableRef(None, e1)
+        e.appendfunc(f1)
+
+        f2 = pymake.functions.WildcardFunction(None)
+        e2 = pymake.data.StringExpansion('foo/*', None)
+        f2.append(e2)
+        e.appendfunc(f2)
+
+        f.append(e)
+
+        exps = list(f.expansions())
+        self.assertEqual(len(exps), 1)
+
+        exps = list(f.expansions(True))
+        self.assertEqual(len(exps), 3)
+
+        self.assertFalse(f.is_filesystem_dependent)
+
+if __name__ == '__main__':
+    unittest.main()
--- a/build/tests/unit-ConfigStatus.py
+++ b/build/tests/unit-ConfigStatus.py
@@ -49,30 +49,40 @@ class TestFileAvoidWrite(unittest.TestCa
             # Check that no write actually happens when writing the
             # same content as what already is in the file
             with FileAvoidWrite('file') as file:
                 file.write('content')
 
 
 class TestEnvironment(unittest.TestCase):
     def test_auto_substs(self):
-        '''Test the automatically set values of ACDEFINES and ALLDEFINES.
+        '''Test the automatically set values of ACDEFINES, ALLDEFINES
+        and ALLSUBSTS.
         '''
         env = ConfigEnvironment(
                   defines = [ ('foo', 'bar'), ('baz', 'qux 42'),
                               ('abc', 'def'), ('extra', 'foobar') ],
-                  non_global_defines = ['extra', 'ignore'])
+                  non_global_defines = ['extra', 'ignore'],
+                  substs = [ ('FOO', 'bar'), ('ABC', 'def'),
+                             ('bar', 'baz qux'), ('zzz', '"abc def"') ])
         # non_global_defines should be filtered out in ACDEFINES and
         # ALLDEFINES.
         # Original order of the defines need to be respected in ACDEFINES
-        self.assertEqual(env.substs['ACDEFINES'], '-Dfoo=bar -Dbaz=qux\\ 42 -Dabc=def')
+        self.assertEqual(env.substs['ACDEFINES'], '''-Dfoo=bar -Dbaz=qux\ 42 -Dabc=def''')
         # ALLDEFINES, on the other hand, needs to be sorted
         self.assertEqual(env.substs['ALLDEFINES'], '''#define abc def
 #define baz qux 42
 #define foo bar''')
+        # Likewise for ALLSUBSTS, which also mustn't contain ALLDEFINES
+        # but contain ACDEFINES
+        self.assertEqual(env.substs['ALLSUBSTS'], '''ABC = def
+ACDEFINES = -Dfoo=bar -Dbaz=qux\ 42 -Dabc=def
+FOO = bar
+bar = baz qux
+zzz = "abc def"''')
 
     def test_config_file(self):
         '''Test the creation of config files.
         '''
         with MockedOpen({'file.in': '''#ifdef foo
 @foo@
 @bar@
 '''}):
--- a/build/win32/mozconfig.vs2010
+++ b/build/win32/mozconfig.vs2010
@@ -1,12 +1,13 @@
 export INCLUDE=/d/msvs10/vc/include:/d/msvs10/vc/atlmfc/include:/d/sdks/v7.0/include:/d/sdks/v7.0/include/atl:/d/msvs8/VC/PlatformSDK/include:/d/sdks/dx10/include
 export LIBPATH=/d/msvs10/vc/lib:/d/msvs10/vc/atlmfc/lib:/c/WINDOWS/Microsoft.NET/Framework/v2.0.50727
 export LIB=/d/msvs10/vc/lib:/d/msvs10/vc/atlmfc/lib:/d/sdks/v7.0/lib:/d/msvs8/VC/PlatformSDK/lib:/d/msvs8/SDK/v2.0/lib:/d/mozilla-build/atlthunk_compat:/d/sdks/dx10/lib/x86
 export PATH="/d/msvs10/VSTSDB/Deploy:/d/msvs10/Common7/IDE/:/d/msvs10/VC/BIN:/d/msvs10/Common7/Tools:/d/msvs10/VC/VCPackages:${PATH}"
 export WIN32_REDIST_DIR=/d/msvs10/VC/redist/x86/Microsoft.VC100.CRT
 
+. $topsrcdir/build/mozconfig.vs2010-common
 
-mk_add_options "export LIB=$LIB"
-mk_add_options "export LIBPATH=$LIBPATH"
-mk_add_options "export PATH=$PATH"
-mk_add_options "export INCLUDE=$INCLUDE"
-mk_add_options "export WIN32_REDIST_DIR=$WIN32_REDIST_DIR"
+mk_export_correct_style LIB
+mk_export_correct_style LIBPATH
+mk_export_correct_style PATH
+mk_export_correct_style INCLUDE
+mk_export_correct_style WIN32_REDIST_DIR
--- a/build/win32/mozconfig.vs2010-win64
+++ b/build/win32/mozconfig.vs2010-win64
@@ -1,13 +1,16 @@
 export INCLUDE=/c/tools/msvs10/vc/include:/c/tools/msvs10/vc/atlmfc/include:/c/tools/sdks/v7.0/include:/c/tools/sdks/v7.0/include/atl:/c/tools/sdks/dx10/include
 export LIBPATH=/c/tools/msvs10/vc/lib:/c/tools/msvs10/vc/atlmfc/lib
 export LIB=/c/tools/msvs10/vc/lib:/c/tools/msvs10/vc/atlmfc/lib:/c/tools/sdks/v7.0/lib:/c/tools/sdks/dx10/lib
 export WIN32_REDIST_DIR=/c/tools/msvs10/VC/redist/x86/Microsoft.VC100.CRT
 export MOZ_TOOLS=C:/mozilla-build/moztools
 export PATH="/c/tools/msvs10/Common7/IDE:/c/tools/msvs10/VC/BIN:/c/tools/msvs10/Common7/Tools:/c/tools/msvs10/VC/VCPackages:/c/mozilla-build/moztools:/c/Tools/sdks/v7.0/bin:${PATH}"
 
-mk_add_options "export LIB=$LIB"
-mk_add_options "export LIBPATH=$LIBPATH"
-mk_add_options "export PATH=$PATH"
-mk_add_options "export INCLUDE=$INCLUDE"
-mk_add_options "export WIN32_REDIST_DIR=$WIN32_REDIST_DIR"
+. $topsrcdir/build/mozconfig.vs2010-common
+
+mk_export_correct_style LIB
+mk_export_correct_style LIBPATH
+mk_export_correct_style PATH
+mk_export_correct_style INCLUDE
+mk_export_correct_style WIN32_REDIST_DIR
+
 mk_add_options "export MOZ_TOOLS=$MOZ_TOOLS"
--- a/build/win64/mozconfig.vs2010
+++ b/build/win64/mozconfig.vs2010
@@ -3,14 +3,15 @@ export LIBPATH=/c/tools/msvs10/vc/lib/am
 export LIB=/c/tools/msvs10/vc/lib/amd64:/c/tools/msvs10/vc/atlmfc/lib/amd64:/c/tools/sdks/v7.0/lib/x64:/c/tools/sdks/dx10/lib/x64
 export PATH="/c/tools/msvs10/Common7/IDE:/c/tools/msvs10/VC/BIN/amd64:/c/tools/msvs10/VC/BIN/x86_amd64:/c/tools/msvs10/VC/BIN:/c/tools/msvs10/Common7/Tools:/c/tools/msvs10/VC/VCPackages:${PATH}"
 export WIN32_REDIST_DIR=/c/tools/msvs10/VC/redist/x64/Microsoft.VC100.CRT
 
 # Use 32bit linker for PGO crash bug.
 # https://connect.microsoft.com/VisualStudio/feedback/details/686117/
 export LD=/c/tools/msvs10/VC/BIN/x86_amd64/link.exe
 
+. $topsrcdir/build/mozconfig.vs2010-common
 
-mk_add_options "export LIB=$LIB"
-mk_add_options "export LIBPATH=$LIBPATH"
-mk_add_options "export PATH=$PATH"
-mk_add_options "export INCLUDE=$INCLUDE"
-mk_add_options "export WIN32_REDIST_DIR=$WIN32_REDIST_DIR"
+mk_export_correct_style LIB
+mk_export_correct_style LIBPATH
+mk_export_correct_style PATH
+mk_export_correct_style INCLUDE
+mk_export_correct_style WIN32_REDIST_DIR
--- a/client.mk
+++ b/client.mk
@@ -98,17 +98,17 @@ MOZCONFIG_MODULES := build/unix/uniq.pl
 define CR
 
 
 endef
 
 # As $(shell) doesn't preserve newlines, use sed to replace them with an
 # unlikely sequence (||), which is then replaced back to newlines by make
 # before evaluation.
-$(eval $(subst ||,$(CR),$(shell $(TOPSRCDIR)/$(MOZCONFIG_LOADER) $(TOPSRCDIR) 2> $(TOPSRCDIR)/.mozconfig.out | sed 's/$$/||/')))
+$(eval $(subst ||,$(CR),$(shell _PYMAKE=$(.PYMAKE) $(TOPSRCDIR)/$(MOZCONFIG_LOADER) $(TOPSRCDIR) 2> $(TOPSRCDIR)/.mozconfig.out | sed 's/$$/||/')))
 
 ifndef MOZ_OBJDIR
   MOZ_OBJDIR = obj-$(CONFIG_GUESS)
 else
 # On Windows Pymake builds check MOZ_OBJDIR doesn't start with "/"
   ifneq (,$(findstring mingw,$(CONFIG_GUESS)))
   ifeq (1_a,$(.PYMAKE)_$(firstword a$(subst /, ,$(MOZ_OBJDIR))))
   $(error For Windows Pymake builds, MOZ_OBJDIR must be a Windows [and not MSYS] style path.)
@@ -397,17 +397,30 @@ cleansrcdir:
 	else \
 	  echo "Removing object files from srcdir..."; \
 	  rm -fr `find . -type d \( -name .deps -print -o -name CVS \
 	          -o -exec test ! -d {}/CVS \; \) -prune \
 	          -o \( -name '*.[ao]' -o -name '*.so' \) -type f -print`; \
 	   build/autoconf/clean-config.sh; \
 	fi;
 
-## Sanity check $X and js/src/$X are in sync
+# Because SpiderMonkey can be distributed and built independently
+# of the Mozilla source tree, it contains its own copies of many of
+# the files used by the top-level Mozilla build process, from the
+# 'config' and 'build' subtrees.
+#
+# To make it simpler to keep the copies in sync, we follow the policy
+# that the SpiderMonkey copies must always be exact copies of those in
+# the containing Mozilla tree.  If you've made a change in one, it
+# belongs in the other as well.  If the change isn't right for both
+# places, then that's something to bring up with the other developers.
+#
+# Some files are reasonable to diverge; for  example,
+# js/src/config/autoconf.mk.in doesn't need most of the stuff in
+# config/autoconf.mk.in.
 .PHONY: check-sync-dirs
 check-sync-dirs: check-sync-dirs-build check-sync-dirs-config
 check-sync-dirs-%:
 	@$(PYTHON) $(TOPSRCDIR)/js/src/config/check-sync-dirs.py $(TOPSRCDIR)/js/src/$* $(TOPSRCDIR)/$*
 
 echo-variable-%:
 	@echo $($*)
 
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -1,668 +1,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/.
-
-# A netscape style .mk file for autoconf builds
-
-target          = @target@
-ac_configure_args = @ac_configure_args@
-MOZILLA_VERSION = @MOZILLA_VERSION@
-FIREFOX_VERSION	= @FIREFOX_VERSION@
-
-MOZ_BUILD_APP = @MOZ_BUILD_APP@
-MOZ_APP_NAME	= @MOZ_APP_NAME@
-MOZ_APP_DISPLAYNAME = @MOZ_APP_DISPLAYNAME@
-MOZ_APP_BASENAME = @MOZ_APP_BASENAME@
-MOZ_APP_VENDOR = @MOZ_APP_VENDOR@
-MOZ_APP_PROFILE = @MOZ_APP_PROFILE@
-MOZ_APP_ID = @MOZ_APP_ID@
-MAR_CHANNEL_ID = @MAR_CHANNEL_ID@
-ACCEPTED_MAR_CHANNEL_IDS = @ACCEPTED_MAR_CHANNEL_IDS@
-MOZ_PROFILE_MIGRATOR = @MOZ_PROFILE_MIGRATOR@
-MOZ_EXTENSION_MANAGER = @MOZ_EXTENSION_MANAGER@
-MOZ_APP_UA_NAME = @MOZ_APP_UA_NAME@
-MOZ_UA_OS_AGNOSTIC = @MOZ_UA_OS_AGNOSTIC@
-MOZ_APP_VERSION = @MOZ_APP_VERSION@
-MOZ_APP_MAXVERSION = @MOZ_APP_MAXVERSION@
-MOZ_MACBUNDLE_NAME = @MOZ_MACBUNDLE_NAME@
-MOZ_MACBUNDLE_ID = @MOZ_MACBUNDLE_ID@
-MOZ_APP_STATIC_INI = @MOZ_APP_STATIC_INI@
-
-MOZ_PKG_SPECIAL = @MOZ_PKG_SPECIAL@
-
-prefix		= @prefix@
-exec_prefix	= @exec_prefix@
-bindir		= @bindir@
-includedir	= @includedir@
-libdir		= @libdir@
-datadir		= @datadir@
-mandir		= @mandir@
-
-LIBXUL_SDK      = @LIBXUL_SDK@
-
-MOZ_FS_LAYOUT = @MOZ_FS_LAYOUT@
-
-L10NBASEDIR     = @L10NBASEDIR@
-
-LIBXUL_DIST	= @LIBXUL_DIST@
-SYSTEM_LIBXUL   = @SYSTEM_LIBXUL@
-
-XULRUNNER_STUB_NAME = @XULRUNNER_STUB_NAME@
-
-MOZ_CHROME_FILE_FORMAT	= @MOZ_CHROME_FILE_FORMAT@
-MOZ_OMNIJAR		= @MOZ_OMNIJAR@
-OMNIJAR_NAME		= @OMNIJAR_NAME@
-
-MOZ_WIDGET_TOOLKIT	= @MOZ_WIDGET_TOOLKIT@
-MOZ_GFX_OPTIMIZE_MOBILE = @MOZ_GFX_OPTIMIZE_MOBILE@
-
-MOZ_X11			= @MOZ_X11@
-
-MOZ_PANGO = @MOZ_PANGO@
-
-MOZ_JS_LIBS		   = @MOZ_JS_LIBS@
-
-MOZ_DEBUG	= @MOZ_DEBUG@
-MOZ_DEBUG_SYMBOLS = @MOZ_DEBUG_SYMBOLS@
-MOZ_DEBUG_ENABLE_DEFS		= @MOZ_DEBUG_ENABLE_DEFS@
-MOZ_DEBUG_DISABLE_DEFS	= @MOZ_DEBUG_DISABLE_DEFS@
-MOZ_DEBUG_FLAGS	= @MOZ_DEBUG_FLAGS@
-MOZ_DEBUG_LDFLAGS=@MOZ_DEBUG_LDFLAGS@
-MOZ_EXTENSIONS  = @MOZ_EXTENSIONS@
-MOZ_JSDEBUGGER  = @MOZ_JSDEBUGGER@
-MOZ_IPDL_TESTS 	= @MOZ_IPDL_TESTS@
-MOZ_MEMORY      = @MOZ_MEMORY@
-MOZ_JEMALLOC    = @MOZ_JEMALLOC@
-MOZ_PROFILING   = @MOZ_PROFILING@
-MOZ_ENABLE_PROFILER_SPS = @MOZ_ENABLE_PROFILER_SPS@
-MOZ_JPROF       = @MOZ_JPROF@
-MOZ_SHARK       = @MOZ_SHARK@
-MOZ_CALLGRIND   = @MOZ_CALLGRIND@
-MOZ_VALGRIND    = @MOZ_VALGRIND@
-MOZ_VTUNE       = @MOZ_VTUNE@
-MOZ_ETW         = @MOZ_ETW@
-DEHYDRA_PATH    = @DEHYDRA_PATH@
-
-MOZ_USING_CCACHE = @MOZ_USING_CCACHE@
-CLANG_CXX = @CLANG_CXX@
-MOZ_LINKER = @MOZ_LINKER@
-MOZ_OLD_LINKER = @MOZ_OLD_LINKER@
-MOZ_ENABLE_SZIP = @MOZ_ENABLE_SZIP@
-NS_TRACE_MALLOC = @NS_TRACE_MALLOC@
-USE_ELF_DYNSTR_GC = @USE_ELF_DYNSTR_GC@
-USE_ELF_HACK = @USE_ELF_HACK@
-STDCXX_COMPAT = @STDCXX_COMPAT@
-MOZ_LIBSTDCXX_TARGET_VERSION=@MOZ_LIBSTDCXX_TARGET_VERSION@
-MOZ_LIBSTDCXX_HOST_VERSION=@MOZ_LIBSTDCXX_HOST_VERSION@
-INCREMENTAL_LINKER = @INCREMENTAL_LINKER@
-MACOSX_DEPLOYMENT_TARGET = @MACOSX_DEPLOYMENT_TARGET@
-ENABLE_TESTS	= @ENABLE_TESTS@
-ENABLE_MARIONETTE = @ENABLE_MARIONETTE@
-IBMBIDI = @IBMBIDI@
-MOZ_UNIVERSALCHARDET = @MOZ_UNIVERSALCHARDET@
-ACCESSIBILITY = @ACCESSIBILITY@
-MOZ_BRANDING_DIRECTORY = @MOZ_BRANDING_DIRECTORY@
-MOZ_MAINTENANCE_SERVICE	= @MOZ_MAINTENANCE_SERVICE@
-MOZ_VERIFY_MAR_SIGNATURE	= @MOZ_VERIFY_MAR_SIGNATURE@
-MOZ_ENABLE_SIGNMAR	= @MOZ_ENABLE_SIGNMAR@
-MOZ_UPDATER	= @MOZ_UPDATER@
-MOZ_UPDATE_CHANNEL	= @MOZ_UPDATE_CHANNEL@
-MOZ_UPDATE_PACKAGING	= @MOZ_UPDATE_PACKAGING@
-MOZ_DISABLE_PARENTAL_CONTROLS = @MOZ_DISABLE_PARENTAL_CONTROLS@
-NS_ENABLE_TSF = @NS_ENABLE_TSF@
-MOZ_SPELLCHECK = @MOZ_SPELLCHECK@
-MOZ_ANDROID_HISTORY = @MOZ_ANDROID_HISTORY@
-MOZ_WEBSMS_BACKEND = @MOZ_WEBSMS_BACKEND@
-MOZ_JAVA_COMPOSITOR = @MOZ_JAVA_COMPOSITOR@
-MOZ_ONLY_TOUCH_EVENTS = @MOZ_ONLY_TOUCH_EVENTS@
-MOZ_PROFILELOCKING = @MOZ_PROFILELOCKING@
-MOZ_FEEDS = @MOZ_FEEDS@
-MOZ_TOOLKIT_SEARCH = @MOZ_TOOLKIT_SEARCH@
-MOZ_PLACES = @MOZ_PLACES@
-MOZ_SAFE_BROWSING = @MOZ_SAFE_BROWSING@
-MOZ_URL_CLASSIFIER = @MOZ_URL_CLASSIFIER@
-MOZ_ZIPWRITER = @MOZ_ZIPWRITER@
-MOZ_OGG = @MOZ_OGG@
-MOZ_RAW = @MOZ_RAW@
-MOZ_SYDNEYAUDIO = @MOZ_SYDNEYAUDIO@
-MOZ_SPEEX_RESAMPLER = @MOZ_SPEEX_RESAMPLER@
-MOZ_CUBEB = @MOZ_CUBEB@
-MOZ_WAVE = @MOZ_WAVE@
-MOZ_MEDIA = @MOZ_MEDIA@
-MOZ_VORBIS = @MOZ_VORBIS@
-MOZ_TREMOR = @MOZ_TREMOR@
-MOZ_OPUS = @MOZ_OPUS@
-MOZ_WEBM = @MOZ_WEBM@
-MOZ_MEDIA_PLUGINS = @MOZ_MEDIA_PLUGINS@
-MOZ_OMX_PLUGIN = @MOZ_OMX_PLUGIN@
-MOZ_GSTREAMER = @MOZ_GSTREAMER@
-MOZ_VP8 = @MOZ_VP8@
-MOZ_VP8_ERROR_CONCEALMENT = @MOZ_VP8_ERROR_CONCEALMENT@
-MOZ_VP8_ENCODER = @MOZ_VP8_ENCODER@
-VPX_AS = @VPX_AS@
-VPX_ASFLAGS = @VPX_ASFLAGS@
-VPX_DASH_C_FLAG = @VPX_DASH_C_FLAG@
-VPX_AS_CONVERSION = @VPX_AS_CONVERSION@
-VPX_ASM_SUFFIX = @VPX_ASM_SUFFIX@
-VPX_X86_ASM = @VPX_X86_ASM@
-VPX_ARM_ASM = @VPX_ARM_ASM@
-VPX_NEED_OBJ_INT_EXTRACT = @VPX_NEED_OBJ_INT_EXTRACT@
-LIBJPEG_TURBO_AS = @LIBJPEG_TURBO_AS@
-LIBJPEG_TURBO_ASFLAGS = @LIBJPEG_TURBO_ASFLAGS@
-LIBJPEG_TURBO_X86_ASM = @LIBJPEG_TURBO_X86_ASM@
-LIBJPEG_TURBO_X64_ASM = @LIBJPEG_TURBO_X64_ASM@
-LIBJPEG_TURBO_ARM_ASM = @LIBJPEG_TURBO_ARM_ASM@
-NS_PRINTING = @NS_PRINTING@
-MOZ_PDF_PRINTING = @MOZ_PDF_PRINTING@
-MOZ_CRASHREPORTER = @MOZ_CRASHREPORTER@
-MOZ_CRASHREPORTER_INJECTOR = @MOZ_CRASHREPORTER_INJECTOR@
-MOZ_HELP_VIEWER = @MOZ_HELP_VIEWER@
-MOC = @MOC@
-RCC = @RCC@
-MOZ_NSS_PATCH = @MOZ_NSS_PATCH@
-MOZ_WEBGL = @MOZ_WEBGL@
-MOZ_ANGLE_RENDERER = @MOZ_ANGLE_RENDERER@
-MOZ_DIRECTX_SDK_PATH = @MOZ_DIRECTX_SDK_PATH@
-MOZ_DIRECTX_SDK_CPU_SUFFIX = @MOZ_DIRECTX_SDK_CPU_SUFFIX@
-MOZ_D3DX9_VERSION = @MOZ_D3DX9_VERSION@
-MOZ_D3DX9_CAB = @MOZ_D3DX9_CAB@
-MOZ_D3DCOMPILER_CAB = @MOZ_D3DCOMPILER_CAB@
-MOZ_D3DX9_DLL = @MOZ_D3DX9_DLL@
-MOZ_D3DCOMPILER_DLL = @MOZ_D3DCOMPILER_DLL@
-MOZ_GL_PROVIDER = @MOZ_GL_PROVIDER@
-MOZ_GL_DEFAULT_PROVIDER = @MOZ_GL_DEFAULT_PROVIDER@
-MOZ_WEBRTC = @MOZ_WEBRTC@
-
-
-JAVA=@JAVA@
-JAVAC=@JAVAC@
-JAR=@JAR@
-
-TAR=@TAR@
-
-MAKENSISU=@MAKENSISU@
-
-# The MOZ_UI_LOCALE var is used to build a particular locale. Do *not*
-# use the var to change any binary files. Do *not* use this var unless you
-# write rules for the "clean-locale" and "locale" targets.
-MOZ_UI_LOCALE = @MOZ_UI_LOCALE@
-
-MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS = @MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS@
-MOZ_COMPONENT_NSPR_LIBS=@MOZ_COMPONENT_NSPR_LIBS@
-
-MOZ_FIX_LINK_PATHS=@MOZ_FIX_LINK_PATHS@
-
-XPCOM_FROZEN_LDOPTS=@XPCOM_FROZEN_LDOPTS@
-XPCOM_LIBS=@XPCOM_LIBS@
-LIBXUL_LIBS=@LIBXUL_LIBS@
-
-ENABLE_STRIP	= @ENABLE_STRIP@
-PKG_SKIP_STRIP	= @PKG_SKIP_STRIP@
-STRIP_FLAGS = @STRIP_FLAGS@
-
-MOZ_POST_DSO_LIB_COMMAND = @MOZ_POST_DSO_LIB_COMMAND@
-MOZ_POST_PROGRAM_COMMAND = @MOZ_POST_PROGRAM_COMMAND@
-
-MOZ_BUILD_ROOT             = @MOZ_BUILD_ROOT@
-
-MOZ_XUL                    = @MOZ_XUL@
-
-NECKO_PROTOCOLS = @NECKO_PROTOCOLS@
-NECKO_COOKIES = @NECKO_COOKIES@
-NECKO_WIFI = @NECKO_WIFI@
-MOZ_AUTH_EXTENSION = @MOZ_AUTH_EXTENSION@
-
-MOZ_NATIVE_HUNSPELL = @MOZ_NATIVE_HUNSPELL@
-MOZ_HUNSPELL_LIBS = @MOZ_HUNSPELL_LIBS@
-MOZ_HUNSPELL_CFLAGS = @MOZ_HUNSPELL_CFLAGS@
-
-MOZ_NATIVE_LIBEVENT = @MOZ_NATIVE_LIBEVENT@
-MOZ_LIBEVENT_LIBS = @MOZ_LIBEVENT_LIBS@
-MOZ_LIBEVENT_INCLUDES = @MOZ_LIBEVENT_INCLUDES@
-
-MOZ_NATIVE_LIBVPX = @MOZ_NATIVE_LIBVPX@
-MOZ_LIBVPX_LIBS = @MOZ_LIBVPX_LIBS@
-MOZ_LIBVPX_CFLAGS = @MOZ_LIBVPX_CFLAGS@
-
-MOZ_NATIVE_ZLIB	= @MOZ_NATIVE_ZLIB@
-MOZ_NATIVE_BZ2	= @MOZ_NATIVE_BZ2@
-MOZ_NATIVE_JPEG	= @MOZ_NATIVE_JPEG@
-MOZ_NATIVE_PNG	= @MOZ_NATIVE_PNG@
-MOZ_TREE_CAIRO = @MOZ_TREE_CAIRO@
-MOZ_TREE_PIXMAN = @MOZ_TREE_PIXMAN@
-
-MOZ_UPDATE_XTERM = @MOZ_UPDATE_XTERM@
-MOZ_PERMISSIONS = @MOZ_PERMISSIONS@
-MOZ_XTF = @MOZ_XTF@
-MOZ_FLEXBOX = @MOZ_FLEXBOX@
-MOZ_CAIRO_CFLAGS = @MOZ_CAIRO_CFLAGS@
-MOZ_PIXMAN_CFLAGS = @MOZ_PIXMAN_CFLAGS@
-
-MOZ_PREF_EXTENSIONS = @MOZ_PREF_EXTENSIONS@
-
-MOZ_CAIRO_LIBS = @MOZ_CAIRO_LIBS@
-MOZ_CAIRO_OSLIBS = @MOZ_CAIRO_OSLIBS@
-MOZ_PIXMAN_LIBS = @MOZ_PIXMAN_LIBS@
-
-MOZ_ENABLE_GNOMEUI = @MOZ_ENABLE_GNOMEUI@
-MOZ_GNOMEUI_CFLAGS = @MOZ_GNOMEUI_CFLAGS@
-MOZ_GNOMEUI_LIBS = @MOZ_GNOMEUI_LIBS@
-
-MOZ_ENABLE_STARTUP_NOTIFICATION = @MOZ_ENABLE_STARTUP_NOTIFICATION@
-MOZ_STARTUP_NOTIFICATION_CFLAGS = @MOZ_STARTUP_NOTIFICATION_CFLAGS@
-MOZ_STARTUP_NOTIFICATION_LIBS = @MOZ_STARTUP_NOTIFICATION_LIBS@
-
-MOZ_ENABLE_GNOMEVFS = @MOZ_ENABLE_GNOMEVFS@
-MOZ_GNOMEVFS_CFLAGS = @MOZ_GNOMEVFS_CFLAGS@
-MOZ_GNOMEVFS_LIBS = @MOZ_GNOMEVFS_LIBS@
-
-MOZ_ENABLE_GCONF = @MOZ_ENABLE_GCONF@
-MOZ_GCONF_CFLAGS = @MOZ_GCONF_CFLAGS@
-MOZ_GCONF_LIBS = @MOZ_GCONF_LIBS@
-
-MOZ_ENABLE_GNOME_COMPONENT = @MOZ_ENABLE_GNOME_COMPONENT@
-
-MOZ_ENABLE_GIO = @MOZ_ENABLE_GIO@
-MOZ_GIO_CFLAGS = @MOZ_GIO_CFLAGS@
-MOZ_GIO_LIBS = @MOZ_GIO_LIBS@
-
-MOZ_NATIVE_NSPR = @MOZ_NATIVE_NSPR@
-MOZ_NATIVE_NSS = @MOZ_NATIVE_NSS@
-
-MOZ_B2G_RIL = @MOZ_B2G_RIL@
-MOZ_B2G_BT = @MOZ_B2G_BT@
-MOZ_B2G_CAMERA = @MOZ_B2G_CAMERA@
-
-MOZ_SYS_MSG = @MOZ_SYS_MSG@
-
-MOZ_ASAN = @MOZ_ASAN@
-MOZ_CFLAGS_NSS = @MOZ_CFLAGS_NSS@
-MOZ_NO_WLZDEFS = @MOZ_NO_WLZDEFS@
-
-BUILD_CTYPES = @BUILD_CTYPES@
-
-COMPILE_ENVIRONMENT = @COMPILE_ENVIRONMENT@
-CROSS_COMPILE   = @CROSS_COMPILE@
-
-WCHAR_CFLAGS	= @WCHAR_CFLAGS@
-
-OS_CPPFLAGS	= @OS_CPPFLAGS@
-OS_CFLAGS	= @OS_CFLAGS@
-OS_CXXFLAGS	= @OS_CXXFLAGS@
-OS_LDFLAGS	= @OS_LDFLAGS@
-
-OS_COMPILE_CFLAGS = @OS_COMPILE_CFLAGS@
-OS_COMPILE_CXXFLAGS = @OS_COMPILE_CXXFLAGS@
-
-OS_LIBS		= @OS_LIBS@
-ACDEFINES	= @ACDEFINES@
-
-WARNINGS_AS_ERRORS = @WARNINGS_AS_ERRORS@
-
-MOZ_OPTIMIZE	= @MOZ_OPTIMIZE@
-MOZ_FRAMEPTR_FLAGS = @MOZ_FRAMEPTR_FLAGS@
-MOZ_OPTIMIZE_FLAGS = @MOZ_OPTIMIZE_FLAGS@
-MOZ_PGO_OPTIMIZE_FLAGS = @MOZ_PGO_OPTIMIZE_FLAGS@
-MOZ_OPTIMIZE_LDFLAGS = @MOZ_OPTIMIZE_LDFLAGS@
-MOZ_OPTIMIZE_SIZE_TWEAK = @MOZ_OPTIMIZE_SIZE_TWEAK@
-
-MOZ_ALLOW_HEAP_EXECUTE_FLAGS = @MOZ_ALLOW_HEAP_EXECUTE_FLAGS@
-
-PROFILE_GEN_CFLAGS = @PROFILE_GEN_CFLAGS@
-PROFILE_GEN_LDFLAGS = @PROFILE_GEN_LDFLAGS@
-PROFILE_USE_CFLAGS = @PROFILE_USE_CFLAGS@
-PROFILE_USE_LDFLAGS = @PROFILE_USE_LDFLAGS@
-
-XCFLAGS		= @XCFLAGS@
-XLDFLAGS	= @XLDFLAGS@
-XLIBS		= @XLIBS@
-XEXT_LIBS	= @XEXT_LIBS@
-XCOMPOSITE_LIBS	= @XCOMPOSITE_LIBS@
-XSS_LIBS	= @XSS_LIBS@
-
-MOZ_THUMB2	= @MOZ_THUMB2@
-MOZ_EGL_XRENDER_COMPOSITE	= @MOZ_EGL_XRENDER_COMPOSITE@
-
-WIN_TOP_SRC	= @WIN_TOP_SRC@
-AR		= @AR@
-AR_FLAGS	= @AR_FLAGS@
-AR_EXTRACT	= @AR_EXTRACT@
-AR_LIST		= @AR_LIST@
-AR_DELETE	= @AR_DELETE@
-AS		= @AS@
-ASFLAGS		= @ASFLAGS@
-AS_DASH_C_FLAG	= @AS_DASH_C_FLAG@
-LD		= @LD@
-RC		= @RC@
-RCFLAGS		= @RCFLAGS@
-MC		= @MC@
-WINDRES		= @WINDRES@
-IMPLIB		= @IMPLIB@
-FILTER		= @FILTER@
-BIN_FLAGS	= @BIN_FLAGS@
-MIDL		= @MIDL@
-MIDL_FLAGS	= @MIDL_FLAGS@
-_MSC_VER	= @_MSC_VER@
-
-DLL_PREFIX	= @DLL_PREFIX@
-LIB_PREFIX	= @LIB_PREFIX@
-OBJ_SUFFIX	= @OBJ_SUFFIX@
-LIB_SUFFIX	= @LIB_SUFFIX@
-DLL_SUFFIX	= @DLL_SUFFIX@
-BIN_SUFFIX	= @BIN_SUFFIX@
-ASM_SUFFIX	= @ASM_SUFFIX@
-IMPORT_LIB_SUFFIX = @IMPORT_LIB_SUFFIX@
-LIBS_DESC_SUFFIX = @LIBS_DESC_SUFFIX@
-USE_N32		= @USE_N32@
-HAVE_64BIT_OS	= @HAVE_64BIT_OS@
-
-CC		    = @CC@
-CXX		    = @CXX@
-CPP       = @CPP@
-TOOLCHAIN_PREFIX = @TOOLCHAIN_PREFIX@
-
-CC_VERSION	= @CC_VERSION@
-CXX_VERSION	= @CXX_VERSION@
-
-GNU_AS		= @GNU_AS@
-GNU_LD		= @GNU_LD@
-GNU_CC		= @GNU_CC@
-GNU_CXX		= @GNU_CXX@
-INTEL_CC	= @INTEL_CC@
-INTEL_CXX	= @INTEL_CXX@
-
-STL_FLAGS		= @STL_FLAGS@
-WRAP_STL_INCLUDES	= @WRAP_STL_INCLUDES@
-MOZ_MSVC_STL_WRAP__Throw= @MOZ_MSVC_STL_WRAP__Throw@
-MOZ_MSVC_STL_WRAP__RAISE= @MOZ_MSVC_STL_WRAP__RAISE@
-
-HOST_CC		= @HOST_CC@
-HOST_CXX	= @HOST_CXX@
-HOST_CFLAGS	= @HOST_CFLAGS@
-HOST_CXXFLAGS	= @HOST_CXXFLAGS@
-HOST_LDFLAGS	= @HOST_LDFLAGS@
-HOST_OPTIMIZE_FLAGS = @HOST_OPTIMIZE_FLAGS@
-HOST_NSPR_MDCPUCFG = @HOST_NSPR_MDCPUCFG@
-HOST_AR		= @HOST_AR@
-HOST_AR_FLAGS	= @HOST_AR_FLAGS@
-HOST_LD		= @HOST_LD@
-HOST_RANLIB	= @HOST_RANLIB@
-HOST_BIN_SUFFIX	= @HOST_BIN_SUFFIX@
-
-HOST_OS_ARCH	= @HOST_OS_ARCH@
-host_cpu	= @host_cpu@
-host_vendor	= @host_vendor@
-host_os		= @host_os@
-
-TARGET_NSPR_MDCPUCFG = @TARGET_NSPR_MDCPUCFG@
-TARGET_CPU	= @TARGET_CPU@
-TARGET_VENDOR	= @TARGET_VENDOR@
-TARGET_OS	= @TARGET_OS@
-TARGET_MD_ARCH	= @TARGET_MD_ARCH@
-TARGET_XPCOM_ABI = @TARGET_XPCOM_ABI@
-
-AUTOCONF	= @AUTOCONF@
-GMAKE		= @GMAKE@
-PERL		= @PERL@
-PYTHON		= @PYTHON@
-RANLIB		= @RANLIB@
-OBJCOPY		= @OBJCOPY@
-UNZIP		= @UNZIP@
-ZIP		= @ZIP@
-XARGS		= @XARGS@
-STRIP		= @STRIP@
-DOXYGEN		= @DOXYGEN@
-PBBUILD_BIN	= @PBBUILD_BIN@
-SDP		= @SDP@
-NSINSTALL_BIN	= @NSINSTALL_BIN@
-WGET		= @WGET@
-RPMBUILD	= @RPMBUILD@
-
-MOZ_JPEG_CFLAGS	= @MOZ_JPEG_CFLAGS@
-MOZ_JPEG_LIBS	= @MOZ_JPEG_LIBS@
-
-MOZ_ZLIB_CFLAGS	= @MOZ_ZLIB_CFLAGS@
-MOZ_ZLIB_LIBS = @MOZ_ZLIB_LIBS@
-
-MOZ_BZ2_CFLAGS	= @MOZ_BZ2_CFLAGS@
-MOZ_BZ2_LIBS	= @MOZ_BZ2_LIBS@
-
-MOZ_PNG_CFLAGS	= @MOZ_PNG_CFLAGS@
-MOZ_PNG_LIBS	= @MOZ_PNG_LIBS@
-
-QCMS_LIBS	= @QCMS_LIBS@
-
-MOZ_HARFBUZZ_LIBS = @MOZ_HARFBUZZ_LIBS@
-MOZ_GRAPHITE_LIBS = @MOZ_GRAPHITE_LIBS@
-MOZ_GRAPHITE = @MOZ_GRAPHITE@
-MOZ_OTS_LIBS = @MOZ_OTS_LIBS@
-MOZ_SKIA_LIBS = @MOZ_SKIA_LIBS@
-MOZ_ENABLE_SKIA = @MOZ_ENABLE_SKIA@
-
-MOZ_NATIVE_SQLITE = @MOZ_NATIVE_SQLITE@
-SQLITE_CFLAGS     = @SQLITE_CFLAGS@
-SQLITE_LIBS       = @SQLITE_LIBS@
-
-NSPR_CONFIG	= @NSPR_CONFIG@
-NSPR_CFLAGS	= @NSPR_CFLAGS@
-NSPR_LIBS	= @NSPR_LIBS@
-
-NSS_CONFIG	= @NSS_CONFIG@
-NSS_CFLAGS	= @NSS_CFLAGS@
-NSS_LIBS	= @NSS_LIBS@
-NSS_DEP_LIBS	= @NSS_DEP_LIBS@
-NSS_DISABLE_DBM = @NSS_DISABLE_DBM@
-
-XPCOM_GLUE_LDOPTS	= @XPCOM_GLUE_LDOPTS@
-XPCOM_STANDALONE_GLUE_LDOPTS	= @XPCOM_STANDALONE_GLUE_LDOPTS@
-XPCOM_STATICRUNTIME_GLUE_LDOPTS	= @XPCOM_STATICRUNTIME_GLUE_LDOPTS@
-XPCOM_STANDALONE_STATICRUNTIME_GLUE_LDOPTS	= @XPCOM_STANDALONE_STATICRUNTIME_GLUE_LDOPTS@
-
-USE_DEPENDENT_LIBS = @USE_DEPENDENT_LIBS@
-
-# UNIX98 iconv support
-LIBICONV = @LIBICONV@
-
-# MKSHLIB_FORCE_ALL is used to force the linker to include all object
-# files present in an archive. MKSHLIB_UNFORCE_ALL reverts the linker
-# to normal behavior. Makefile's that create shared libraries out of
-# archives use these flags to force in all of the .o files in the
-# archives into the shared library.
-WRAP_LDFLAGS            = @WRAP_LDFLAGS@
-DSO_CFLAGS              = @DSO_CFLAGS@
-DSO_PIC_CFLAGS          = @DSO_PIC_CFLAGS@
-MKSHLIB                 = @MKSHLIB@
-MKCSHLIB                = @MKCSHLIB@
-MKSHLIB_FORCE_ALL       = @MKSHLIB_FORCE_ALL@
-MKSHLIB_UNFORCE_ALL     = @MKSHLIB_UNFORCE_ALL@
-DSO_LDOPTS              = @DSO_LDOPTS@
-DLL_SUFFIX              = @DLL_SUFFIX@
-
-NO_LD_ARCHIVE_FLAGS     = @NO_LD_ARCHIVE_FLAGS@
-
-GTK_CONFIG	= @GTK_CONFIG@
-TK_CFLAGS	= @TK_CFLAGS@
-TK_LIBS		= @TK_LIBS@
-
-CAIRO_FT_CFLAGS		= @CAIRO_FT_CFLAGS@
-
-MOZ_TREE_FREETYPE		= @MOZ_TREE_FREETYPE@
-MOZ_ENABLE_CAIRO_FT	= @MOZ_ENABLE_CAIRO_FT@
-MOZ_ENABLE_GTK2		= @MOZ_ENABLE_GTK2@
-MOZ_ENABLE_QT		= @MOZ_ENABLE_QT@
-MOZ_ENABLE_XREMOTE	= @MOZ_ENABLE_XREMOTE@
-MOZ_ENABLE_DWRITE_FONT	= @MOZ_ENABLE_DWRITE_FONT@
-MOZ_ENABLE_D2D_SURFACE	= @MOZ_ENABLE_D2D_SURFACE@
-MOZ_ENABLE_D3D9_LAYER	= @MOZ_ENABLE_D3D9_LAYER@
-MOZ_ENABLE_D3D10_LAYER  = @MOZ_ENABLE_D3D10_LAYER@
-MOZ_METRO	= @MOZ_METRO@
-MAKEPRI	= @MAKEPRI@
-CRTDLLVERSION	= @CRTDLLVERSION@
-CRTEXPDLLVERSION	= @CRTEXPDLLVERSION@
-
-MOZ_GTK2_CFLAGS		= @MOZ_GTK2_CFLAGS@
-MOZ_GTK2_LIBS		= @MOZ_GTK2_LIBS@
-
-MOZ_QT_CFLAGS		= @MOZ_QT_CFLAGS@
-MOZ_QT_LIBS		= @MOZ_QT_LIBS@
-MOZ_ENABLE_QTNETWORK    = @MOZ_ENABLE_QTNETWORK@
-MOZ_ENABLE_QMSYSTEM2    = @MOZ_ENABLE_QMSYSTEM2@
-MOZ_ENABLE_QTMOBILITY   = @MOZ_ENABLE_QTMOBILITY@
-MOZ_ENABLE_CONTENTACTION   = @MOZ_ENABLE_CONTENTACTION@
-MOZ_ENABLE_MEEGOTOUCHSHARE = @MOZ_ENABLE_MEEGOTOUCHSHARE@
-MOZ_ENABLE_CONTENTMANAGER = @MOZ_ENABLE_CONTENTMANAGER@
-
-MOZ_DBUS_CFLAGS         = @MOZ_DBUS_CFLAGS@
-MOZ_DBUS_LIBS           = @MOZ_DBUS_LIBS@
-MOZ_DBUS_GLIB_CFLAGS    = @MOZ_DBUS_GLIB_CFLAGS@
-MOZ_DBUS_GLIB_LIBS      = @MOZ_DBUS_GLIB_LIBS@
-MOZ_ENABLE_DBUS         = @MOZ_ENABLE_DBUS@
-
-MOZ_GTHREAD_CFLAGS      = @MOZ_GTHREAD_CFLAGS@
-MOZ_GTHREAD_LIBS        = @MOZ_GTHREAD_LIBS@
-
-FT2_CFLAGS             = @FT2_CFLAGS@
-FT2_LIBS               = @FT2_LIBS@
-
-MOZ_PANGO_CFLAGS        = @MOZ_PANGO_CFLAGS@
-MOZ_PANGO_LIBS          = @MOZ_PANGO_LIBS@
-
-XT_LIBS			= @XT_LIBS@
-
-MOZ_LIBPROXY_CFLAGS     = @MOZ_LIBPROXY_CFLAGS@
-MOZ_LIBPROXY_LIBS       = @MOZ_LIBPROXY_LIBS@
-MOZ_ENABLE_LIBPROXY     = @MOZ_ENABLE_LIBPROXY@
-
-MOZ_LIBNOTIFY_CFLAGS	= @MOZ_LIBNOTIFY_CFLAGS@
-MOZ_LIBNOTIFY_LIBS	= @MOZ_LIBNOTIFY_LIBS@
-MOZ_ENABLE_LIBNOTIFY	= @MOZ_ENABLE_LIBNOTIFY@
-
-MOZ_ALSA_LIBS           = @MOZ_ALSA_LIBS@
-MOZ_ALSA_CFLAGS         = @MOZ_ALSA_CFLAGS@
-
-GLIB_CFLAGS	= @GLIB_CFLAGS@
-GLIB_LIBS	= @GLIB_LIBS@
-GLIB_GMODULE_LIBS	= @GLIB_GMODULE_LIBS@
-
-MOZ_NATIVE_MAKEDEPEND	= @MOZ_NATIVE_MAKEDEPEND@
-
-CL_INCLUDES_PREFIX = @CL_INCLUDES_PREFIX@
-
-MOZ_AUTO_DEPS	= @MOZ_AUTO_DEPS@
-COMPILER_DEPEND = @COMPILER_DEPEND@
-MDDEPDIR        := @MDDEPDIR@
-CC_WRAPPER = @CC_WRAPPER@
-CXX_WRAPPER = @CXX_WRAPPER@
-
-MOZ_DEMANGLE_SYMBOLS = @MOZ_DEMANGLE_SYMBOLS@
-
-OS_TARGET=@OS_TARGET@
-OS_ARCH=@OS_ARCH@
-OS_RELEASE=@OS_RELEASE@
-OS_TEST=@OS_TEST@
-CPU_ARCH=@CPU_ARCH@
-INTEL_ARCHITECTURE=@INTEL_ARCHITECTURE@
-
-# For Solaris build
-SOLARIS_SUNPRO_CC = @SOLARIS_SUNPRO_CC@
-SOLARIS_SUNPRO_CXX = @SOLARIS_SUNPRO_CXX@
-
-# For AIX build
-AIX_OBJMODEL = @AIX_OBJMODEL@
-
-# For OS/2 build
-MOZ_OS2_TOOLS = @MOZ_OS2_TOOLS@
-MOZ_OS2_HIGH_MEMORY = @MOZ_OS2_HIGH_MEMORY@
-
-MOZ_PSM=@MOZ_PSM@
-
-MOZILLA_OFFICIAL = @MOZILLA_OFFICIAL@
-
-# Win32 options
-MOZ_BROWSE_INFO	= @MOZ_BROWSE_INFO@
-MOZ_TOOLS_DIR	= @MOZ_TOOLS_DIR@
-MSMANIFEST_TOOL = @MSMANIFEST_TOOL@
-WIN32_REDIST_DIR = @WIN32_REDIST_DIR@
-MOZ_GLUE_LDFLAGS = @MOZ_GLUE_LDFLAGS@
-MOZ_GLUE_PROGRAM_LDFLAGS = @MOZ_GLUE_PROGRAM_LDFLAGS@
-WIN32_CRT_LIBS = @WIN32_CRT_LIBS@
-
-# This is used to pass jemalloc flags to NSS
-DLLFLAGS = @DLLFLAGS@
-
-# Codesighs tools option, enables win32 mapfiles.
-MOZ_MAPINFO	= @MOZ_MAPINFO@
-
-MOZ_PHOENIX	= @MOZ_PHOENIX@
-MOZ_XULRUNNER	= @MOZ_XULRUNNER@
-
-MOZ_DISTRIBUTION_ID = @MOZ_DISTRIBUTION_ID@
-
-MOZ_PLATFORM_MAEMO = @MOZ_PLATFORM_MAEMO@
-MOZ_PLATFORM_MAEMO_CFLAGS	= @MOZ_PLATFORM_MAEMO_CFLAGS@
-MOZ_PLATFORM_MAEMO_LIBS 	= @MOZ_PLATFORM_MAEMO_LIBS@
-MOZ_MAEMO_LIBLOCATION 	= @MOZ_MAEMO_LIBLOCATION@
-
-MOZ_ENABLE_LIBCONIC = @MOZ_ENABLE_LIBCONIC@
-LIBCONIC_CFLAGS     = @LIBCONIC_CFLAGS@
-LIBCONIC_LIBS       = @LIBCONIC_LIBS@
-
-GSTREAMER_CFLAGS = @GSTREAMER_CFLAGS@
-GSTREAMER_LIBS   = @GSTREAMER_LIBS@
-
-MACOS_SDK_DIR	= @MACOS_SDK_DIR@
-NEXT_ROOT	= @NEXT_ROOT@
-GCC_VERSION	= @GCC_VERSION@
-UNIVERSAL_BINARY= @UNIVERSAL_BINARY@
-MOZ_CAN_RUN_PROGRAMS = @MOZ_CAN_RUN_PROGRAMS@
-HAVE_DTRACE= @HAVE_DTRACE@
-
-VISIBILITY_FLAGS = @VISIBILITY_FLAGS@
-WRAP_SYSTEM_INCLUDES = @WRAP_SYSTEM_INCLUDES@
-
-HAVE_ARM_SIMD = @HAVE_ARM_SIMD@
-HAVE_ARM_NEON = @HAVE_ARM_NEON@
-HAVE_GCC_ALIGN_ARG_POINTER = @HAVE_GCC_ALIGN_ARG_POINTER@
-HAVE_COMPILER_FLAG_MSSSE3 = @HAVE_COMPILER_FLAG_MSSSE3@
-
-MOZ_THEME_FASTSTRIPE = @MOZ_THEME_FASTSTRIPE@
-
-MOZ_SERVICES_AITC = @MOZ_SERVICES_AITC@
-MOZ_SERVICES_NOTIFICATIONS = @MOZ_SERVICES_NOTIFICATIONS@
-MOZ_SERVICES_SYNC = @MOZ_SERVICES_SYNC@
-
-MOZ_WEBAPP_RUNTIME = @MOZ_WEBAPP_RUNTIME@
-
-MOZ_OFFICIAL_BRANDING = @MOZ_OFFICIAL_BRANDING@
-
-HAVE_CLOCK_MONOTONIC = @HAVE_CLOCK_MONOTONIC@
-REALTIME_LIBS = @REALTIME_LIBS@
-
-MOZ_APP_COMPONENT_LIBS = @MOZ_APP_COMPONENT_LIBS@
-MOZ_APP_EXTRA_LIBS = @MOZ_APP_EXTRA_LIBS@
-
-ANDROID_NDK       = @ANDROID_NDK@
-ANDROID_TOOLCHAIN = @ANDROID_TOOLCHAIN@
-ANDROID_PLATFORM  = @ANDROID_PLATFORM@
-ANDROID_SDK       = @ANDROID_SDK@
-ANDROID_PLATFORM_TOOLS = @ANDROID_PLATFORM_TOOLS@
-ANDROID_VERSION   = @ANDROID_VERSION@
-ANDROID_SOURCE    = @ANDROID_SOURCE@
-STLPORT_SOURCES   = @STLPORT_SOURCES@
-
-ANDROID_PACKAGE_NAME = @ANDROID_PACKAGE_NAME@
-
-JS_SHARED_LIBRARY = @JS_SHARED_LIBRARY@
-
-MOZ_INSTRUMENT_EVENT_LOOP = @MOZ_INSTRUMENT_EVENT_LOOP@
-
-MOZ_SYSTEM_PLY = @MOZ_SYSTEM_PLY@
-
-MOZ_PACKAGE_JSSHELL = @MOZ_PACKAGE_JSSHELL@
-
-MOZ_LINKER_EXTRACT = @MOZ_LINKER_EXTRACT@
-
-MOZ_PER_WINDOW_PRIVATE_BROWSING = @MOZ_PER_WINDOW_PRIVATE_BROWSING@
-
+@ALLSUBSTS@
 include $(topsrcdir)/config/baseconfig.mk
--- a/config/makefiles/autotargets.mk
+++ b/config/makefiles/autotargets.mk
@@ -52,24 +52,27 @@ mkdir_stem =$(foreach val,$(getargv),$(s
 mkdir_deps =$(foreach dir,$(getargv),$(call slash_strip,$(dir)/.mkdir.done))
 
 #######################
 ##---]  TARGETS  [---##
 #######################
 
 %/.mkdir.done: # mkdir -p -p => mkdir -p
 	$(subst $(space)-p,$(null),$(MKDIR)) -p $(dir $@)
-	@$(TOUCH) $@
+# Make the timestamp old enough for not being a problem with symbolic links
+# targets depending on it. Use Jan 3, 1970 to accomodate any timezone where
+# 197001010000 would translate to something older than epoch.
+	@touch -t 197001030000 $@
 
 # A handful of makefiles are attempting "mkdir dot".  Likely not intended
 # or stale logic so add a stub target to handle the request and warn for now.
 .mkdir.done:
 ifndef NOWARN_AUTOTARGETS # {
 	@echo "WARNING: $(MKDIR) -dot- requested by $(MAKE) -C $(CURDIR) $(MAKECMDGOALS)"
-	@$(TOUCH) $@
+	@touch -t 197001030000 $@
 endif #}
 
 INCLUDED_AUTOTARGETS_MK = 1
 endif #}
 
 
 ## Accumulate deps and cleanup
 ifneq (,$(GENERATED_DIRS))
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -27,35 +27,41 @@ include $(topsrcdir)/config/makefiles/ma
 
 ifdef SDK_XPIDLSRCS
 XPIDLSRCS += $(SDK_XPIDLSRCS)
 endif
 ifdef SDK_HEADERS
 EXPORTS += $(SDK_HEADERS)
 endif
 
-REPORT_BUILD = @echo $(notdir $<)
+REPORT_BUILD = $(info $(notdir $<))
 
 ifeq ($(OS_ARCH),OS2)
 EXEC			=
 else
 EXEC			= exec
 endif
 
 # Don't copy xulrunner files at install time, when using system xulrunner
 ifdef SYSTEM_LIBXUL
   SKIP_COPY_XULRUNNER=1
 endif
 
-# ELOG prints out failed command when building silently (gmake -s).
+# ELOG prints out failed command when building silently (gmake -s). Pymake
+# prints out failed commands anyway, so ELOG just makes things worse by
+# forcing shell invocations.
+ifndef .PYMAKE
 ifneq (,$(findstring s, $(filter-out --%, $(MAKEFLAGS))))
   ELOG := $(EXEC) sh $(BUILD_TOOLS)/print-failed-commands.sh
 else
   ELOG :=
-endif
+endif # -s
+else
+  ELOG :=
+endif # ifndef .PYMAKE
 
 _VPATH_SRCS = $(abspath $<)
 
 # Add $(DIST)/lib to VPATH so that -lfoo dependencies are followed
 VPATH += $(DIST)/lib
 ifdef LIBXUL_SDK
 VPATH += $(LIBXUL_SDK)/lib
 endif
@@ -96,17 +102,17 @@ endif #}
 ifdef CPP_UNIT_TESTS
 
 # Compile the tests to $(DIST)/bin.  Make lots of niceties available by default
 # through TestHarness.h, by modifying the list of includes and the libs against
 # which stuff links.
 CPPSRCS += $(CPP_UNIT_TESTS)
 SIMPLE_PROGRAMS += $(CPP_UNIT_TESTS:.cpp=$(BIN_SUFFIX))
 INCLUDES += -I$(DIST)/include/testing
-LIBS += $(XPCOM_GLUE_LDOPTS) $(NSPR_LIBS) $(MOZ_JS_LIBS)
+LIBS += $(XPCOM_GLUE_LDOPTS) $(NSPR_LIBS) $(MOZ_JS_LIBS) $(if $(JS_SHARED_LIBRARY),,$(MOZ_ZLIB_LIBS))
 
 # ...and run them the usual way
 check::
 	@$(EXIT_ON_ERROR) \
 	  for f in $(subst .cpp,$(BIN_SUFFIX),$(CPP_UNIT_TESTS)); do \
 	    XPCOM_DEBUG_BREAK=stack-and-abort $(RUN_TEST_PROGRAM) $(DIST)/bin/$$f; \
 	  done
 
@@ -331,18 +337,18 @@ ifeq ($(SOLARIS_SUNPRO_CXX),1)
 GARBAGE_DIRS += SunWS_cache
 endif
 
 XPIDL_GEN_DIR		= _xpidlgen
 
 ifdef MOZ_UPDATE_XTERM
 # Its good not to have a newline at the end of the titlebar string because it
 # makes the make -s output easier to read.  Echo -n does not work on all
-# platforms, but we can trick sed into doing it.
-UPDATE_TITLE = sed -e "s!Y!$(1) in $(shell $(BUILD_TOOLS)/print-depth-path.sh)/$(2)!" $(MOZILLA_DIR)/config/xterm.str;
+# platforms, but we can trick printf into doing it.
+UPDATE_TITLE = printf "\033]0;%s in %s\007" $(1) $(shell $(BUILD_TOOLS)/print-depth-path.sh)/$(2) ;
 endif
 
 define SUBMAKE # $(call SUBMAKE,target,directory)
 +@$(UPDATE_TITLE)
 +$(MAKE) $(if $(2),-C $(2)) $(1)
 
 endef # The extra line is important here! don't delete it
 
@@ -1221,27 +1227,19 @@ endif
 
 ifneq ($(PREF_JS_EXPORTS),)
 # on win32, pref files need CRLF line endings... see bug 206029
 ifeq (WINNT,$(OS_ARCH))
 PREF_PPFLAGS = --line-endings=crlf
 endif
 
 ifndef NO_DIST_INSTALL
-$(FINAL_TARGET)/$(PREF_DIR):
-	$(NSINSTALL) -D $@
-
-libs:: $(FINAL_TARGET)/$(PREF_DIR)
-libs:: $(PREF_JS_EXPORTS)
-	$(EXIT_ON_ERROR)  \
-	for i in $^; do \
-	  dest=$(FINAL_TARGET)/$(PREF_DIR)/`basename $$i`; \
-	  $(RM) -f $$dest; \
-	  $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(PREF_PPFLAGS) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $$i > $$dest; \
-	done
+PREF_JS_EXPORTS_PATH := $(FINAL_TARGET)/$(PREF_DIR)
+PREF_JS_EXPORTS_FLAGS := $(PREF_PPFLAGS)
+PP_TARGETS += PREF_JS_EXPORTS
 endif
 endif
 
 ################################################################################
 # Copy each element of AUTOCFG_JS_EXPORTS to $(FINAL_TARGET)/defaults/autoconfig
 
 ifneq ($(AUTOCFG_JS_EXPORTS),)
 $(FINAL_TARGET)/defaults/autoconfig::
@@ -1311,16 +1309,17 @@ ifneq ($(XPIDL_MODULE).idl,$(strip $(XPI
 $(XPIDL_GEN_DIR)/$(XPIDL_MODULE).xpt: $(patsubst %.idl,$(XPIDL_GEN_DIR)/%.xpt,$(XPIDLSRCS)) $(GLOBAL_DEPS)
 	$(XPIDL_LINK) $(XPIDL_GEN_DIR)/$(XPIDL_MODULE).xpt $(patsubst %.idl,$(XPIDL_GEN_DIR)/%.xpt,$(XPIDLSRCS))
 endif # XPIDL_MODULE.xpt != XPIDLSRCS
 
 libs:: $(XPIDL_GEN_DIR)/$(XPIDL_MODULE).xpt
 ifndef NO_DIST_INSTALL
 	$(call install_cmd,$(IFLAGS1) $(XPIDL_GEN_DIR)/$(XPIDL_MODULE).xpt $(FINAL_TARGET)/components)
 ifndef NO_INTERFACES_MANIFEST
+libs:: $(call mkdir_deps,$(FINAL_TARGET)/components)
 	@$(PYTHON) $(MOZILLA_DIR)/config/buildlist.py $(FINAL_TARGET)/components/interfaces.manifest "interfaces $(XPIDL_MODULE).xpt"
 	@$(PYTHON) $(MOZILLA_DIR)/config/buildlist.py $(FINAL_TARGET)/chrome.manifest "manifest components/interfaces.manifest"
 endif
 endif
 
 GARBAGE_DIRS		+= $(XPIDL_GEN_DIR)
 
 endif #} XPIDLSRCS
@@ -1361,32 +1360,25 @@ ifdef EXTRA_COMPONENTS
 libs:: $(EXTRA_COMPONENTS)
 ifndef NO_DIST_INSTALL
 	$(call install_cmd,$(IFLAGS1) $^ $(FINAL_TARGET)/components)
 endif
 
 endif
 
 ifdef EXTRA_PP_COMPONENTS
-libs:: $(EXTRA_PP_COMPONENTS)
 ifndef NO_DIST_INSTALL
-	$(EXIT_ON_ERROR) \
-	$(NSINSTALL) -D $(FINAL_TARGET)/components; \
-	for i in $^; do \
-	  fname=`basename $$i`; \
-	  dest=$(FINAL_TARGET)/components/$${fname}; \
-	  $(RM) -f $$dest; \
-	  $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $$i > $$dest; \
-	done
+EXTRA_PP_COMPONENTS_PATH := $(FINAL_TARGET)/components
+PP_TARGETS += EXTRA_PP_COMPONENTS
 endif
 endif
 
 EXTRA_MANIFESTS = $(filter %.manifest,$(EXTRA_COMPONENTS) $(EXTRA_PP_COMPONENTS))
 ifneq (,$(EXTRA_MANIFESTS))
-libs::
+libs:: $(call mkdir_deps,$(FINAL_TARGET))
 	$(PYTHON) $(MOZILLA_DIR)/config/buildlist.py $(FINAL_TARGET)/chrome.manifest $(patsubst %,"manifest components/%",$(notdir $(EXTRA_MANIFESTS)))
 endif
 
 ################################################################################
 # Copy each element of EXTRA_JS_MODULES to JS_MODULES_PATH, or
 # $(FINAL_TARGET)/modules if that isn't defined.
 JS_MODULES_PATH ?= $(FINAL_TARGET)/modules
 
@@ -1394,27 +1386,20 @@ ifdef EXTRA_JS_MODULES
 libs:: $(EXTRA_JS_MODULES)
 ifndef NO_DIST_INSTALL
 	$(call install_cmd,$(IFLAGS1) $^ $(JS_MODULES_PATH))
 endif
 
 endif
 
 ifdef EXTRA_PP_JS_MODULES
-libs:: $(EXTRA_PP_JS_MODULES)
 ifndef NO_DIST_INSTALL
-	$(EXIT_ON_ERROR) \
-	$(NSINSTALL) -D $(JS_MODULES_PATH); \
-	for i in $^; do \
-	  dest=$(JS_MODULES_PATH)/`basename $$i`; \
-	  $(RM) -f $$dest; \
-	  $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $$i > $$dest; \
-	done
+EXTRA_PP_JS_MODULES_PATH := $(JS_MODULES_PATH)
+PP_TARGETS += EXTRA_PP_JS_MODULES
 endif
-
 endif
 
 ################################################################################
 # Copy testing-only JS modules to appropriate destination.
 #
 # For each file defined in TESTING_JS_MODULES, copy it to
 # objdir/_tests/modules/. If TESTING_JS_MODULE_DIR is defined, that path
 # wlll be appended to the output directory.
@@ -1475,41 +1460,25 @@ libs realchrome:: $(CHROME_DEPS) $(FINAL
 	$(PYTHON) $(MOZILLA_DIR)/config/JarMaker.py \
 	  $(QUIET) -j $(FINAL_TARGET)/chrome \
 	  $(MAKE_JARS_FLAGS) $(XULPPFLAGS) $(DEFINES) $(ACDEFINES) \
 	  $(JAR_MANIFEST)
 endif
 endif
 
 ifneq ($(DIST_FILES),)
-$(DIST)/bin:
-	$(NSINSTALL) -D $@
-
-libs:: $(DIST)/bin
-libs:: $(DIST_FILES)
-	@$(EXIT_ON_ERROR) \
-	for f in $^; do \
-	  dest=$(FINAL_TARGET)/`basename $$f`; \
-	  $(RM) -f $$dest; \
-	  $(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py \
-	    $(XULAPP_DEFINES) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) \
-	    $$f > $$dest; \
-	done
+DIST_FILES_PATH := $(FINAL_TARGET)
+DIST_FILES_FLAGS := $(XULAPP_DEFINES)
+PP_TARGETS += DIST_FILES
 endif
 
 ifneq ($(DIST_CHROME_FILES),)
-libs:: $(DIST_CHROME_FILES)
-	@$(EXIT_ON_ERROR) \
-	for f in $^; do \
-	  dest=$(FINAL_TARGET)/chrome/`basename $$f`; \
-	  $(RM) -f $$dest; \
-	  $(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py \
-	    $(XULAPP_DEFINES) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) \
-	    $$f > $$dest; \
-	done
+DIST_CHROME_FILES_PATH := $(FINAL_TARGET)/chrome
+DIST_CHROME_FILES_FLAGS := $(XULAPP_DEFINES)
+PP_TARGETS += DIST_CHROME_FILES
 endif
 
 ifneq ($(XPI_PKGNAME),)
 libs realchrome::
 ifdef STRIP_XPI
 ifndef MOZ_DEBUG
 	@echo "Stripping $(XPI_PKGNAME) package directory..."
 	@echo $(FINAL_TARGET)
@@ -1658,16 +1627,68 @@ ifdef ETAGS
 ifneq ($(CSRCS)$(CPPSRCS)$(HEADERS),)
 all:: TAGS
 TAGS:: $(CSRCS) $(CPPSRCS) $(HEADERS)
 	$(ETAGS) $(CSRCS) $(CPPSRCS) $(HEADERS)
 endif
 endif
 
 ################################################################################
+# Install/copy rules
+#
+# The INSTALL_TARGETS variable contains a list of all install target
+# categories. Each category defines a list of files, an install destination,
+# and whether the files are executables or not.
+#
+# FOO_FILES := foo bar
+# FOO_EXECUTABLES := baz
+# FOO_DEST := target_path
+# INSTALL_TARGETS += FOO
+define install_file_template
+libs:: $(2)/$(notdir $(1))
+$(2)/$(notdir $(1)): $(1) $$(call mkdir_deps,$(2))
+	$(INSTALL) $(3) $$< $${@D}
+endef
+$(foreach category,$(INSTALL_TARGETS),\
+  $(if $($(category)_DEST),,$(error Missing $(category)_DEST))\
+  $(foreach file,$($(category)_FILES),\
+    $(eval $(call install_file_template,$(file),$($(category)_DEST),$(IFLAGS1)))\
+  )\
+  $(foreach file,$($(category)_EXECUTABLES),\
+    $(eval $(call install_file_template,$(file),$($(category)_DEST),$(IFLAGS2)))\
+  )\
+)
+
+################################################################################
+# Preprocessing rules
+#
+# The PP_TARGETS variable contains a list of all preprocessing target
+# categories. Each category defines a target path, and optional extra flags
+# like the following:
+#
+# FOO_PATH := target_path
+# FOO_FLAGS := -Dsome_flag
+# PP_TARGETS += FOO
+
+# preprocess_file_template defines preprocessing rules.
+# $(call preprocess_file_template, source_file, target_path, extra_flags)
+define preprocess_file_template
+$(2)/$(notdir $(1)): $(1) $$(call mkdir_deps,$(2)) $$(GLOBAL_DEPS)
+	$$(RM) $$@
+	$$(PYTHON) $$(topsrcdir)/config/Preprocessor.py $(3) $$(DEFINES) $$(ACDEFINES) $$(XULPPFLAGS) $$< > $$@
+libs:: $(2)/$(notdir $(1))
+endef
+
+$(foreach category,$(PP_TARGETS),\
+  $(foreach file,$($(category)),\
+    $(eval $(call preprocess_file_template,$(file),$($(category)_PATH),$($(category)_FLAGS)))\
+   )\
+ )
+
+################################################################################
 # Special gmake rules.
 ################################################################################
 
 
 #
 # Disallow parallel builds with MSVC < 8
 #
 ifneq (,$(filter 1200 1300 1310,$(_MSC_VER)))
@@ -1756,9 +1777,8 @@ default all::
 
 #############################################################################
 # Derived targets and dependencies
 
 include $(topsrcdir)/config/makefiles/autotargets.mk
 ifneq ($(NULL),$(AUTO_DEPS))
   default all libs tools export:: $(AUTO_DEPS)
 endif
-
deleted file mode 100644
--- a/config/xterm.str
+++ /dev/null
@@ -1,1 +0,0 @@
-]0;gmake: Y
\ No newline at end of file
--- a/configure.in
+++ b/configure.in
@@ -190,20 +190,20 @@ if test -n "$gonkdir" ; then
     i?86)
         ARCH_DIR=arch-x86
         ;;
     esac
 
     CPPFLAGS="-DANDROID -isystem $gonkdir/bionic/libc/$ARCH_DIR/include -isystem $gonkdir/bionic/libc/include/ -isystem $gonkdir/bionic/libc/kernel/common -isystem $gonkdir/bionic/libc/kernel/$ARCH_DIR -isystem $gonkdir/bionic/libm/include -I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/hardware/libhardware/include -I$gonkdir/hardware/libhardware_legacy/include -I$gonkdir/system -I$gonkdir/system/core/include -isystem $gonkdir/bionic -I$gonkdir/frameworks/base/include -I$gonkdir/external/dbus $CPPFLAGS -I$gonkdir/frameworks/base/services/sensorservice -I$gonkdir/frameworks/base/services/camera"
     CFLAGS="-mandroid -fno-short-enums -fno-exceptions $CFLAGS"
     CXXFLAGS="-mandroid -fno-short-enums -fno-exceptions $CXXFLAGS $STLPORT_CPPFLAGS"
-    LIBS="$LIBS $STLPORT_LIBS"
-
     dnl Add -llog by default, since we use it all over the place.
-    LDFLAGS="-mandroid -L$gonkdir/out/target/product/$GONK_PRODUCT/obj/lib -Wl,-rpath-link=$gonkdir/out/target/product/$GONK_PRODUCT/obj/lib --sysroot=$gonkdir/out/target/product/$GONK_PRODUCT/obj/ -llog $LDFLAGS"
+    LIBS="$LIBS -llog $STLPORT_LIBS"
+
+    LDFLAGS="-mandroid -L$gonkdir/out/target/product/$GONK_PRODUCT/obj/lib -Wl,-rpath-link=$gonkdir/out/target/product/$GONK_PRODUCT/obj/lib --sysroot=$gonkdir/out/target/product/$GONK_PRODUCT/obj/ $LDFLAGS"
 
     dnl prevent cross compile section from using these flags as host flags
     if test -z "$HOST_CPPFLAGS" ; then
         HOST_CPPFLAGS=" "
     fi
     if test -z "$HOST_CFLAGS" ; then
         HOST_CFLAGS=" "
     fi
@@ -1988,26 +1988,16 @@ case "$target" in
     else
         AC_MSG_RESULT([no])
     fi
     LDFLAGS=$_SAVE_LDFLAGS
 
     MOZ_FIX_LINK_PATHS='-Wl,-executable_path,$(LIBXUL_DIST)/bin'
     ;;
 
-*-freebsd*)
-    if test `test -x /usr/bin/objformat && /usr/bin/objformat || echo elf` != "elf"; then
-	DLL_SUFFIX=".so.1.0"
-	DSO_LDOPTS="-shared"
-    fi
-    if test ! "$GNU_CC"; then
-	DSO_LDOPTS="-Bshareable $DSO_LDOPTS"
-    fi
-    ;;
-
 ia64*-hpux*)
     DLL_SUFFIX=".so"
     if test ! "$GNU_CC"; then
        DSO_LDOPTS='-b'
        DSO_CFLAGS=""
        DSO_PIC_CFLAGS=
        MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_LDOPTS) -o $@'
        MKCSHLIB='$(CC) $(CFLAGS) $(DSO_LDOPTS) -o $@'
@@ -3189,21 +3179,19 @@ then
 	    rm -f conftest*
     	AC_MSG_RESULT($ac_cv_have_dash_pthreads)
     fi
 
 	case "$target" in
 	    *-*-freebsd*)
 			AC_DEFINE(_REENTRANT)
 			AC_DEFINE(_THREAD_SAFE)
-			dnl -pthread links in -lc_r, so don't specify it explicitly.
+			dnl -pthread links in -lpthread, so don't specify it explicitly.
 			if test "$ac_cv_have_dash_pthread" = "yes"; then
 				_PTHREAD_LDFLAGS="-pthread"
-			else
-				_PTHREAD_LDFLAGS="-lc_r"
 			fi
 			;;
 
 	    *-*-openbsd*|*-*-bsdi*)
 			AC_DEFINE(_REENTRANT)
 			AC_DEFINE(_THREAD_SAFE)
 			dnl -pthread links in -lc_r, so don't specify it explicitly.
 			if test "$ac_cv_have_dash_pthread" = "yes"; then
@@ -4795,23 +4783,23 @@ MOZ_ARG_DISABLE_BOOL(pango,
 
 dnl ========================================================
 dnl = Pango
 dnl ========================================================
 if test "$MOZ_ENABLE_GTK2" -o "$MOZ_ENABLE_QT"
 then
     AC_SUBST(MOZ_PANGO)
 
-    PKG_CHECK_MODULES(_PANGOCHK, pango >= $PANGO_VERSION)
-
-    PKG_CHECK_MODULES(MOZ_PANGO, pango >= $PANGO_VERSION pangoft2 >= $PANGO_VERSION pangocairo >= $PANGO_VERSION)
-    AC_SUBST(MOZ_PANGO_CFLAGS)
-    AC_SUBST(MOZ_PANGO_LIBS)
     if test "$MOZ_PANGO"
     then
+        PKG_CHECK_MODULES(_PANGOCHK, pango >= $PANGO_VERSION)
+
+        PKG_CHECK_MODULES(MOZ_PANGO, pango >= $PANGO_VERSION pangoft2 >= $PANGO_VERSION pangocairo >= $PANGO_VERSION)
+        AC_SUBST(MOZ_PANGO_CFLAGS)
+        AC_SUBST(MOZ_PANGO_LIBS)
         AC_DEFINE(MOZ_PANGO)
     else
         PKG_CHECK_MODULES(FT2, freetype2 > 6.1.0)
         AC_SUBST(FT2_CFLAGS)
         AC_SUBST(FT2_LIBS)
     fi
 fi
 
@@ -6965,17 +6953,21 @@ else
     AC_DEFINE(MOZ_MEMORY_DARWIN)
     ;;
   *-*freebsd*)
     AC_DEFINE(MOZ_MEMORY_BSD)
     ;;
   *-android*|*-linuxandroid*)
     AC_DEFINE(MOZ_MEMORY_LINUX)
     AC_DEFINE(MOZ_MEMORY_ANDROID)
-    _WRAP_MALLOC=1
+    if test -z "$gonkdir"; then
+      _WRAP_MALLOC=1
+    else
+      AC_DEFINE(MOZ_MEMORY_GONK)
+    fi
     MOZ_GLUE_LDFLAGS=
     ;;
   *-*linux*)
     AC_DEFINE(MOZ_MEMORY_LINUX)
     ;;
   *-netbsd*)
     AC_DEFINE(MOZ_MEMORY_BSD)
     ;;
@@ -7783,27 +7775,16 @@ MOZ_ENABLE_SKIA=
 fi
 
 MOZ_ARG_ENABLE_BOOL(skia,
 [  --enable-skia   Enable use of Skia],
 MOZ_ENABLE_SKIA=1,
 MOZ_ENABLE_SKIA=)
 
 if test "$USE_FC_FREETYPE"; then
-    PKG_CHECK_MODULES(_FONTCONFIG, fontconfig,
-    [
-        if test "$MOZ_PANGO"; then
-            MOZ_PANGO_CFLAGS="$MOZ_PANGO_CFLAGS $_FONTCONFIG_CFLAGS"
-            MOZ_PANGO_LIBS="$MOZ_PANGO_LIBS $_FONTCONFIG_LIBS"
-        else
-            FT2_CFLAGS="$FT2_CFLAGS $_FONTCONFIG_CFLAGS"
-            FT2_LIBS="$FT2_LIBS $_FONTCONFIG_LIBS"
-        fi
-    ])
-
     if test "$COMPILE_ENVIRONMENT"; then
         dnl ========================================================
         dnl = Check for freetype2 and its functionality
         dnl ========================================================
         PKG_CHECK_MODULES(FT2, freetype2 >= 6.1.0, _HAVE_FREETYPE2=1, _HAVE_FREETYPE2=)
 
         if test "$_HAVE_FREETYPE2"; then
             _SAVE_LIBS="$LIBS"
@@ -7838,16 +7819,27 @@ if test "$USE_FC_FREETYPE"; then
         _SAVE_CPPFLAGS="$CPPFLAGS"
         CPPFLAGS="$CPPFLAGS $FT2_CFLAGS $XCFLAGS"
         MOZ_CHECK_HEADERS([fontconfig/fcfreetype.h], ,
             [AC_MSG_ERROR(Can't find header fontconfig/fcfreetype.h.)], [#include <fontconfig/fontconfig.h>])
         CPPFLAGS="$_SAVE_CPPFLAGS"
     else
         AC_DEFINE(HAVE_FONTCONFIG_FCFREETYPE_H)
     fi
+
+    PKG_CHECK_MODULES(_FONTCONFIG, fontconfig,
+    [
+        if test "$MOZ_PANGO"; then
+            MOZ_PANGO_CFLAGS="$MOZ_PANGO_CFLAGS $_FONTCONFIG_CFLAGS"
+            MOZ_PANGO_LIBS="$MOZ_PANGO_LIBS $_FONTCONFIG_LIBS"
+        else
+            FT2_CFLAGS="$FT2_CFLAGS $_FONTCONFIG_CFLAGS"
+            FT2_LIBS="$FT2_LIBS $_FONTCONFIG_LIBS"
+        fi
+    ])
 fi
 
 dnl ========================================================
 dnl Check for pixman and cairo
 dnl ========================================================
 
 MOZ_TREE_CAIRO=1
 MOZ_ARG_ENABLE_BOOL(system-cairo,
@@ -8985,17 +8977,16 @@ if test -z "$JS_SHARED_LIBRARY" ; then
     ac_configure_args="$ac_configure_args --disable-shared-js"
 fi
 if test -z "$MOZ_NATIVE_NSPR"; then
     ac_configure_args="$ac_configure_args --with-nspr-cflags='$NSPR_CFLAGS'"
     ac_configure_args="$ac_configure_args --with-nspr-libs='$NSPR_LIBS'"
 fi
 ac_configure_args="$ac_configure_args --with-dist-dir=../../dist"
 ac_configure_args="$ac_configure_args --prefix=$dist"
-ac_configure_args="$ac_configure_args --with-sync-build-files=$_topsrcdir"
 if test "$MOZ_MEMORY"; then
    ac_configure_args="$ac_configure_args --enable-jemalloc"
 fi
 if test -n "$MOZ_GLUE_LDFLAGS"; then
    export MOZ_GLUE_LDFLAGS
 fi
 if test -n "$MOZ_GLUE_PROGRAM_LDFLAGS"; then
    export MOZ_GLUE_PROGRAM_LDFLAGS
--- a/content/base/crashtests/crashtests.list
+++ b/content/base/crashtests/crashtests.list
@@ -105,11 +105,11 @@ load 700512.html
 load xhr_html_nullresponse.html
 load 709384.html
 load 713417.html
 load 713417-2.html
 load 715056.html
 load 741163-1.html
 load 766426.html
 load 771639.html
-asserts(0-1) load 752226-1.html
-asserts(0-1) load 752226-2.html
+load 752226-1.html
+load 752226-2.html
 HTTP(..) load xhr_abortinprogress.html
--- a/content/base/public/Element.h
+++ b/content/base/public/Element.h
@@ -2,23 +2,25 @@
  * vim: sw=2 ts=2 et :
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_Element_h__
 #define mozilla_dom_Element_h__
 
-#include "mozilla/dom/FragmentOrElement.h"
-#include "nsEventStates.h"
+#include "mozilla/dom/FragmentOrElement.h" // for base class
+#include "nsChangeHint.h"                  // for enum
+#include "nsEventStates.h"                 // for member
 
 class nsEventStateManager;
+class nsFocusManager;
 class nsGlobalWindow;
-class nsFocusManager;
 class nsICSSDeclaration;
+class nsISMILAttr;
 
 // Element-specific flags
 enum {
   // Set if the element has a pending style change.
   ELEMENT_HAS_PENDING_RESTYLE     = (1 << NODE_TYPE_SPECIFIC_BITS_OFFSET),
 
   // Set if the element is a potential restyle root (that is, has a style
   // change pending _and_ that style change will attempt to restyle
--- a/content/base/public/FragmentOrElement.h
+++ b/content/base/public/FragmentOrElement.h
@@ -7,60 +7,39 @@
  * Base class for all element classes as well as nsDocumentFragment.  This
  * provides an implementation of nsIDOMNode, implements nsIContent, provides
  * utility methods for subclasses, and so forth.
  */
 
 #ifndef FragmentOrElement_h___
 #define FragmentOrElement_h___
 
-#include "nsCOMPtr.h"
-#include "nsAutoPtr.h"
-#include "nsIContent.h"
-#include "nsIDOMElement.h"
-#include "nsIDOMDocumentFragment.h"
-#include "nsILinkHandler.h"
-#include "nsNodeUtils.h"
-#include "nsAttrAndChildArray.h"
-#include "mozFlushType.h"
-#include "nsDOMAttributeMap.h"
-#include "nsIWeakReference.h"
-#include "nsCycleCollectionParticipant.h"
-#include "nsIDocument.h"
-#include "nsIDOMNodeSelector.h"
-#include "nsIDOMXPathNSResolver.h"
-#include "nsPresContext.h"
-#include "nsIDOMDOMStringMap.h"
-#include "nsContentList.h"
-#include "nsDOMClassInfoID.h" // DOMCI_DATA
-#include "nsIDOMTouchEvent.h"
-#include "nsIInlineEventHandlers.h"
-#include "mozilla/CORSMode.h"
-#include "mozilla/Attributes.h"
+#include "nsAttrAndChildArray.h"          // member
+#include "nsCOMPtr.h"                     // member
+#include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_*
+#include "nsIContent.h"                   // base class
+#include "nsIDOMNodeSelector.h"           // base class
+#include "nsIDOMTouchEvent.h"             // base class (nsITouchEventReceiver)
+#include "nsIDOMXPathNSResolver.h"        // base class
+#include "nsIInlineEventHandlers.h"       // base class
+#include "nsINodeList.h"                  // base class
+#include "nsIWeakReference.h"             // base class
+#include "nsNodeUtils.h"                  // class member nsNodeUtils::CloneNodeImpl
 
-#include "nsISMILAttr.h"
-
-class nsIDOMAttr;
-class nsIDOMEventListener;
-class nsIFrame;
+class ContentUnbinder;
+class nsContentList;
+class nsDOMAttributeMap;
+class nsDOMTokenList;
+class nsIControllers;
+class nsICSSDeclaration;
+class nsIDocument;
+class nsIDOMDOMStringMap;
 class nsIDOMNamedNodeMap;
-class nsICSSDeclaration;
-class nsIDOMCSSStyleDeclaration;
-class nsIURI;
 class nsINodeInfo;
-class nsIControllers;
-class nsEventListenerManager;
-class nsIScrollableFrame;
-class nsAttrValueOrString;
-class nsContentList;
-class nsDOMTokenList;
-class ContentUnbinder;
-struct nsRect;
-
-typedef PRUptrdiff PtrBits;
+class nsIURI;
 
 /**
  * Class that implements the nsIDOMNodeList interface (a list of children of
  * the content), by holding a reference to the content and delegating GetLength
  * and Item to its existing child list.
  * @see nsIDOMNodeList
  */
 class nsChildContentList MOZ_FINAL : public nsINodeList
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -1,42 +1,31 @@
 /* -*- Mode: C++; tab-width: 2; 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 nsIContent_h___
 #define nsIContent_h___
 
-#include "nsCOMPtr.h" // for already_AddRefed
-#include "nsStringGlue.h"
-#include "nsCaseTreatment.h"
-#include "nsChangeHint.h"
-#include "nsINode.h"
-#include "nsIDocument.h" // for IsInHTMLDocument
-#include "nsCSSProperty.h"
+#include "nsCaseTreatment.h" // for enum, cannot be forward-declared
+#include "nsCOMPtr.h"        // for already_AddRefed in constructor
+#include "nsIDocument.h"     // for use in inline function (IsInHTMLDocument)
+#include "nsINode.h"         // for base class
 
 // Forward declarations
+class nsAString;
 class nsIAtom;
-class nsIDOMEvent;
-class nsIContent;
-class nsEventListenerManager;
 class nsIURI;
 class nsRuleWalker;
 class nsAttrValue;
 class nsAttrName;
 class nsTextFragment;
-class nsIDocShell;
 class nsIFrame;
-class nsISMILAttr;
-class nsIDOMCSSStyleDeclaration;
 
 namespace mozilla {
-namespace css {
-class StyleRule;
-} // namespace css
 namespace widget {
 struct IMEState;
 } // namespace widget
 } // namespace mozilla
 
 enum nsLinkState {
   eLinkState_Unknown    = 0,
   eLinkState_Unvisited  = 1,
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -1,89 +1,74 @@
 /* -*- Mode: C++; tab-width: 2; 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 nsIDocument_h___
 #define nsIDocument_h___
 
-#include "nsINode.h"
-#include "nsStringGlue.h"
-#include "nsIDocumentObserver.h" // for nsUpdateType
-#include "nsCOMPtr.h"
-#include "nsCOMArray.h"
-#include "nsIURI.h"
-#include "nsILoadGroup.h"
-#include "nsCRT.h"
-#include "mozFlushType.h"
-#include "nsIAtom.h"
-#include "nsCompatibility.h"
-#include "nsTObserverArray.h"
-#include "nsTHashtable.h"
-#include "nsHashKeys.h"
-#include "nsIVariant.h"
-#include "nsIObserver.h"
-#include "nsGkAtoms.h"
-#include "nsAutoPtr.h"
-#include "nsPIDOMWindow.h"
-#include "nsSMILAnimationController.h"
-#include "nsIScriptGlobalObject.h"
-#include "nsIDocumentEncoder.h"
-#include "nsIFrameRequestCallback.h"
-#include "nsEventStates.h"
-#include "nsIStructuredCloneContainer.h"
-#include "nsILoadContext.h"
+#include "mozFlushType.h"                // for enum
+#include "nsAutoPtr.h"                   // for member
+#include "nsCOMArray.h"                  // for member
+#include "nsCRT.h"                       // for NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
+#include "nsCompatibility.h"             // for member
+#include "nsCOMPtr.h"                    // for member
+#include "nsGkAtoms.h"                   // for static class members
+#include "nsIDocumentEncoder.h"          // for member (in nsCOMPtr)
+#include "nsIDocumentObserver.h"         // for typedef (nsUpdateType)
+#include "nsIFrameRequestCallback.h"     // for member (in nsCOMPtr)
+#include "nsILoadContext.h"              // for member (in nsCOMPtr)
+#include "nsILoadGroup.h"                // for member (in nsCOMPtr)
+#include "nsINode.h"                     // for base class
+#include "nsIScriptGlobalObject.h"       // for member (in nsCOMPtr)
+#include "nsIStructuredCloneContainer.h" // for member (in nsCOMPtr)
+#include "nsPIDOMWindow.h"               // for use in inline functions
+#include "nsPropertyTable.h"             // for member
+#include "nsTHashtable.h"                // for member
 
-class nsIRequest;
-class nsPIDOMWindow;
-class nsIStreamListener;
+class imgIRequest;
+class nsAString;
+class nsBindingManager;
+class nsCSSStyleSheet;
+class nsDOMNavigationTiming;
+class nsEventStates;
+class nsFrameLoader;
+class nsHTMLCSSStyleSheet;
+class nsHTMLStyleSheet;
+class nsIAtom;
 class nsIBFCacheEntry;
+class nsIBoxObject;
+class nsIChannel;
 class nsIContent;
-class nsPresContext;
+class nsIContentSink;
+class nsIDocShell;
+class nsIDocumentObserver;
+class nsIDOMDocument;
+class nsIDOMDocumentFragment;
+class nsIDOMDocumentType;
+class nsIDOMElement;
+class nsIDOMEventTarget;
+class nsIDOMNodeList;
+class nsILayoutHistoryState;
+class nsIObjectLoadingContent;
+class nsIObserver;
 class nsIPresShell;
-class nsIDocShell;
-class nsStyleSet;
+class nsIPrincipal;
+class nsIRequest;
+class nsIStreamListener;
+class nsIStyleRule;
 class nsIStyleSheet;
-class nsIStyleRule;
-class nsCSSStyleSheet;
+class nsIURI;
+class nsIVariant;
 class nsIViewManager;
-class nsIDOMEvent;
-class nsIDOMEventTarget;
-class nsDeviceContext;
-class nsIParser;
-class nsIDOMNode;
-class nsIDOMElement;
-class nsIDOMDocumentFragment;
-class nsILineBreaker;
-class nsIWordBreaker;
-class nsISelection;
-class nsIChannel;
-class nsIPrincipal;
-class nsIDOMDocument;
-class nsIDOMDocumentType;
+class nsPresContext;
 class nsScriptLoader;
-class nsIContentSink;
-class nsHTMLStyleSheet;
-class nsHTMLCSSStyleSheet;
-class nsILayoutHistoryState;
-class nsIVariant;
-class nsIDOMUserDataHandler;
-template<class E> class nsCOMArray;
-class nsIDocumentObserver;
-class nsBindingManager;
-class nsIDOMNodeList;
-class mozAutoSubtreeModified;
-struct JSObject;
-class nsFrameLoader;
-class nsIBoxObject;
-class imgIRequest;
-class nsISHEntry;
-class nsDOMNavigationTiming;
+class nsSMILAnimationController;
+class nsStyleSet;
 class nsWindowSizes;
-class nsIObjectLoadingContent;
 
 namespace mozilla {
 namespace css {
 class Loader;
 } // namespace css
 
 namespace dom {
 class Link;
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -1,66 +1,65 @@
 /* -*- Mode: C++; tab-width: 2; 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 nsINode_h___
 #define nsINode_h___
 
-#include "nsIDOMEventTarget.h"
-#include "nsEvent.h"
-#include "nsPropertyTable.h"
-#include "nsTObserverArray.h"
-#include "nsINodeInfo.h"
-#include "nsCOMPtr.h"
-#include "nsWrapperCache.h"
-#include "nsIProgrammingLanguage.h" // for ::JAVASCRIPT
-#include "nsDOMError.h"
-#include "nsDOMString.h"
-#include "jspubtd.h"
-#include "nsWindowMemoryReporter.h"
-#include "nsIVariant.h"
-#include "nsGkAtoms.h"
+#include "nsCOMPtr.h"               // for member, local
+#include "nsGkAtoms.h"              // for nsGkAtoms::baseURIProperty
+#include "nsIDOMEventTarget.h"      // for base class
+#include "nsINodeInfo.h"            // member (in nsCOMPtr)
+#include "nsIVariant.h"             // for use in GetUserData()
+#include "nsNodeInfoManager.h"      // for use in NodePrincipal()
+#include "nsPropertyTable.h"        // for typedefs
+#include "nsTObserverArray.h"       // for member
+#include "nsWindowMemoryReporter.h" // for NS_DECL_SIZEOF_EXCLUDING_THIS
+#include "nsWrapperCache.h"         // for base class
 
 // Including 'windows.h' will #define GetClassInfo to something else.
 #ifdef XP_WIN
 #ifdef GetClassInfo
 #undef GetClassInfo
 #endif
 #endif
 
+class nsAttrAndChildArray;
+class nsChildContentList;
 class nsIContent;
 class nsIDocument;
-class nsIDOMEvent;
+class nsIDOMElement;
 class nsIDOMNode;
-class nsIDOMElement;
 class nsIDOMNodeList;
+class nsIDOMUserDataHandler;
+class nsIEditor;
+class nsIFrame;
+class nsIMutationObserver;
 class nsINodeList;
 class nsIPresShell;
-class nsEventChainVisitor;
-class nsEventChainPreVisitor;
-class nsEventChainPostVisitor;
-class nsEventListenerManager;
 class nsIPrincipal;
-class nsIMutationObserver;
-class nsChildContentList;
-class nsNodeWeakReference;
+class nsIURI;
 class nsNodeSupportsWeakRefTearoff;
-class nsIEditor;
-class nsIDOMUserDataHandler;
-class nsAttrAndChildArray;
+class nsNodeWeakReference;
 class nsXPCClassInfo;
 
 namespace mozilla {
 namespace dom {
 class Element;
 } // namespace dom
 } // namespace mozilla
 
+namespace JS {
+class Value;
+}
+
+inline void SetDOMStringToNull(nsAString& aString);
+
 enum {
   // This bit will be set if the node has a listener manager.
   NODE_HAS_LISTENERMANAGER =     0x00000001U,
 
   // Whether this node has had any properties set on it
   NODE_HAS_PROPERTIES =          0x00000002U,
 
   // Whether this node is the root of an anonymous subtree.  Note that this
@@ -145,29 +144,16 @@ enum {
 
   // Set if the node has had :hover selectors matched against it
   NODE_HAS_RELEVANT_HOVER_RULES = 0x00080000U,
 
   // Remaining bits are node type specific.
   NODE_TYPE_SPECIFIC_BITS_OFFSET =        20
 };
 
-// Useful inline function for getting a node given an nsIContent and an
-// nsIDocument.  Returns the first argument cast to nsINode if it is non-null,
-// otherwise returns the second (which may be null).  We use type variables
-// instead of nsIContent* and nsIDocument* because the actual types must be
-// known for the cast to work.
-template<class C, class D>
-inline nsINode* NODE_FROM(C& aContent, D& aDocument)
-{
-  if (aContent)
-    return static_cast<nsINode*>(aContent);
-  return static_cast<nsINode*>(aDocument);
-}
-
 /**
  * Class used to detect unexpected mutations. To use the class create an
  * nsMutationGuard on the stack before unexpected mutations could occur.
  * You can then at any time call Mutated to check if any unexpected mutations
  * have occurred.
  *
  * When a guard is instantiated sMutationCount is set to 300. It is then
  * decremented by every mutation (capped at 0). This means that we can only
@@ -1554,16 +1540,29 @@ protected:
     // Pointer to the root of our subtree.  Might be null.
     nsINode* mSubtreeRoot;
   };
 
   // Storage for more members that are usually not needed; allocated lazily.
   nsSlots* mSlots;
 };
 
+// Useful inline function for getting a node given an nsIContent and an
+// nsIDocument.  Returns the first argument cast to nsINode if it is non-null,
+// otherwise returns the second (which may be null).  We use type variables
+// instead of nsIContent* and nsIDocument* because the actual types must be
+// known for the cast to work.
+template<class C, class D>
+inline nsINode* NODE_FROM(C& aContent, D& aDocument)
+{
+  if (aContent)
+    return static_cast<nsINode*>(aContent);
+  return static_cast<nsINode*>(aDocument);
+}
+
 
 extern const nsIID kThisPtrOffsetsSID;
 
 // _implClass is the class to use to cast to nsISupports
 #define NS_OFFSET_AND_INTERFACE_TABLE_BEGIN_AMBIGUOUS(_class, _implClass)     \
   static const QITableEntry offsetAndQITable[] = {                            \
     NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(_class, nsISupports, _implClass)
 
--- a/content/base/public/nsINodeInfo.h
+++ b/content/base/public/nsINodeInfo.h
@@ -18,30 +18,29 @@
  * references are released the nsNodeInfoManager deletes itself.
  *
  * -- jst@netscape.com
  */
 
 #ifndef nsINodeInfo_h___
 #define nsINodeInfo_h___
 
-#include "nsISupports.h"
-#include "nsIAtom.h"
-#include "nsINameSpaceManager.h"
-#include "nsNodeInfoManager.h"
-#include "nsCOMPtr.h"
+#include "nsCOMPtr.h"            // for member
+#include "nsIAtom.h"             // for member (in nsCOMPtr)
+#include "nsINameSpaceManager.h" // for kNameSpaceID_*
+#include "nsISupports.h"         // for base class
 
 #ifdef MOZILLA_INTERNAL_API
 #include "nsDOMString.h"
 #endif
 
-// Forward declarations
 class nsIDocument;
 class nsIURI;
 class nsIPrincipal;
+class nsNodeInfoManager;
 
 // IID for the nsINodeInfo interface
 #define NS_INODEINFO_IID      \
 { 0xc5188ea1, 0x0a9c, 0x43e6, \
  { 0x95, 0x90, 0xcc, 0x43, 0x6b, 0xe9, 0xcf, 0xa0 } }
 
 class nsINodeInfo : public nsISupports
 {
--- a/content/base/public/nsIObjectLoadingContent.idl
+++ b/content/base/public/nsIObjectLoadingContent.idl
@@ -19,16 +19,19 @@ interface nsIURI;
 
 /**
  * This interface represents a content node that loads objects.
  */
 
 [scriptable, uuid(e3e284a3-b4a8-49ef-af6b-c8c4a158db86)]
 interface nsIObjectLoadingContent : nsISupports
 {
+  /**
+   * See notes in nsObjectLoadingContent.h
+   */
   const unsigned long TYPE_LOADING  = 0;
   const unsigned long TYPE_IMAGE    = 1;
   const unsigned long TYPE_PLUGIN   = 2;
   const unsigned long TYPE_DOCUMENT = 3;
   const unsigned long TYPE_NULL     = 4;
 
   /**
    * The actual mime type (the one we got back from the network
--- a/content/base/public/nsIXMLHttpRequest.idl
+++ b/content/base/public/nsIXMLHttpRequest.idl
@@ -390,17 +390,17 @@ interface nsIXHRSendable : nsISupports {
   void getSendInfo(out nsIInputStream body,
                    out ACString contentType,
                    out ACString charset);
 };
 
 /**
  * @deprecated
  */
-[deprecated, scriptable, uuid(8ae70a39-edf1-40b4-a992-472d23421c25)]
+[scriptable, uuid(8ae70a39-edf1-40b4-a992-472d23421c25)]
 interface nsIJSXMLHttpRequest : nsISupports {
 };
 
 %{ C++
 #define NS_XMLHTTPREQUEST_CID                       \
  { /* d164e770-4157-11d4-9a42-000064657374 */       \
   0xd164e770, 0x4157, 0x11d4,                       \
  {0x9a, 0x42, 0x00, 0x00, 0x64, 0x65, 0x73, 0x74} }
--- a/content/base/src/nsDOMMutationObserver.cpp
+++ b/content/base/src/nsDOMMutationObserver.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8; -*- */
 /* vim: set sw=4 ts=8 et tw=80 : */
 /* 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 "nsDOMMutationObserver.h"        
 #include "nsDOMClassInfoID.h"
+#include "nsDOMError.h"
 #include "nsIClassInfo.h"
 #include "nsIXPCScriptable.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsContentUtils.h"
 #include "nsThreadUtils.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsTextFragment.h"
 #include "jsapi.h"
--- a/content/base/src/nsEventSource.cpp
+++ b/content/base/src/nsEventSource.cpp
@@ -28,16 +28,17 @@
 #include "nsContentUtils.h"
 #include "mozilla/Preferences.h"
 #include "xpcpublic.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsDOMEventTargetHelper.h"
 #include "mozilla/Attributes.h"
 #include "nsDOMClassInfoID.h"
+#include "nsDOMError.h"
 
 using namespace mozilla;
 
 #define REPLACEMENT_CHAR     (PRUnichar)0xFFFD
 #define BOM_CHAR             (PRUnichar)0xFEFF
 #define SPACE_CHAR           (PRUnichar)0x0020
 #define CR_CHAR              (PRUnichar)0x000D
 #define LF_CHAR              (PRUnichar)0x000A
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -5,16 +5,17 @@
 
 #include "base/basictypes.h"
 
 #include "nsFrameMessageManager.h"
 
 #include "ContentChild.h"
 #include "ContentParent.h"
 #include "nsContentUtils.h"
+#include "nsDOMError.h"
 #include "nsIXPConnect.h"
 #include "jsapi.h"
 #include "nsJSUtils.h"
 #include "nsJSPrincipals.h"
 #include "nsNetUtil.h"
 #include "nsScriptLoader.h"
 #include "nsIJSContextStack.h"
 #include "nsIXULRuntime.h"
@@ -26,16 +27,19 @@
 #include "nsIDOMFile.h"
 #include "xpcpublic.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/StructuredCloneUtils.h"
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
+#ifdef XP_WIN
+#include <windows.h>
+#endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static bool
 IsChromeProcess()
 {
   nsCOMPtr<nsIXULRuntime> rt = do_GetService("@mozilla.org/xre/runtime;1");
@@ -289,16 +293,21 @@ nsFrameMessageManager::SendAsyncMessage(
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::Dump(const nsAString& aStr)
 {
 #ifdef ANDROID
   __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", NS_ConvertUTF16toUTF8(aStr).get());
 #endif
+#ifdef XP_WIN
+  if (IsDebuggerPresent()) {
+    OutputDebugStringW(PromiseFlatString(aStr).get());
+  }
+#endif
   fputs(NS_ConvertUTF16toUTF8(aStr).get(), stdout);
   fflush(stdout);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::PrivateNoteIntentionalCrash()
 {
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -43,16 +43,17 @@ class nsIDOMAttr;
 class nsIDOMEventListener;
 class nsIFrame;
 class nsIDOMNamedNodeMap;
 class nsICSSDeclaration;
 class nsIDOMCSSStyleDeclaration;
 class nsIURI;
 class nsINodeInfo;
 class nsIControllers;
+class nsEventChainVisitor;
 class nsEventListenerManager;
 class nsIScrollableFrame;
 class nsAttrValueOrString;
 class nsContentList;
 class nsDOMTokenList;
 class ContentUnbinder;
 struct nsRect;
 
--- a/content/base/src/nsGkAtoms.h
+++ b/content/base/src/nsGkAtoms.h
@@ -7,17 +7,17 @@
  * This class wraps up the creation (and destruction) of the standard
  * set of atoms used by gklayout; the atoms are created when gklayout
  * is loaded and they are destroyed when gklayout is unloaded.
  */
 
 #ifndef nsGkAtoms_h___
 #define nsGkAtoms_h___
 
-#include "nsIAtom.h"
+class nsIAtom;
 
 class nsGkAtoms {
 public:
 
   static void AddRefAtoms();
 
   /* Declare all atoms
 
--- a/content/base/src/nsNodeInfoManager.h
+++ b/content/base/src/nsNodeInfoManager.h
@@ -5,34 +5,32 @@
 
 /*
  * A class for handing out nodeinfos and ensuring sharing of them as needed.
  */
 
 #ifndef nsNodeInfoManager_h___
 #define nsNodeInfoManager_h___
 
-#include "nsCOMPtr.h" // for already_AddRefed
-#include "plhash.h"
-#include "nsCycleCollectionParticipant.h"
-#include "mozilla/Attributes.h"
+#include "mozilla/Attributes.h"           // for MOZ_FINAL
+#include "nsCOMPtr.h"                     // for member
+#include "nsCycleCollectionParticipant.h" // for NS_DECL_CYCLE_*
+#include "plhash.h"                       // for typedef PLHashNumber
 
+class nsAString;
+class nsBindingManager;
 class nsIAtom;
 class nsIDocument;
+class nsIDOMDocumentType;
 class nsINodeInfo;
-class nsNodeInfo;
 class nsIPrincipal;
-class nsIURI;
-class nsDocument;
-class nsIDOMDocumentType;
-class nsIDOMDocument;
-class nsAString;
-class nsIDOMNamedNodeMap;
-class nsXULPrototypeDocument;
-class nsBindingManager;
+class nsNodeInfo;
+struct PLHashEntry;
+struct PLHashTable;
+template<class T> struct already_AddRefed;
 
 class nsNodeInfoManager MOZ_FINAL : public nsISupports
 {
 public:
   nsNodeInfoManager();
   ~nsNodeInfoManager();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
--- a/content/base/src/nsNodeUtils.h
+++ b/content/base/src/nsNodeUtils.h
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; 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 nsNodeUtils_h___
 #define nsNodeUtils_h___
 
-#include "nsINode.h"
-#include "nsIContent.h"
+#include "nsIContent.h"          // for use in inline function (ParentChainChanged)
+#include "nsIMutationObserver.h" // for use in inline function (ParentChainChanged)
 
 struct CharacterDataChangeInfo;
 struct JSContext;
 struct JSObject;
 class nsIVariant;
 class nsIDOMNode;
 class nsIDOMUserDataHandler;
 template<class E> class nsCOMArray;
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -76,124 +76,130 @@ static NS_DEFINE_CID(kAppShellCID, NS_AP
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gObjectLog = PR_NewLogModule("objlc");
 #endif
 
 #define LOG(args) PR_LOG(gObjectLog, PR_LOG_DEBUG, args)
 #define LOG_ENABLED() PR_LOG_TEST(gObjectLog, PR_LOG_DEBUG)
 
+///
+/// Runnables and helper classes
+///
+
 class nsAsyncInstantiateEvent : public nsRunnable {
 public:
-  nsObjectLoadingContent *mContent;
-  nsAsyncInstantiateEvent(nsObjectLoadingContent* aContent)
-  : mContent(aContent)
-  {
-    static_cast<nsIObjectLoadingContent *>(mContent)->AddRef();
-  }
+  nsAsyncInstantiateEvent(nsObjectLoadingContent *aContent)
+  : mContent(aContent) {}
 
-  ~nsAsyncInstantiateEvent()
-  {
-    static_cast<nsIObjectLoadingContent *>(mContent)->Release();
-  }
+  ~nsAsyncInstantiateEvent() {}
 
   NS_IMETHOD Run();
+
+private:
+  nsCOMPtr<nsIObjectLoadingContent> mContent;
 };
 
 NS_IMETHODIMP
 nsAsyncInstantiateEvent::Run()
 {
+  nsObjectLoadingContent *objLC =
+    static_cast<nsObjectLoadingContent *>(mContent.get());
+
   // do nothing if we've been revoked
-  if (mContent->mPendingInstantiateEvent != this) {
+  if (objLC->mPendingInstantiateEvent != this) {
     return NS_OK;
   }
-  mContent->mPendingInstantiateEvent = nullptr;
+  objLC->mPendingInstantiateEvent = nullptr;
 
-  return mContent->SyncStartPluginInstance();
+  return objLC->SyncStartPluginInstance();
 }
 
 // Checks to see if the content for a plugin instance has a parent.
 // The plugin instance is stopped if there is no parent.
 class InDocCheckEvent : public nsRunnable {
 public:
-  nsCOMPtr<nsIContent> mContent;
+  InDocCheckEvent(nsObjectLoadingContent *aContent)
+  : mContent(aContent) {}
 
-  InDocCheckEvent(nsIContent* aContent)
-  : mContent(aContent)
-  {
-  }
-
-  ~InDocCheckEvent()
-  {
-  }
+  ~InDocCheckEvent() {}
 
   NS_IMETHOD Run();
+
+private:
+  nsCOMPtr<nsIObjectLoadingContent> mContent;
 };
 
 NS_IMETHODIMP
 InDocCheckEvent::Run()
 {
-  if (!mContent->IsInDoc()) {
-    nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(mContent);
-    if (olc) {
-      olc->StopPluginInstance();
-    }
+  nsObjectLoadingContent *objLC =
+    static_cast<nsObjectLoadingContent *>(mContent.get());
+
+  nsCOMPtr<nsIContent> content =
+    do_QueryInterface(static_cast<nsIImageLoadingContent *>(objLC));
+
+  if (!content->IsInDoc()) {
+    nsObjectLoadingContent *objLC =
+      static_cast<nsObjectLoadingContent *>(mContent.get());
+    objLC->UnloadObject();
   }
   return NS_OK;
 }
 
 /**
  * A task for firing PluginNotFound and PluginBlocklisted DOM Events.
  */
 class nsPluginErrorEvent : public nsRunnable {
 public:
-  nsCOMPtr<nsIContent> mContent;
-  PluginSupportState mState;
-
-  nsPluginErrorEvent(nsIContent* aContent, PluginSupportState aState)
+  nsPluginErrorEvent(nsIContent* aContent,
+                     nsObjectLoadingContent::FallbackType aFallbackType)
     : mContent(aContent),
-      mState(aState)
-  {}
+      mFallbackType(aFallbackType) {}
 
   ~nsPluginErrorEvent() {}
 
   NS_IMETHOD Run();
+
+private:
+  nsCOMPtr<nsIContent> mContent;
+  nsObjectLoadingContent::FallbackType mFallbackType;
 };
 
 NS_IMETHODIMP
 nsPluginErrorEvent::Run()
 {
-  LOG(("OBJLC []: Firing plugin not found event for content %p\n",
-       mContent.get()));
   nsString type;
-  switch (mState) {
-    case ePluginClickToPlay:
-      type = NS_LITERAL_STRING("PluginClickToPlay");
-      break;
-    case ePluginVulnerableUpdatable:
+  switch (mFallbackType) {
+    case nsObjectLoadingContent::eFallbackVulnerableUpdatable:
       type = NS_LITERAL_STRING("PluginVulnerableUpdatable");
       break;
-    case ePluginVulnerableNoUpdate:
+    case nsObjectLoadingContent::eFallbackVulnerableNoUpdate:
       type = NS_LITERAL_STRING("PluginVulnerableNoUpdate");
       break;
-    case ePluginUnsupported:
+    case nsObjectLoadingContent::eFallbackClickToPlay:
+      type = NS_LITERAL_STRING("PluginClickToPlay");
+      break;
+    case nsObjectLoadingContent::eFallbackUnsupported:
       type = NS_LITERAL_STRING("PluginNotFound");
       break;
-    case ePluginDisabled:
+    case nsObjectLoadingContent::eFallbackDisabled:
       type = NS_LITERAL_STRING("PluginDisabled");
       break;
-    case ePluginBlocklisted:
+    case nsObjectLoadingContent::eFallbackBlocklisted:
       type = NS_LITERAL_STRING("PluginBlocklisted");
       break;
-    case ePluginOutdated:
+    case nsObjectLoadingContent::eFallbackOutdated:
       type = NS_LITERAL_STRING("PluginOutdated");
       break;
     default:
       return NS_OK;
   }
+  LOG(("OBJLC [%p]: nsPluginErrorEvent firing '%s'",
+       mContent.get(), NS_ConvertUTF16toUTF8(type).get()));
   nsContentUtils::DispatchTrustedEvent(mContent->GetDocument(), mContent,
                                        type, true, true);
 
   return NS_OK;
 }
 
 /**
  * A task for firing PluginCrashed DOM Events.
@@ -224,17 +230,17 @@ public:
   ~nsPluginCrashedEvent() {}
 
   NS_IMETHOD Run();
 };
 
 NS_IMETHODIMP
 nsPluginCrashedEvent::Run()
 {
-  LOG(("OBJLC []: Firing plugin crashed event for content %p\n",
+  LOG(("OBJLC [%p]: Firing plugin crashed event\n",
        mContent.get()));
 
   nsCOMPtr<nsIDOMDocument> domDoc =
     do_QueryInterface(mContent->GetDocument());
   if (!domDoc) {
     NS_WARNING("Couldn't get document for PluginCrashed event!");
     return NS_OK;
   }
@@ -246,17 +252,17 @@ nsPluginCrashedEvent::Run()
   if (!containerEvent) {
     NS_WARNING("Couldn't QI event for PluginCrashed event!");
     return NS_OK;
   }
 
   event->InitEvent(NS_LITERAL_STRING("PluginCrashed"), true, true);
   event->SetTrusted(true);
   event->GetInternalNSEvent()->flags |= NS_EVENT_FLAG_ONLY_CHROME_DISPATCH;
-  
+
   nsCOMPtr<nsIWritableVariant> variant;
 
   // add a "pluginDumpID" property to this event
   variant = do_CreateInstance("@mozilla.org/variant;1");
   if (!variant) {
     NS_WARNING("Couldn't create pluginDumpID variant for PluginCrashed event!");
     return NS_OK;
   }
@@ -348,17 +354,18 @@ nsStopPluginRunnable::Run()
     PRUint32 currentLevel = 0;
     appShell->GetEventloopNestingLevel(&currentLevel);
     if (currentLevel > mInstanceOwner->GetLastEventloopNestingLevel()) {
       if (!mTimer)
         mTimer = do_CreateInstance("@mozilla.org/timer;1");
       if (mTimer) {
         // Fire 100ms timer to try to tear down this plugin as quickly as
         // possible once the nesting level comes back down.
-        nsresult rv = mTimer->InitWithCallback(this, 100, nsITimer::TYPE_ONE_SHOT);
+        nsresult rv = mTimer->InitWithCallback(this, 100,
+                                               nsITimer::TYPE_ONE_SHOT);
         if (NS_SUCCEEDED(rv)) {
           return rv;
         }
       }
       NS_ERROR("Failed to setup a timer to stop the plugin later (at a safe "
                "time). Stopping the plugin now, this might crash.");
     }
   }
@@ -366,176 +373,112 @@ nsStopPluginRunnable::Run()
   mTimer = nullptr;
 
   static_cast<nsObjectLoadingContent*>(mContent.get())->
     DoStopPlugin(mInstanceOwner, false, true);
 
   return NS_OK;
 }
 
-class AutoNotifier {
-  public:
-    AutoNotifier(nsObjectLoadingContent* aContent, bool aNotify) :
-      mContent(aContent), mNotify(aNotify) {
-        mOldType = aContent->Type();
-        mOldState = aContent->ObjectState();
-    }
-    ~AutoNotifier() {
-      mContent->NotifyStateChanged(mOldType, mOldState, false, mNotify);
-    }
+// You can't take the address of bitfield members, so we have two separate
+// classes for these :-/
 
-    /**
-     * Send notifications now, ignoring the value of mNotify. The new type and
-     * state is saved, and the destructor will notify again if mNotify is true
-     * and the values changed.
-     */
-    void Notify() {
-      NS_ASSERTION(mNotify, "Should not notify when notify=false");
+// Sets a object's mInstantiating bit to false when destroyed
+class AutoSetInstantiatingToFalse {
+public:
+  AutoSetInstantiatingToFalse(nsObjectLoadingContent *aContent)
+    : mContent(aContent) {}
+  ~AutoSetInstantiatingToFalse() { mContent->mInstantiating = false; }
+private:
+  nsObjectLoadingContent* mContent;
+};
 
-      mContent->NotifyStateChanged(mOldType, mOldState, true, true);
-      mOldType = mContent->Type();
-      mOldState = mContent->ObjectState();
-    }
-
-  private:
-    nsObjectLoadingContent*            mContent;
-    bool                               mNotify;
-    nsObjectLoadingContent::ObjectType mOldType;
-    nsEventStates                      mOldState;
+// Sets a object's mInstantiating bit to false when destroyed
+class AutoSetLoadingToFalse {
+public:
+  AutoSetLoadingToFalse(nsObjectLoadingContent *aContent)
+    : mContent(aContent) {}
+  ~AutoSetLoadingToFalse() { mContent->mIsLoading = false; }
+private:
+  nsObjectLoadingContent* mContent;
 };
 
-/**
- * A class that will automatically fall back if a |rv| variable has a failure
- * code when this class is destroyed. It does not notify.
- */
-class AutoFallback {
-  public:
-    AutoFallback(nsObjectLoadingContent* aContent, const nsresult* rv)
-      : mContent(aContent), mResult(rv), mPluginState(ePluginOtherState) {}
-    ~AutoFallback() {
-      if (NS_FAILED(*mResult)) {
-        LOG(("OBJLC [%p]: rv=%08x, falling back\n", mContent, *mResult));
-        mContent->Fallback(false);
-        if (mPluginState != ePluginOtherState) {
-          mContent->mFallbackReason = mPluginState;
-        }
-      }
+///
+/// Helper functions
+///
+
+static bool
+IsSuccessfulRequest(nsIRequest* aRequest)
+{
+  nsresult status;
+  nsresult rv = aRequest->GetStatus(&status);
+  if (NS_FAILED(rv) || NS_FAILED(status)) {
+    return false;
+  }
+
+  // This may still be an error page or somesuch
+  nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(aRequest));
+  if (httpChan) {
+    bool success;
+    rv = httpChan->GetRequestSucceeded(&success);
+    if (NS_FAILED(rv) || !success) {
+      return false;
     }
+  }
+
+  // Otherwise, the request is successful
+  return true;
+}
 
-    /**
-     * This should be set to something other than ePluginOtherState to indicate
-     * a specific failure that should be passed on.
-     */
-     void SetPluginState(PluginSupportState aState) {
-       NS_ASSERTION(aState != ePluginOtherState, "Should not be setting ePluginOtherState");
-       mPluginState = aState;
-     }
-  private:
-    nsObjectLoadingContent* mContent;
-    const nsresult* mResult;
-    PluginSupportState mPluginState;
-};
+static bool
+CanHandleURI(nsIURI* aURI)
+{
+  nsCAutoString scheme;
+  if (NS_FAILED(aURI->GetScheme(scheme))) {
+    return false;
+  }
+
+  nsIIOService* ios = nsContentUtils::GetIOService();
+  if (!ios)
+    return false;
 
-/**
- * A class that automatically sets mInstantiating to false when it goes
- * out of scope.
- */
-class AutoSetInstantiatingToFalse {
-  public:
-    AutoSetInstantiatingToFalse(nsObjectLoadingContent* objlc) : mContent(objlc) {}
-    ~AutoSetInstantiatingToFalse() { mContent->mInstantiating = false; }
-  private:
-    nsObjectLoadingContent* mContent;
-};
+  nsCOMPtr<nsIProtocolHandler> handler;
+  ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
+  if (!handler) {
+    return false;
+  }
 
-// helper functions
+  nsCOMPtr<nsIExternalProtocolHandler> extHandler =
+    do_QueryInterface(handler);
+  // We can handle this URI if its protocol handler is not the external one
+  return extHandler == nullptr;
+}
+
+// Helper for tedious URI equality syntax when one or both arguments may be
+// null and URIEquals(null, null) should be true
+static bool inline
+URIEquals(nsIURI *a, nsIURI *b)
+{
+  bool equal;
+  return (!a && !b) || (a && b && NS_SUCCEEDED(a->Equals(b, &equal)) && equal);
+}
+
 static bool
 IsSupportedImage(const nsCString& aMimeType)
 {
   imgILoader* loader = nsContentUtils::GetImgLoader();
   if (!loader) {
     return false;
   }
 
   bool supported;
   nsresult rv = loader->SupportImageWithMimeType(aMimeType.get(), &supported);
   return NS_SUCCEEDED(rv) && supported;
 }
 
-nsresult nsObjectLoadingContent::IsPluginEnabledForType(const nsCString& aMIMEType)
-{
-  nsCOMPtr<nsIPluginHost> pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID));
-  nsPluginHost *pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
-  if (!pluginHost) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsresult rv = pluginHost->IsPluginEnabledForType(aMIMEType.get());
-
-  // Check to see if the plugin is disabled before deciding if it
-  // should be in the "click to play" state, since we only want to
-  // display "click to play" UI for enabled plugins.
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  if (!pluginHost->IsPluginClickToPlayForType(aMIMEType.get())) {
-    mCTPPlayable = true;
-  }
-
-  if (!mCTPPlayable) {
-    nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
-    MOZ_ASSERT(thisContent);
-    nsIDocument* ownerDoc = thisContent->OwnerDoc();
-
-    nsCOMPtr<nsIDOMWindow> window = ownerDoc->GetWindow();
-    if (!window) {
-      return NS_ERROR_FAILURE;
-    }
-    nsCOMPtr<nsIDOMWindow> topWindow;
-    rv = window->GetTop(getter_AddRefs(topWindow));
-    NS_ENSURE_SUCCESS(rv, rv);
-    nsCOMPtr<nsIDOMDocument> topDocument;
-    rv = topWindow->GetDocument(getter_AddRefs(topDocument));
-    NS_ENSURE_SUCCESS(rv, rv);
-    nsCOMPtr<nsIDocument> topDoc = do_QueryInterface(topDocument);
-
-    nsCOMPtr<nsIPermissionManager> permissionManager = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-    bool allowPerm = false;
-    // For now we always say that the system principal uses click-to-play since
-    // that maintains current behavior and we have tests that expect this.
-    // What we really should do is disable plugins entirely in pages that use
-    // the system principal, i.e. in chrome pages. That way the click-to-play
-    // code here wouldn't matter at all. Bug 775301 is tracking this.
-    if (!nsContentUtils::IsSystemPrincipal(topDoc->NodePrincipal())) {
-      PRUint32 permission;
-      rv = permissionManager->TestPermissionFromPrincipal(topDoc->NodePrincipal(),
-                                                          "plugins",
-                                                          &permission);
-      NS_ENSURE_SUCCESS(rv, rv);
-      allowPerm = permission == nsIPermissionManager::ALLOW_ACTION;
-    }
-
-    PRUint32 state;
-    rv = pluginHost->GetBlocklistStateForType(aMIMEType.get(), &state);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (allowPerm &&
-        state != nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE &&
-        state != nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
-      mCTPPlayable = true;
-    } else {
-      return NS_ERROR_PLUGIN_CLICKTOPLAY;
-    }
-  }
-
-  return NS_OK;
-}
-
 static void
 GetExtensionFromURI(nsIURI* uri, nsCString& ext)
 {
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
   if (url) {
     url->GetFileExtension(ext);
   } else {
     nsCString spec;
@@ -547,144 +490,229 @@ GetExtensionFromURI(nsIURI* uri, nsCStri
     }
   }
 }
 
 /**
  * Checks whether a plugin exists and is enabled for the extension
  * in the given URI. The MIME type is returned in the mimeType out parameter.
  */
-bool nsObjectLoadingContent::IsPluginEnabledByExtension(nsIURI* uri, nsCString& mimeType)
+bool
+IsPluginEnabledByExtension(nsIURI* uri, nsCString& mimeType)
 {
   nsCAutoString ext;
   GetExtensionFromURI(uri, ext);
-  bool enabled = false;
 
   if (ext.IsEmpty()) {
     return false;
   }
 
-  nsCOMPtr<nsIPluginHost> pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID));
-  nsPluginHost *pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
+  nsRefPtr<nsPluginHost> pluginHost =
+    already_AddRefed<nsPluginHost>(nsPluginHost::GetInst());
+
   if (!pluginHost) {
+    NS_NOTREACHED("No pluginhost");
     return false;
   }
 
   const char* typeFromExt;
-  if (NS_SUCCEEDED(pluginHost->IsPluginEnabledForExtension(ext.get(), typeFromExt))) {
+  nsresult rv = pluginHost->IsPluginEnabledForExtension(ext.get(), typeFromExt);
+  if (NS_SUCCEEDED(rv)) {
     mimeType = typeFromExt;
-    enabled = true;
-
-    if (!pluginHost->IsPluginClickToPlayForType(mimeType.get())) {
-      mCTPPlayable = true;
-    }
+    return true;
   }
-
-  if (!mCTPPlayable) {
-    return false;
-  } else {
-    return enabled;
-  }
+  return false;
 }
 
 nsresult
-nsObjectLoadingContent::BindToTree(nsIDocument* aDocument, nsIContent* /*aParent*/,
+IsPluginEnabledForType(const nsCString& aMIMEType)
+{
+  nsRefPtr<nsPluginHost> pluginHost =
+    already_AddRefed<nsPluginHost>(nsPluginHost::GetInst());
+
+  if (!pluginHost) {
+    NS_NOTREACHED("No pluginhost");
+    return false;
+  }
+
+  nsresult rv = pluginHost->IsPluginEnabledForType(aMIMEType.get());
+
+  // Check to see if the plugin is disabled before deciding if it
+  // should be in the "click to play" state, since we only want to
+  // display "click to play" UI for enabled plugins.
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+///
+/// Member Functions
+///
+
+bool
+nsObjectLoadingContent::IsSupportedDocument(const nsCString& aMimeType)
+{
+  nsCOMPtr<nsIContent> thisContent =
+    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  NS_ASSERTION(thisContent, "must be a content");
+
+  nsresult rv;
+  nsCOMPtr<nsIWebNavigationInfo> info(
+    do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID, &rv));
+  PRUint32 supported;
+  if (info) {
+    nsCOMPtr<nsIWebNavigation> webNav;
+    nsIDocument* currentDoc = thisContent->GetCurrentDoc();
+    if (currentDoc) {
+      webNav = do_GetInterface(currentDoc->GetScriptGlobalObject());
+    }
+    rv = info->IsTypeSupported(aMimeType, webNav, &supported);
+  }
+
+  if (NS_SUCCEEDED(rv)) {
+    if (supported == nsIWebNavigationInfo::UNSUPPORTED) {
+      // Try a stream converter
+      // NOTE: We treat any type we can convert from as a supported type. If a
+      // type is not actually supported, the URI loader will detect that and
+      // return an error, and we'll fallback.
+      nsCOMPtr<nsIStreamConverterService> convServ =
+        do_GetService("@mozilla.org/streamConverters;1");
+      bool canConvert = false;
+      if (convServ) {
+        rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert);
+      }
+      return NS_SUCCEEDED(rv) && canConvert;
+    }
+
+    // Don't want to support plugins as documents
+    return supported != nsIWebNavigationInfo::PLUGIN;
+  }
+
+  return false;
+}
+
+nsresult
+nsObjectLoadingContent::BindToTree(nsIDocument* aDocument,
+                                   nsIContent* /*aParent*/,
                                    nsIContent* /*aBindingParent*/,
                                    bool /*aCompileEventHandlers*/)
 {
   if (aDocument) {
     return aDocument->AddPlugin(this);
   }
   return NS_OK;
 }
 
 void
 nsObjectLoadingContent::UnbindFromTree(bool /*aDeep*/, bool /*aNullParent*/)
 {
-  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
+  nsCOMPtr<nsIContent> thisContent =
+    do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
   MOZ_ASSERT(thisContent);
   nsIDocument* ownerDoc = thisContent->OwnerDoc();
   ownerDoc->RemovePlugin(this);
+
+  if (mType == eType_Plugin) {
+    // we'll let the plugin continue to run at least until we get back to
+    // the event loop. If we get back to the event loop and the node
+    // has still not been added back to the document then we tear down the
+    // plugin
+    nsCOMPtr<nsIRunnable> event = new InDocCheckEvent(this);
+
+    nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
+    if (appShell) {
+      appShell->RunInStableState(event);
+    }
+  } else {
+    /// XXX(johns): The implementation for GenericFrame notes that ideally we
+    ///             would keep the docshell around, but trash the frameloader
+    UnloadObject();
+  }
+
 }
 
 nsObjectLoadingContent::nsObjectLoadingContent()
   : mPendingInstantiateEvent(nullptr)
   , mChannel(nullptr)
   , mType(eType_Loading)
+  , mFallbackType(eFallbackAlternate)
+  , mChannelLoaded(false)
   , mInstantiating(false)
-  , mUserDisabled(false)
-  , mSuppressed(false)
   , mNetworkCreated(true)
+  , mActivated(false)
   , mIsStopping(false)
-  , mSrcStreamLoading(false)
-  , mFallbackReason(ePluginOtherState)
-  , mCTPPlayable(false)
-  , mActivated(false) {}
+  , mIsLoading(false)
+  , mSrcStreamLoading(false) {}
 
 nsObjectLoadingContent::~nsObjectLoadingContent()
 {
-  DestroyImageLoadingContent();
+  // Should have been unbound from the tree at this point, and InDocCheckEvent
+  // keeps us alive
   if (mFrameLoader) {
+    NS_NOTREACHED("Should not be tearing down frame loaders at this point");
     mFrameLoader->Destroy();
   }
+  if (mInstanceOwner) {
+    // This is especially bad as delayed stop will try to hold on to this
+    // object...
+    NS_NOTREACHED("Should not be tearing down a plugin at this point!");
+    StopPluginInstance();
+  }
+  DestroyImageLoadingContent();
 }
 
 nsresult
-nsObjectLoadingContent::InstantiatePluginInstance(const char* aMimeType, nsIURI* aURI)
+nsObjectLoadingContent::InstantiatePluginInstance()
 {
+  if (mType != eType_Plugin) {
+    LOG(("OBJLC [%p]: Refusing to instantiate non-plugin, "
+         "type %u", this, mType));
+    return NS_OK;
+  }
+
   // Don't do anything if we already have an active instance.
   if (mInstanceOwner) {
     return NS_OK;
   }
 
   // Don't allow re-entry into initialization code.
   if (mInstantiating) {
     return NS_OK;
   }
   mInstantiating = true;
   AutoSetInstantiatingToFalse autoInstantiating(this);
 
   // Instantiating an instance can result in script execution, which
   // can destroy this DOM object. Don't allow that for the scope
   // of this method.
   nsCOMPtr<nsIObjectLoadingContent> kungFuDeathGrip = this;
-  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
 
-  nsCOMPtr<nsIURI> baseURI;
-  if (!aURI) {
-    // We need some URI. If we have nothing else, use the base URI.
-    // XXX(biesi): The code used to do this. Not sure why this is correct...
-    GetObjectBaseURI(thisContent, getter_AddRefs(baseURI));
-    aURI = baseURI;
-  }
-
+  nsCOMPtr<nsIContent> thisContent =
+    do_QueryInterface(static_cast<nsIImageLoadingContent *>(this));
   // Flush layout so that the plugin is initialized with the latest information.
   nsIDocument* doc = thisContent->GetCurrentDoc();
   if (!doc) {
     return NS_ERROR_FAILURE;
   }
   if (!doc->IsActive()) {
-    NS_ERROR("Shouldn't be calling InstantiatePluginInstance in an inactive document");
+    NS_ERROR("Shouldn't be calling "
+             "InstantiatePluginInstance in an inactive document");
     return NS_ERROR_FAILURE;
   }
   doc->FlushPendingNotifications(Flush_Layout);
 
   nsresult rv = NS_ERROR_FAILURE;
-  nsCOMPtr<nsIPluginHost> pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID, &rv));
-  nsPluginHost* pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
+  nsRefPtr<nsPluginHost> pluginHost =
+    already_AddRefed<nsPluginHost>(nsPluginHost::GetInst());
 
-  if (!pluginHost->IsPluginClickToPlayForType(aMimeType)) {
-    mCTPPlayable = true;
-  }
-
-  if (!mCTPPlayable) {
-    return NS_ERROR_PLUGIN_CLICKTOPLAY;
+  if (!pluginHost) {
+    NS_NOTREACHED("No pluginhost");
+    return false;
   }
 
   // If you add early return(s), be sure to balance this call to
   // appShell->SuspendNative() with additional call(s) to
   // appShell->ReturnNative().
   nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
   if (appShell) {
     appShell->SuspendNative();
@@ -693,23 +721,26 @@ nsObjectLoadingContent::InstantiatePlugi
   nsCOMPtr<nsIPluginDocument> pDoc(do_QueryInterface(doc));
   bool fullPageMode = false;
   if (pDoc) {
     pDoc->GetWillHandleInstantiation(&fullPageMode);
   }
 
   if (fullPageMode) {
     nsCOMPtr<nsIStreamListener> stream;
-    rv = pluginHost->InstantiateFullPagePluginInstance(aMimeType, aURI, this,
-                                                       getter_AddRefs(mInstanceOwner), getter_AddRefs(stream));
+    rv = pluginHost->InstantiateFullPagePluginInstance(mContentType.get(),
+                                                       mURI.get(), this,
+                                                       getter_AddRefs(mInstanceOwner),
+                                                       getter_AddRefs(stream));
     if (NS_SUCCEEDED(rv)) {
       pDoc->SetStreamListener(stream);
     }
   } else {
-    rv = pluginHost->InstantiateEmbeddedPluginInstance(aMimeType, aURI, this,
+    rv = pluginHost->InstantiateEmbeddedPluginInstance(mContentType.get(),
+                                                       mURI.get(), this,
                                                        getter_AddRefs(mInstanceOwner));
   }
 
   if (appShell) {
     appShell->ResumeNative();
   }
 
   if (NS_FAILED(rv)) {
@@ -718,307 +749,90 @@ nsObjectLoadingContent::InstantiatePlugi
 
   // Set up scripting interfaces.
   NotifyContentObjectWrapper();
 
   nsRefPtr<nsNPAPIPluginInstance> pluginInstance;
   GetPluginInstance(getter_AddRefs(pluginInstance));
   if (pluginInstance) {
     nsCOMPtr<nsIPluginTag> pluginTag;
-    pluginHost->GetPluginTagForInstance(pluginInstance, getter_AddRefs(pluginTag));
-    
+    pluginHost->GetPluginTagForInstance(pluginInstance,
+                                        getter_AddRefs(pluginTag));
+
     nsCOMPtr<nsIBlocklistService> blocklist =
-    do_GetService("@mozilla.org/extensions/blocklist;1");
+      do_GetService("@mozilla.org/extensions/blocklist;1");
     if (blocklist) {
       PRUint32 blockState = nsIBlocklistService::STATE_NOT_BLOCKED;
       blocklist->GetPluginBlocklistState(pluginTag, EmptyString(),
                                          EmptyString(), &blockState);
       if (blockState == nsIBlocklistService::STATE_OUTDATED)
-        FirePluginError(thisContent, ePluginOutdated);
+        FirePluginError(eFallbackOutdated);
     }
   }
 
-  mActivated = true;
   return NS_OK;
 }
 
 void
 nsObjectLoadingContent::NotifyOwnerDocumentActivityChanged()
 {
   if (!mInstanceOwner) {
     return;
   }
 
-  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  nsCOMPtr<nsIContent> thisContent =
+    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   nsIDocument* ownerDoc = thisContent->OwnerDoc();
   if (!ownerDoc->IsActive()) {
     StopPluginInstance();
   }
 }
 
 // nsIRequestObserver
 NS_IMETHODIMP
 nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest,
                                        nsISupports *aContext)
 {
+  /// This must call LoadObject, even upon failure, to allow it to either
+  /// proceed with the load, or trigger fallback content.
+
   SAMPLE_LABEL("nsObjectLoadingContent", "OnStartRequest");
 
+  LOG(("OBJLC [%p]: Channel OnStartRequest", this));
+
   if (aRequest != mChannel || !aRequest) {
-    // This is a bit of an edge case - happens when a new load starts before the
-    // previous one got here
+    // happens when a new load starts before the previous one got here
     return NS_BINDING_ABORTED;
   }
 
-  AutoNotifier notifier(this, true);
+  NS_ASSERTION(!mChannelLoaded, "mChannelLoaded set already?");
+  NS_ASSERTION(!mFinalListener, "mFinalListener exists already?");
 
-  if (!IsSuccessfulRequest(aRequest)) {
-    LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this));
-    Fallback(false);
-    return NS_BINDING_ABORTED;
-  }
+  mChannelLoaded = true;
 
   nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
   NS_ASSERTION(chan, "Why is our request not a channel?");
 
-  nsresult rv = NS_ERROR_UNEXPECTED;
-  // This fallback variable MUST be declared after the notifier variable. Do NOT
-  // change the order of the declarations!
-  AutoFallback fallback(this, &rv);
+  nsCOMPtr<nsIURI> uri;
 
-  nsCString channelType;
-  rv = chan->GetContentType(channelType);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (channelType.EqualsASCII(APPLICATION_GUESS_FROM_EXT)) {
-    channelType = APPLICATION_OCTET_STREAM;
-    chan->SetContentType(channelType);
+  if (IsSuccessfulRequest(aRequest)) {
+    chan->GetURI(getter_AddRefs(uri));
   }
 
-  // We want to ignore the channel type if one of the following is true:
-  //
-  // 1) The channel type is application/octet-stream or binary/octet-stream 
-  //    and we have a type hint (in mContentType) and the type hint is not a
-  //    document type.
-  // 2) Our type hint is a type that we support with a plugin
-  //    (where "support" means it is enabled or it is click-to-play)
-  //    and this object loading content has the capability to load a plugin.
-  //    We have to be careful here - there might be a plugin that supports
-  //    image types, so make sure the type of the content is not an image.
-  bool isOctetStream = (channelType.EqualsASCII(APPLICATION_OCTET_STREAM) ||
-                        channelType.EqualsASCII(BINARY_OCTET_STREAM));
-  ObjectType typeOfContent = GetTypeOfContent(mContentType);
-  bool caseOne = (isOctetStream &&
-                  !mContentType.IsEmpty() &&
-                  typeOfContent != eType_Document);
-  nsresult pluginState = IsPluginEnabledForType(mContentType);
-  bool pluginSupported = (NS_SUCCEEDED(pluginState) || 
-                          pluginState == NS_ERROR_PLUGIN_CLICKTOPLAY);
-  PRUint32 caps = GetCapabilities();
-  bool caseTwo = (pluginSupported && 
-                  (caps & eSupportPlugins) &&
-                  typeOfContent != eType_Image &&
-                  typeOfContent != eType_Document);
-  if (caseOne || caseTwo) {
-    // Set the type we'll use for dispatch on the channel.  Otherwise we could
-    // end up trying to dispatch to a nsFrameLoader, which will complain that
-    // it couldn't find a way to handle application/octet-stream
-    nsCAutoString typeHint, dummy;
-    NS_ParseContentType(mContentType, typeHint, dummy);
-    if (!typeHint.IsEmpty()) {
-      chan->SetContentType(typeHint);
-    }
-  } else {
-    mContentType = channelType;
-  }
-
-  nsCOMPtr<nsIURI> uri;
-  chan->GetURI(getter_AddRefs(uri));
   if (!uri) {
+    LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this));
+    // If the request fails, we still call LoadObject() to handle fallback
+    // content and notifying of failure. (mChannelLoaded && !mChannel) indicates
+    // the bad state.
+    mChannel = nullptr;
+    LoadObject(true, false);
     return NS_ERROR_FAILURE;
   }
 
-  if (mContentType.EqualsASCII(APPLICATION_OCTET_STREAM) ||
-      mContentType.EqualsASCII(BINARY_OCTET_STREAM)) {
-    nsCAutoString extType;
-    if (IsPluginEnabledByExtension(uri, extType)) {
-      mContentType = extType;
-      chan->SetContentType(extType);
-    }
-  }
-
-  // Now find out what type the content is
-  // UnloadContent will set our type to null; need to be sure to only set it to
-  // the real value on success
-  ObjectType newType = GetTypeOfContent(mContentType);
-  LOG(("OBJLC [%p]: OnStartRequest: Content Type=<%s> Old type=%u New Type=%u\n",
-       this, mContentType.get(), mType, newType));
-
-  // Now do a content policy check
-  // XXXbz this duplicates some code in nsContentBlocker::ShouldLoad  
-  PRInt32 contentPolicyType;
-  switch (newType) {
-    case eType_Image:
-      contentPolicyType = nsIContentPolicy::TYPE_IMAGE;
-      break;
-    case eType_Document:
-      contentPolicyType = nsIContentPolicy::TYPE_SUBDOCUMENT;
-      break;
-    default:
-      contentPolicyType = nsIContentPolicy::TYPE_OBJECT;
-      break;
-  }
-  nsCOMPtr<nsIContent> thisContent = 
-    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
-  NS_ASSERTION(thisContent, "must be a content");
-
-  nsIDocument* doc = thisContent->OwnerDoc();
-
-  PRInt16 shouldProcess = nsIContentPolicy::ACCEPT;
-  rv =
-    NS_CheckContentProcessPolicy(contentPolicyType,
-                                 uri,
-                                 doc->NodePrincipal(),
-                                 static_cast<nsIImageLoadingContent*>(this),
-                                 mContentType,
-                                 nullptr, //extra
-                                 &shouldProcess,
-                                 nsContentUtils::GetContentPolicy(),
-                                 nsContentUtils::GetSecurityManager());
-  if (NS_FAILED(rv) || NS_CP_REJECTED(shouldProcess)) {
-    HandleBeingBlockedByContentPolicy(rv, shouldProcess);
-    rv = NS_OK; // otherwise, the AutoFallback will make us fall back
-    return NS_BINDING_ABORTED;
-  }  
-  
-  if (mType != newType) {
-    UnloadContent();
-  }
-
-  switch (newType) {
-    case eType_Image:
-      rv = LoadImageWithChannel(chan, getter_AddRefs(mFinalListener));
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      // If we have a success result but no final listener, then the image is
-      // cached. In that case, we can just return: No need to try to call the
-      // final listener.
-      if (!mFinalListener) {
-        mType = newType;
-        return NS_BINDING_ABORTED;
-      }
-      break;
-    case eType_Document: {
-      if (!mFrameLoader) {
-        mFrameLoader = nsFrameLoader::Create(thisContent->AsElement(),
-                                             mNetworkCreated);
-        if (!mFrameLoader) {
-          Fallback(false);
-          return NS_ERROR_UNEXPECTED;
-        }
-      }
-
-      rv = mFrameLoader->CheckForRecursiveLoad(uri);
-      if (NS_FAILED(rv)) {
-        Fallback(false);
-        return rv;
-      }
-
-      if (mType != newType) {
-        // XXX We must call this before getting the docshell to work around
-        // bug 300540; when that's fixed, this if statement can be removed.
-        mType = newType;
-        notifier.Notify();
-
-        if (!mFrameLoader) {
-          // mFrameLoader got nulled out when we notified, which most
-          // likely means the node was removed from the
-          // document. Abort the load that just started.
-          return NS_BINDING_ABORTED;
-        }
-      }
-
-      // We're loading a document, so we have to set LOAD_DOCUMENT_URI
-      // (especially important for firing onload)
-      nsLoadFlags flags = 0;
-      chan->GetLoadFlags(&flags);
-      flags |= nsIChannel::LOAD_DOCUMENT_URI;
-      chan->SetLoadFlags(flags);
-
-      nsCOMPtr<nsIDocShell> docShell;
-      rv = mFrameLoader->GetDocShell(getter_AddRefs(docShell));
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(docShell));
-      NS_ASSERTION(req, "Docshell must be an ifreq");
-
-      nsCOMPtr<nsIURILoader>
-        uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID, &rv));
-      NS_ENSURE_SUCCESS(rv, rv);
-      rv = uriLoader->OpenChannel(chan, nsIURILoader::DONT_RETARGET, req,
-                                  getter_AddRefs(mFinalListener));
-      break;
-    }
-    case eType_Plugin: {
-      if (mType != newType) {
-        mType = newType;
-        notifier.Notify();
-      }
-      nsCOMPtr<nsIPluginHost> pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID));
-      nsPluginHost *pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
-      if (!pluginHost) {
-        return NS_ERROR_NOT_AVAILABLE;
-      }
-      pluginHost->NewEmbeddedPluginStreamListener(uri, this, nullptr,
-                                                  getter_AddRefs(mFinalListener));
-      break;
-    }
-    case eType_Loading:
-      NS_NOTREACHED("Should not have a loading type here!");
-    case eType_Null:
-      // Need to fallback here (instead of using the case below), so that we can
-      // set mFallbackReason without it being overwritten. This is also why we
-      // return early.
-      Fallback(false);
-
-      PluginSupportState pluginState = GetPluginSupportState(thisContent,
-                                                             mContentType);
-      // Do nothing, but fire the plugin not found event if needed
-      if (pluginState != ePluginOtherState) {
-        mFallbackReason = pluginState;
-        FirePluginError(thisContent, pluginState);
-      }
-      return NS_BINDING_ABORTED;
-  }
-
-  if (mFinalListener) {
-    mType = newType;
-
-    mSrcStreamLoading = true;
-    rv = mFinalListener->OnStartRequest(aRequest, aContext);
-    mSrcStreamLoading = false;
-
-    if (NS_SUCCEEDED(rv)) {
-      // Plugins need to set up for NPRuntime.
-      if (mType == eType_Plugin) {
-        NotifyContentObjectWrapper();
-      }
-    } else {
-      // Plugins don't fall back if there is an error here.
-      if (mType == eType_Plugin) {
-        rv = NS_OK; // this is necessary to avoid auto-fallback
-        return NS_BINDING_ABORTED;
-      }
-      Fallback(false);
-    }
-
-    return rv;
-  }
-
-  Fallback(false);
-  return NS_BINDING_ABORTED;
+  return LoadObject(true, false, aRequest);
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::OnStopRequest(nsIRequest *aRequest,
                                       nsISupports *aContext,
                                       nsresult aStatusCode)
 {
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
@@ -1048,29 +862,32 @@ nsObjectLoadingContent::OnDataAvailable(
 {
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
 
   if (aRequest != mChannel) {
     return NS_BINDING_ABORTED;
   }
 
   if (mFinalListener) {
-    return mFinalListener->OnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
+    return mFinalListener->OnDataAvailable(aRequest, aContext, aInputStream,
+                                           aOffset, aCount);
   }
 
-  // Abort this load if we have no listener here
+  // We shouldn't have a connected channel with no final listener
+  NS_NOTREACHED("Got data for channel with no connected final listener");
+  mChannel = nullptr;
+
   return NS_ERROR_UNEXPECTED;
 }
 
 // nsIFrameLoaderOwner
 NS_IMETHODIMP
 nsObjectLoadingContent::GetFrameLoader(nsIFrameLoader** aFrameLoader)
 {
-  *aFrameLoader = mFrameLoader;
-  NS_IF_ADDREF(*aFrameLoader);
+  NS_IF_ADDREF(*aFrameLoader = mFrameLoader);
   return NS_OK;
 }
 
 NS_IMETHODIMP_(already_AddRefed<nsFrameLoader>)
 nsObjectLoadingContent::GetFrameLoader()
 {
   nsFrameLoader* loader = mFrameLoader;
   NS_IF_ADDREF(loader);
@@ -1078,17 +895,16 @@ nsObjectLoadingContent::GetFrameLoader()
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoader)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-// nsIObjectLoadingContent
 NS_IMETHODIMP
 nsObjectLoadingContent::GetActualType(nsACString& aType)
 {
   aType = mContentType;
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -1096,34 +912,35 @@ nsObjectLoadingContent::GetDisplayedType
 {
   *aType = mType;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::HasNewFrame(nsIObjectFrame* aFrame)
 {
-  // Not having an instance yet is OK, but try to start one now that
-  // we have a frame.
-  if (!mInstanceOwner) {
-    AsyncStartPluginInstance();
-    return NS_OK;
-  }
+  if (mType == eType_Plugin) {
+    if (!mInstanceOwner) {
+      // We have successfully set ourselves up in LoadObject, but not spawned an
+      // instance due to a lack of a frame.
+      AsyncStartPluginInstance();
+      return NS_OK;
+    }
 
-  // Disconnect any existing frame
-  DisconnectFrame();
+    // Disconnect any existing frame
+    DisconnectFrame();
 
-  // Set up relationship between instance owner and frame.
-  nsObjectFrame *objFrame = static_cast<nsObjectFrame*>(aFrame);
-  mInstanceOwner->SetFrame(objFrame);
+    // Set up relationship between instance owner and frame.
+    nsObjectFrame *objFrame = static_cast<nsObjectFrame*>(aFrame);
+    mInstanceOwner->SetFrame(objFrame);
 
-  // Set up new frame to draw.
-  objFrame->FixupWindow(objFrame->GetContentRectRelativeToSelf().Size());
-  objFrame->Invalidate(objFrame->GetContentRectRelativeToSelf());
-
+    // Set up new frame to draw.
+    objFrame->FixupWindow(objFrame->GetContentRectRelativeToSelf().Size());
+    objFrame->Invalidate(objFrame->GetContentRectRelativeToSelf());
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::DisconnectFrame()
 {
   if (mInstanceOwner) {
     mInstanceOwner->SetFrame(nullptr);
@@ -1193,596 +1010,952 @@ nsObjectLoadingContent::ObjectState() co
       return ImageState();
     case eType_Plugin:
     case eType_Document:
       // These are OK. If documents start to load successfully, they display
       // something, and are thus not broken in this sense. The same goes for
       // plugins.
       return nsEventStates();
     case eType_Null:
-      if (mSuppressed)
-        return NS_EVENT_STATE_SUPPRESSED;
-      if (mUserDisabled)
-        return NS_EVENT_STATE_USERDISABLED;
-
-      // Otherwise, broken
-      nsEventStates state = NS_EVENT_STATE_BROKEN;
-      switch (mFallbackReason) {
-        case ePluginClickToPlay:
+      switch (mFallbackType) {
+        case eFallbackSuppressed:
+          return NS_EVENT_STATE_SUPPRESSED;
+        case eFallbackUserDisabled:
+          return NS_EVENT_STATE_USERDISABLED;
+        case eFallbackClickToPlay:
           return NS_EVENT_STATE_TYPE_CLICK_TO_PLAY;
-        case ePluginVulnerableUpdatable:
-          return NS_EVENT_STATE_VULNERABLE_UPDATABLE;
-        case ePluginVulnerableNoUpdate:
-          return NS_EVENT_STATE_VULNERABLE_NO_UPDATE;
-        case ePluginDisabled:
-          state |= NS_EVENT_STATE_HANDLER_DISABLED;
-          break;
-        case ePluginBlocklisted:
-          state |= NS_EVENT_STATE_HANDLER_BLOCKED;
-          break;
-        case ePluginCrashed:
-          state |= NS_EVENT_STATE_HANDLER_CRASHED;
-          break;
-        case ePluginUnsupported: {
+        case eFallbackDisabled:
+          return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_DISABLED;
+        case eFallbackBlocklisted:
+          return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_BLOCKED;
+        case eFallbackCrashed:
+          return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_CRASHED;
+        case eFallbackUnsupported: {
           // Check to see if plugins are blocked on this platform.
           char* pluginsBlocked = PR_GetEnv("MOZ_PLUGINS_BLOCKED");
           if (pluginsBlocked && pluginsBlocked[0] == '1') {
-            state |= NS_EVENT_STATE_TYPE_UNSUPPORTED_PLATFORM;
+            return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_TYPE_UNSUPPORTED_PLATFORM;
           } else {
-            state |= NS_EVENT_STATE_TYPE_UNSUPPORTED;
+            return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_TYPE_UNSUPPORTED;
           }
-          break;
         }
-        case ePluginOutdated:
-        case ePluginOtherState:
-          // Do nothing, but avoid a compile warning
-          break;
+        case eFallbackOutdated:
+        case eFallbackAlternate:
+          return NS_EVENT_STATE_BROKEN;
+        case eFallbackVulnerableUpdatable:
+          return NS_EVENT_STATE_VULNERABLE_UPDATABLE;
+        case eFallbackVulnerableNoUpdate:
+          return NS_EVENT_STATE_VULNERABLE_NO_UPDATE;
       }
-      return state;
   };
   NS_NOTREACHED("unknown type?");
-  // this return statement only exists to avoid a compile warning
-  return nsEventStates();
+  return NS_EVENT_STATE_LOADING;
+}
+
+// Helper to call CheckURILoad on URI -> BaseURI and BaseURI -> Origin
+bool nsObjectLoadingContent::CheckObjectURIs(PRInt16 *aContentPolicy,
+                                             PRInt32 aContentPolicyType)
+{
+  nsCOMPtr<nsIContent> thisContent =
+    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  NS_ASSERTION(thisContent, "Must be an instance of content");
+
+  nsCOMPtr<nsIURI> docBaseURI = thisContent->GetBaseURI();
+
+  // Must have these to load
+  if (!aContentPolicy || !mBaseURI) {
+    return false;
+  }
+
+  bool ret;
+  if (!URIEquals(mBaseURI, docBaseURI)) {
+    // If our object sets a new baseURI, make sure that base URI could be
+    // loaded by the document
+    ret = CheckURILoad(mBaseURI, aContentPolicy, aContentPolicyType);
+    if (!ret) {
+      return false;
+    }
+  }
+
+  if (mURI) {
+    return CheckURILoad(mURI, aContentPolicy, aContentPolicyType);
+  }
+
+  return true;
+}
+
+bool nsObjectLoadingContent::CheckURILoad(nsIURI *aURI,
+                                          PRInt16 *aContentPolicy,
+                                          PRInt32 aContentPolicyType)
+{
+  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
+  NS_ASSERTION(secMan, "No security manager!?");
+
+  nsCOMPtr<nsIContent> thisContent =
+    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  NS_ASSERTION(thisContent, "Must be an instance of content");
+
+  nsCOMPtr<nsIURI> docBaseURI = thisContent->GetBaseURI();
+
+  nsIDocument* doc = thisContent->OwnerDoc();
+  nsresult rv =
+    secMan->CheckLoadURIWithPrincipal(thisContent->NodePrincipal(), aURI, 0);
+  
+  if (NS_FAILED(rv)) {
+    nsCAutoString uri;
+    nsCAutoString baseUri;
+    aURI->GetSpec(uri);
+    aURI->GetSpec(baseUri);
+    LOG(("OBJLC [%p]: CheckLoadURIWithPrincipal denied load of %s (base %s)",
+         this, uri.get(), baseUri.get()));
+    return false;
+  }
+
+  PRInt16 shouldLoad = nsIContentPolicy::ACCEPT; // default permit
+  rv = NS_CheckContentLoadPolicy(aContentPolicyType,
+                                 aURI,
+                                 doc->NodePrincipal(),
+                                 static_cast<nsIImageLoadingContent*>(this),
+                                 mContentType,
+                                 nullptr, //extra
+                                 &shouldLoad,
+                                 nsContentUtils::GetContentPolicy(),
+                                 secMan);
+  NS_ENSURE_SUCCESS(rv, false);
+  if (aContentPolicy) {
+    *aContentPolicy = shouldLoad;
+  }
+  if (NS_CP_REJECTED(shouldLoad)) {
+    nsCAutoString uri;
+    nsCAutoString baseUri;
+    aURI->GetSpec(uri);
+    aURI->GetSpec(baseUri);
+    LOG(("OBJLC [%p]: Content policy denied load of %s (base %s)",
+         this, uri.get(), baseUri.get()));
+    return false;
+  }
+  return true;
 }
 
-// <protected>
+nsObjectLoadingContent::ParameterUpdateFlags
+nsObjectLoadingContent::UpdateObjectParameters()
+{
+  nsCOMPtr<nsIContent> thisContent =
+    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  NS_ASSERTION(thisContent, "Must be an instance of content");
+
+  PRUint32 caps = GetCapabilities();
+  LOG(("OBJLC [%p]: Updating object parameters", this));
+
+  nsresult rv;
+  nsCAutoString newMime;
+  nsCOMPtr<nsIURI> newURI;
+  nsCOMPtr<nsIURI> newBaseURI;
+  ObjectType newType;
+  // Set if this state can't be used to load anything, forces eType_Null
+  bool stateInvalid = false;
+  // Indicates what parameters changed.
+  // eParamChannelChanged - means parameters that affect channel opening
+  //                        decisions changed
+  // eParamStateChanged -   means anything that affects what content we load
+  //                        changed, even if the channel we'd open remains the
+  //                        same.
+  //
+  // State changes outside of the channel parameters only matter if we've
+  // already opened a channel or tried to instantiate content, whereas channel
+  // parameter changes require re-opening the channel even if we haven't gotten
+  // that far.
+  nsObjectLoadingContent::ParameterUpdateFlags retval = eParamNoChange;
+
+  ///
+  /// Initial MIME Type
+  ///
+  if (thisContent->NodeInfo()->Equals(nsGkAtoms::applet)) {
+    newMime.AssignLiteral("application/x-java-vm");
+  } else {
+    nsAutoString typeAttr;
+    thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type, typeAttr);
+    if (!typeAttr.IsEmpty()) {
+      CopyUTF16toUTF8(typeAttr, newMime);
+    }
+  }
+
+  ///
+  /// classID
+  ///
+
+  bool usingClassID = false;
+  if (caps & eSupportClassID) {
+    nsAutoString classIDAttr;
+    thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::classid, classIDAttr);
+    if (!classIDAttr.IsEmpty()) {
+      usingClassID = true;
+      if (NS_FAILED(TypeForClassID(classIDAttr, newMime))) {
+        // XXX(johns): Our de-facto behavior since forever was to refuse to load
+        // Objects who don't have a classid we support, regardless of other type
+        // or uri info leads to a valid plugin.
+        newMime.Assign("");
+        stateInvalid = true;
+      }
+    }
+  }
+
+  ///
+  /// Codebase
+  ///
+
+  nsAutoString codebaseStr;
+  nsCOMPtr<nsIURI> docBaseURI = thisContent->GetBaseURI();
+  thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::codebase, codebaseStr);
+  if (codebaseStr.IsEmpty() && thisContent->NodeInfo()->Equals(nsGkAtoms::applet)) {
+    // bug 406541
+    // NOTE we send the full absolute URI resolved here to java in
+    //      pluginInstanceOwner to avoid disagreements between parsing of
+    //      relative URIs. We need to mimic java's quirks here to make that
+    //      not break things.
+    codebaseStr.AssignLiteral("/"); // Java resolves codebase="" as "/"
+    // XXX(johns) This doesn't catch the case of "file:" which java would
+    // interpret as "file:///" but we would interpret as this document's URI
+    // but with a changed scheme.
+  }
+
+  if (!codebaseStr.IsEmpty()) {
+    rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(newBaseURI),
+                                                   codebaseStr,
+                                                   thisContent->OwnerDoc(),
+                                                   docBaseURI);
+    if (NS_SUCCEEDED(rv)) {
+      NS_TryToSetImmutable(newBaseURI);
+    } else {
+      // Malformed URI
+      LOG(("OBJLC [%p]: Could not parse plugin's codebase as a URI, "
+           "will use document baseURI instead", this));
+    }
+  }
+
+  // Otherwise, use normal document baseURI
+  if (!newBaseURI) {
+    newBaseURI = docBaseURI;
+  }
+
+  ///
+  /// URI
+  ///
+
+  nsAutoString uriStr;
+  // Different elements keep this in various locations
+  if (thisContent->NodeInfo()->Equals(nsGkAtoms::object)) {
+    thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::data, uriStr);
+  } else if (thisContent->NodeInfo()->Equals(nsGkAtoms::embed)) {
+    thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, uriStr);
+  } else if (thisContent->NodeInfo()->Equals(nsGkAtoms::applet)) {
+    // Applet tags have no URI, and their 'code=' parameter is not parsed as one
+  } else {
+    NS_NOTREACHED("Unrecognized plugin-loading tag");
+  }
+
+  // Note that the baseURI changing could affect the newURI, even if uriStr did
+  // not change.
+  if (!uriStr.IsEmpty()) {
+    rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(newURI),
+                                                   uriStr,
+                                                   thisContent->OwnerDoc(),
+                                                   newBaseURI);
+    if (NS_SUCCEEDED(rv)) {
+      NS_TryToSetImmutable(newURI);
+    } else {
+      stateInvalid = true;
+    }
+  }
+
+  ///
+  /// Check if the original (pre-channel) content-type or URI changed, and
+  /// record mOriginal{ContentType,URI}
+  ///
+
+  if ((mOriginalContentType != newMime) || !URIEquals(mOriginalURI, newURI)) {
+    // These parameters changing requires re-opening the channel, so don't
+    // consider the currently-open channel below
+    // XXX(johns): Changing the mime type might change our decision on whether
+    //             or not we load a channel, so we count changes to it as a
+    //             channel parameter change for the sake of simplicity.
+    retval = (ParameterUpdateFlags)(retval | eParamChannelChanged);
+    LOG(("OBJLC [%p]: Channel parameters changed", this));
+  }
+  mOriginalContentType = newMime;
+  mOriginalURI = newURI;
+
+  ///
+  /// If we have a channel, see if its MIME type should take precendence and
+  /// check the final (redirected) URL
+  ///
+
+  // If we have a loaded channel and channel parameters did not change, use it
+  // to determine what we would load.
+  bool useChannel = mChannelLoaded && !(retval & eParamChannelChanged);
+  if (mChannel && useChannel) {
+    nsCString channelType;
+    rv = mChannel->GetContentType(channelType);
+    if (NS_FAILED(rv)) {
+      NS_NOTREACHED("GetContentType failed");
+      stateInvalid = true;
+      channelType.Assign("");
+    }
+
+    LOG(("OBJLC [%p]: Channel has a content type of %s", this, channelType.get()));
+
+    bool binaryChannelType = false;
+    if (channelType.EqualsASCII(APPLICATION_GUESS_FROM_EXT)) {
+      channelType = APPLICATION_OCTET_STREAM;
+      mChannel->SetContentType(channelType);
+      binaryChannelType = true;
+    } else if (channelType.EqualsASCII(APPLICATION_OCTET_STREAM)
+               || channelType.EqualsASCII(BINARY_OCTET_STREAM)) {
+      binaryChannelType = true;
+    }
+
+    // Channel can change our URI through redirection
+    rv = NS_GetFinalChannelURI(mChannel, getter_AddRefs(newURI));
+    if (NS_FAILED(rv)) {
+      NS_NOTREACHED("NS_GetFinalChannelURI failure");
+      stateInvalid = true;
+    }
+
+    // The channel type overrides the guessed / provided type, except when:
+    //
+    // 1) If the channel returns a binary stream type, and we have a type hint
+    //    for a non-document (we never want to display binary-as-document),
+    //    use our type hint instead.
+    // 2) Our type hint is a type that we support with a plugin, ignore the
+    //    server's type
+    //
+    //    XXX(johns): HTML5's "typesmustmatch" attribute would need to be
+    //                honored here if implemented
+
+    ObjectType typeHint = newMime.IsEmpty() ? eType_Null : GetTypeOfContent(newMime);
+
+    bool caseOne = binaryChannelType
+                   && typeHint != eType_Null
+                   && typeHint != eType_Document;
+    bool caseTwo = typeHint == eType_Plugin;
+    if (caseOne || caseTwo) {
+        // Set the type we'll use for dispatch on the channel.  Otherwise we could
+        // end up trying to dispatch to a nsFrameLoader, which will complain that
+        // it couldn't find a way to handle application/octet-stream
+        nsCAutoString typeHint, dummy;
+        NS_ParseContentType(newMime, typeHint, dummy);
+        if (!typeHint.IsEmpty()) {
+          mChannel->SetContentType(typeHint);
+        }
+    } else if (binaryChannelType
+               && IsPluginEnabledByExtension(newURI, newMime)) {
+      mChannel->SetContentType(newMime);
+    } else {
+      newMime = channelType;
+    }
+  }
+
+  bool isJava = nsPluginHost::IsJavaMIMEType(newMime.get());
+  if (useChannel && (!mChannel || isJava)) {
+    // Sanity checks
+    // - Java does not load with a channel, and being java retroactively changes
+    //   how we may have interpreted the codebase to construct this URI above.
+    //   Because the behavior here is more or less undefined, play it safe and
+    //   reject the load.
+    //
+    // - (useChannel && !mChannel) is true if a channel was opened but was
+    //   subsequently invalidated
+    //   in that case.
+    stateInvalid = true;
+  }
+
+  ///
+  /// Determine final type
+  ///
+  //  1) If we have attempted channel load, or set stateInvalid above, the type
+  //     is always null (fallback)
+  //  2) Otherwise, If we have a loaded channel, we grabbed its mimeType above,
+  //     use that type.
+  //  3) Otherwise, See if we can load this as a plugin without a channel
+  //     (image/document types always need a channel).
+  //     - If we have indication this is a plugin (mime, extension, or classID)
+  //       AND:
+  //       - We have eAllowPluginSkipChannel OR
+  //       - We have no URI in the first place OR
+  //       - We're loading based on classID
+  //         XXX(johns): Legacy behavior is to skip channel loading if we have
+  //                     a classID. I don't know why.
+  //  3) Otherwise, if we have a URI, set type to loading to indicate
+  //     we'd need a channel to proceed.
+  //  4) Otherwise, type null to indicate unloadable content (fallback)
+  //
+  // XXX(johns): <embed> tags both support URIs and have
+  //   eAllowPluginSkipChannel, meaning it is possible that we have a URI, but
+  //   are not going to open a channel for it. The old objLC code did this (in a
+  //   less obviously-intended way), so it's probably best not to change our
+  //   behavior at this point.
+  //   We ALSO skip channel loading for objects whose type is found by ClassID
+  //   (We only support a tiny subset of classid: java and ActiveX, above)
+  //
+
+  if (stateInvalid) {
+    newType = eType_Null;
+  } else if (useChannel) {
+      // If useChannel is set above, we considered it in setting newMime
+      newType = GetTypeOfContent(newMime);
+      LOG(("OBJLC [%p]: Using channel type", this));
+  } else if (((caps & eAllowPluginSkipChannel) || !newURI || usingClassID) &&
+             (GetTypeOfContent(newMime) == eType_Plugin)) {
+    newType = eType_Plugin;
+    LOG(("OBJLC [%p]: Skipping loading channel, type plugin", this));
+  } else if (newURI) {
+    // We could potentially load this if we opened a channel on mURI, indicate
+    // This by leaving type as loading
+    newType = eType_Loading;
+  } else {
+    // Unloadable - no URI, and no plugin type. Non-plugin types (images,
+    // documents) always load with a channel.
+    newType = eType_Null;
+  }
+
+  ///
+  /// Update changed values
+  ///
+
+  if (newType != mType) {
+    retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
+    LOG(("OBJLC [%p]: Type changed from %u -> %u", this, mType, newType));
+    mType = newType;
+  }
+
+  if (!URIEquals(mBaseURI, newBaseURI)) {
+    if (isJava) {
+      // Java bases its class loading on the base URI, so we consider the state
+      // to have changed if this changes. If the object is using a relative URI,
+      // mURI will have changed below regardless
+      retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
+    }
+    LOG(("OBJLC [%p]: Object effective baseURI changed", this));
+    mBaseURI = newBaseURI;
+  }
+
+  if (!URIEquals(newURI, mURI)) {
+    retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
+    LOG(("OBJLC [%p]: Object effective URI changed", this));
+    mURI = newURI;
+  }
+
+  if (mContentType != newMime) {
+    retval = (ParameterUpdateFlags)(retval | eParamStateChanged);
+    LOG(("OBJLC [%p]: Object effective mime type changed (%s -> %s)", this, mContentType.get(), newMime.get()));
+    mContentType = newMime;
+  }
+
+  return retval;
+}
+
+// Only OnStartRequest should be passing the channel parameter
 nsresult
-nsObjectLoadingContent::LoadObject(const nsAString& aURI,
-                                   bool aNotify,
-                                   const nsCString& aTypeHint,
+nsObjectLoadingContent::LoadObject(bool aNotify,
                                    bool aForceLoad)
 {
-  LOG(("OBJLC [%p]: Loading object: URI string=<%s> notify=%i type=<%s> forceload=%i\n",
-       this, NS_ConvertUTF16toUTF8(aURI).get(), aNotify, aTypeHint.get(), aForceLoad));
+  return LoadObject(aNotify, aForceLoad, nullptr);
+}
 
-  // Avoid StringToURI in order to use the codebase attribute as base URI
-  nsCOMPtr<nsIContent> thisContent = 
+nsresult
+nsObjectLoadingContent::LoadObject(bool aNotify,
+                                   bool aForceLoad,
+                                   nsIRequest *aLoadingChannel)
+{
+  nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   NS_ASSERTION(thisContent, "must be a content");
-
   nsIDocument* doc = thisContent->OwnerDoc();
-  nsCOMPtr<nsIURI> baseURI;
-  GetObjectBaseURI(thisContent, getter_AddRefs(baseURI));
+  nsresult rv = NS_OK;
 
-  nsCOMPtr<nsIURI> uri;
-  nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
-                                            aURI, doc,
-                                            baseURI);
-  // If URI creation failed, fallback immediately - this only happens for
-  // malformed URIs
-  if (!uri) {
-    Fallback(aNotify);
+  // XXX(johns): In these cases, we refuse to touch our content and just
+  //   remain unloaded, as per legacy behavior. It would make more sense to
+  //   load fallback content initially and refuse to ever change state again.
+  if (doc->IsBeingUsedAsImage() || doc->IsLoadedAsData()) {
     return NS_OK;
   }
 
-  NS_TryToSetImmutable(uri);
+  // Sanity check
+  if (!thisContent->IsInDoc()) {
+    NS_NOTREACHED("LoadObject called while not bound to a document");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  LOG(("OBJLC [%p]: LoadObject called, notify %u, forceload %u, channel %p",
+       this, aNotify, aForceLoad, aLoadingChannel));
+
+  // We can't re-use an already open channel, but aForceLoad may make us try
+  // to load a plugin without any changes in channel state.
+  if (aForceLoad && mChannelLoaded) {
+    CloseChannel();
+    mChannelLoaded = false;
+  }
+
+  // Save these for NotifyStateChanged();
+  nsEventStates oldState = ObjectState();
+  ObjectType oldType = mType;
+
+  ParameterUpdateFlags stateChange = UpdateObjectParameters();
+
+  if (!stateChange && !aForceLoad) {
+    return NS_OK;
+  }
+
+  ///
+  /// State has changed, unload existing content and attempt to load new type
+  ///
+  LOG(("OBJLC [%p]: LoadObject - plugin state changed (%u)",
+       this, stateChange));
+
+  // Setup fallback info. We may also change type to fallback below in case of
+  // sanity/OOM/etc. errors. We default to showing alternate content
+  // NOTE LoadFallback can override this in some cases
+  FallbackType fallbackType = eFallbackAlternate;
+
+  if (mType == eType_Null) {
+    nsresult pluginsupport = IsPluginEnabledForType(mContentType);
+    if (pluginsupport == NS_ERROR_PLUGIN_DISABLED) {
+      fallbackType = eFallbackDisabled;
+    } else if (pluginsupport == NS_ERROR_PLUGIN_BLOCKLISTED) {
+      fallbackType = eFallbackBlocklisted;
+    } else {
+      fallbackType = eFallbackUnsupported;
+    }
+  }
+
+  // We synchronously start/stop plugin instances below, which may spin the
+  // event loop. Re-entering into the load is fine, but at that point the
+  // original load call needs to abort when unwinding
+  // NOTE this is located *after* the state change check, a subseqent load
+  //      with no subsequently changed state will be a no-op.
+  mIsLoading = true;
+  AutoSetLoadingToFalse reentryCheck(this);
+
+  // Unload existing content, keeping in mind stopping plugins might spin the
+  // event loop. Note that we check for still-open channels below
+  UnloadObject(false); // Don't reset state
+  if (!mIsLoading) {
+    // The event loop must've spun and re-entered into LoadObject, which
+    // finished the load
+    return NS_OK;
+  }
 
-  return LoadObject(uri, aNotify, aTypeHint, aForceLoad);
-}
+  if (stateChange & eParamChannelChanged) {
+    // If the channel params changed, throw away the channel, but unset
+    // mChannelLoaded so we'll still try to open a new one for this load if
+    // necessary
+    CloseChannel();
+    mChannelLoaded = false;
+  } else if (mType == eType_Null && mChannel) {
+    // If we opened a channel but then failed to find a loadable state, throw it
+    // away. mChannelLoaded will indicate that we tried to load a channel at one
+    // point so we wont recurse
+    CloseChannel();
+  } else if (mChannelLoaded && mChannel != aLoadingChannel) {
+    // The only time we should have a loaded channel with a changed state is
+    // when the channel has just opened -- in which case this call should
+    // have originated from OnStartRequest
+    NS_NOTREACHED("Loading with a channel, but state doesn't make sense");
+    return NS_OK;
+  }
+
+  //
+  // Security checks
+  //
+
+  // NOTE For eType_Loading we'll try all three types, as we want to go ahead
+  //      with the channel if it could be any acceptable type. This type is
+  //      passed to OpenChannel() as the LoadType. We pass through LoadObject
+  //      again once the channel is opened and we're actually loading, so if
+  //      the final URI doesn't pass the now-known type, we'll abort.
+  PRInt32 policyType;
+  if (mType != eType_Null) {
+    bool allowLoad = false;
+    PRInt16 contentPolicy = nsIContentPolicy::ACCEPT;
+    PRUint32 caps = GetCapabilities();
+    bool supportImage = caps & eSupportImages;
+    bool supportDoc = (caps & eSupportDocuments) || (caps & eSupportSVG);
+    bool supportPlugin = caps & eSupportPlugins;
+    if (mType == eType_Image || (mType == eType_Loading && supportImage)) {
+      policyType = nsIContentPolicy::TYPE_IMAGE;
+      allowLoad = CheckObjectURIs(&contentPolicy, policyType);
+    }
+    if (!allowLoad &&
+        (mType == eType_Document || (mType == eType_Loading && supportDoc))) {
+      contentPolicy = nsIContentPolicy::ACCEPT;
+      policyType = nsIContentPolicy::TYPE_SUBDOCUMENT;
+      allowLoad = CheckObjectURIs(&contentPolicy, policyType);
+    }
+    if (!allowLoad &&
+        (mType == eType_Plugin || (mType == eType_Loading && supportPlugin))) {
+      contentPolicy = nsIContentPolicy::ACCEPT;
+      policyType = nsIContentPolicy::TYPE_OBJECT;
+      allowLoad = CheckObjectURIs(&contentPolicy, policyType);
+    }
+
+    // Load denied, switch to fallback and set disabled/suppressed if applicable
+    if (!allowLoad) {
+      mType = eType_Null;
+      if (contentPolicy == nsIContentPolicy::REJECT_TYPE) {
+        fallbackType = eFallbackUserDisabled;
+      } else {
+        fallbackType = eFallbackSuppressed;
+      }
+    }
+  }
+
+  // If we're a plugin but shouldn't start yet, load fallback with
+  // reason click-to-play instead
+  FallbackType clickToPlayReason;
+  if (mType == eType_Plugin && !ShouldPlay(clickToPlayReason)) {
+    LOG(("OBJLC [%p]: Marking plugin as click-to-play", this));
+    mType = eType_Null;
+    fallbackType = clickToPlayReason;
+  }
+
+  // Sanity check: We shouldn't have any loaded resources, pending events, or
+  // a final listener at this point
+  if (mFrameLoader || mPendingInstantiateEvent || mInstanceOwner ||
+      mFinalListener)
+  {
+    NS_NOTREACHED("Trying to load new plugin with existing content");
+    rv = NS_ERROR_UNEXPECTED;
+    return NS_OK;
+  }
+
+  // More sanity-checking:
+  // If mChannel is set, mChannelLoaded should be set, and vice-versa
+  if (mType != eType_Null && !!mChannel != mChannelLoaded) {
+    NS_NOTREACHED("Trying to load with bad channel state");
+    rv = NS_ERROR_UNEXPECTED;
+    return NS_OK;
+  }
 
-void
-nsObjectLoadingContent::UpdateFallbackState(nsIContent* aContent,
-                                            AutoFallback& fallback,
-                                            const nsCString& aTypeHint)
-{
-  // Notify the UI and update the fallback state
-  PluginSupportState state = GetPluginSupportState(aContent, aTypeHint);
-  if (state != ePluginOtherState) {
-    fallback.SetPluginState(state);
-    FirePluginError(aContent, state);
+  ///
+  /// Attempt to load new type
+  ///
+  switch (mType) {
+    case eType_Image:
+      if (!mChannel) {
+        // We have a LoadImage() call, but UpdateObjectParameters requires a
+        // channel for images, so this is not a valid state.
+        NS_NOTREACHED("Attempting to load image without a channel?");
+        rv = NS_ERROR_UNEXPECTED;
+        break;
+      }
+      rv = LoadImageWithChannel(mChannel, getter_AddRefs(mFinalListener));
+      if (mFinalListener) {
+        // Note that LoadObject is called from mChannel's OnStartRequest
+        // when loading with a channel
+        mSrcStreamLoading = true;
+        rv = mFinalListener->OnStartRequest(mChannel, nullptr);
+        mSrcStreamLoading = false;
+      }
+    break;
+    case eType_Plugin:
+    {
+      if (mChannel) {
+        nsRefPtr<nsPluginHost> pluginHost =
+          already_AddRefed<nsPluginHost>(nsPluginHost::GetInst());
+        if (!pluginHost) {
+          NS_NOTREACHED("No pluginHost");
+          rv = NS_ERROR_UNEXPECTED;
+          break;
+        }
+
+        // Force a sync state change now, we need the frame created
+        NotifyStateChanged(oldType, oldState, true, aNotify);
+        oldType = mType;
+        oldState = ObjectState();
+
+        rv = pluginHost->NewEmbeddedPluginStreamListener(mURI, this, nullptr,
+                                                         getter_AddRefs(mFinalListener));
+        if (NS_SUCCEEDED(rv)) {
+          // Note that LoadObject is called from mChannel's OnStartRequest
+          // when loading with a channel
+
+          mSrcStreamLoading = true;
+          rv = mFinalListener->OnStartRequest(mChannel, nullptr);
+          mSrcStreamLoading = false;
+          if (NS_SUCCEEDED(rv)) {
+            NotifyContentObjectWrapper();
+          }
+        }
+      } else {
+        rv = AsyncStartPluginInstance();
+      }
+    }
+    break;
+    case eType_Document:
+    {
+      if (!mChannel) {
+        // We could mFrameLoader->LoadURI(mURI), but UpdateObjectParameters
+        // requires documents have a channel, so this is not a valid state.
+        NS_NOTREACHED("Attempting to load a document without a channel");
+        mType = eType_Null;
+        break;
+      }
+      if (!mFrameLoader) {
+        // Force a sync state change, we need the frame created
+        NotifyStateChanged(oldType, oldState, true, aNotify);
+        oldType = mType;
+        oldState = ObjectState();
+
+        mFrameLoader = nsFrameLoader::Create(thisContent->AsElement(),
+                                             mNetworkCreated);
+        if (!mFrameLoader) {
+          NS_NOTREACHED("nsFrameLoader::Create failed");
+          mType = eType_Null;
+          break;
+        }
+      }
+
+      rv = mFrameLoader->CheckForRecursiveLoad(mURI);
+      if (NS_FAILED(rv)) {
+        mType = eType_Null;
+        break;
+      }
+
+      // We're loading a document, so we have to set LOAD_DOCUMENT_URI
+      // (especially important for firing onload)
+      nsLoadFlags flags = 0;
+      mChannel->GetLoadFlags(&flags);
+      flags |= nsIChannel::LOAD_DOCUMENT_URI;
+      mChannel->SetLoadFlags(flags);
+
+      nsCOMPtr<nsIDocShell> docShell;
+      rv = mFrameLoader->GetDocShell(getter_AddRefs(docShell));
+      if (NS_FAILED(rv)) {
+        NS_NOTREACHED("Could not get DocShell from mFrameLoader?");
+        mType = eType_Null;
+        break;
+      }
+
+      nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(docShell));
+      NS_ASSERTION(req, "Docshell must be an ifreq");
+
+      nsCOMPtr<nsIURILoader>
+        uriLoader(do_GetService(NS_URI_LOADER_CONTRACTID, &rv));
+      if (NS_FAILED(rv)) {
+        NS_NOTREACHED("Failed to get uriLoader service");
+        mType = eType_Null;
+        break;
+      }
+      rv = uriLoader->OpenChannel(mChannel, nsIURILoader::DONT_RETARGET, req,
+                                  getter_AddRefs(mFinalListener));
+      if (NS_SUCCEEDED(rv)) {
+        // Note that LoadObject is called from mChannel's OnStartRequest
+        // when loading with a channel
+        mSrcStreamLoading = true;
+        rv = mFinalListener->OnStartRequest(mChannel, nullptr);
+        mSrcStreamLoading = false;
+      }
+    }
+    break;
+    case eType_Loading:
+      // If our type remains Loading, we need a channel to proceed
+      rv = OpenChannel(policyType);
+      if (NS_FAILED(rv)) {
+        LOG(("OBJLC [%p]: OpenChannel returned failure (%u)", this, rv));
+      }
+    break;
+    case eType_Null:
+      // Handled below, silence compiler warnings
+    break;
+  };
+
+  if (NS_FAILED(rv)) {
+    // If we failed in the loading hunk above, switch to fallback
+    LOG(("OBJLC [%p]: Loading failed, switching to fallback", this));
+    mType = eType_Null;
   }
+
+  // Switching to fallback state
+  if (mType == eType_Null) {
+    LOG(("OBJLC [%p]: Loading fallback, type %u", this, fallbackType));
+    NS_ASSERTION(!mFrameLoader && !mInstanceOwner,
+                 "switched to type null but also loaded something");
+
+    if (mChannel) {
+      // If we were loading with a channel but then failed over, throw it away
+      // (this also closes mFinalListener)
+      CloseChannel();
+    }
+
+    // Don't notify or send events - we'll handle those ourselves
+    // (so really this is just setting mFallbackType)
+    LoadFallback(fallbackType, false);
+  }
+
+  // Notify of our final state if we haven't already
+  NotifyStateChanged(oldType, oldState, false, aNotify);
+
+  if (mType == eType_Null && mFallbackType != eFallbackAlternate) {
+    // if we're not showing alternate content, fire a pluginerror to trigger
+    // (we stopped LoadFallback from doing so above, it doesn't know of our old
+    //  state)
+    FirePluginError(mFallbackType);
+  }
+
+  return NS_OK;
 }
 
 nsresult
-nsObjectLoadingContent::LoadObject(nsIURI* aURI,
-                                   bool aNotify,
-                                   const nsCString& aTypeHint,
-                                   bool aForceLoad)
+nsObjectLoadingContent::CloseChannel()
 {
-  // Only do a URI equality check for things that aren't stopped plugins.
-  // This is because we still need to load again if the plugin has been stopped.
-  if (mType == eType_Document || mType == eType_Image || mInstanceOwner) {
-    if (mURI && aURI) {
-      bool equal;
-      nsresult rv = mURI->Equals(aURI, &equal);
-      if (NS_SUCCEEDED(rv) && equal && !aForceLoad) {
-        // URI didn't change, do nothing
-        return NS_OK;
-      }
-      StopPluginInstance();
-    }
-  }
-
-  // Need to revoke any potentially pending instantiate events
-  if (mType == eType_Plugin && mPendingInstantiateEvent) {
-    mPendingInstantiateEvent = nullptr;
-  }
-
-  AutoNotifier notifier(this, aNotify);
-
-  mUserDisabled = mSuppressed = false;
-
-  mURI = aURI;
-  mContentType = aTypeHint;
-
-  nsCOMPtr<nsIContent> thisContent = 
-    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
-  NS_ASSERTION(thisContent, "must be a content");
-
-  nsIDocument* doc = thisContent->OwnerDoc();
-  if (doc->IsBeingUsedAsImage()) {
-    return NS_OK;
-  }
-
-  // From here on, we will always change the content. This means that a
-  // possibly-loading channel should be aborted.
   if (mChannel) {
-    LOG(("OBJLC [%p]: Cancelling existing load\n", this));
-
+    LOG(("OBJLC [%p]: Closing channel\n", this));
     // These three statements are carefully ordered:
     // - onStopRequest should get a channel whose status is the same as the
     //   status argument
     // - onStopRequest must get a non-null channel
     mChannel->Cancel(NS_BINDING_ABORTED);
     if (mFinalListener) {
-      // NOTE: Since mFinalListener is only set in onStartRequest, which takes
-      // care of calling mFinalListener->OnStartRequest, mFinalListener is only
-      // non-null here if onStartRequest was already called.
+      // NOTE mFinalListener is only created when we load with a channel, which
+      //      LoadObject() requires come from a OnStartRequest call
       mFinalListener->OnStopRequest(mChannel, nullptr, NS_BINDING_ABORTED);
       mFinalListener = nullptr;
     }
     mChannel = nullptr;
   }
-
-  // Security checks
-  if (doc->IsLoadedAsData()) {
-    if (!doc->IsStaticDocument()) {
-      Fallback(false);
-    }
-    return NS_OK;
-  }
-
-  // Can't do security checks without a URI - hopefully the plugin will take
-  // care of that
-  // Null URIs happen when the URL to load is specified via other means than the
-  // data/src attribute, for example via custom <param> elements.
-  if (aURI) {
-    nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
-    NS_ASSERTION(secMan, "No security manager!?");
-    nsresult rv =
-      secMan->CheckLoadURIWithPrincipal(thisContent->NodePrincipal(), aURI, 0);
-    if (NS_FAILED(rv)) {
-      Fallback(false);
-      return NS_OK;
-    }
-
-    PRInt16 shouldLoad = nsIContentPolicy::ACCEPT; // default permit
-    rv =
-      NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OBJECT,
-                                aURI,
-                                doc->NodePrincipal(),
-                                static_cast<nsIImageLoadingContent*>(this),
-                                aTypeHint,
-                                nullptr, //extra
-                                &shouldLoad,
-                                nsContentUtils::GetContentPolicy(),
-                                secMan);
-    if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
-      HandleBeingBlockedByContentPolicy(rv, shouldLoad);
-      return NS_OK;
-    }
-  }
-
-  nsresult rv = NS_ERROR_UNEXPECTED;
-  // This fallback variable MUST be declared after the notifier variable. Do NOT
-  // change the order of the declarations!
-  AutoFallback fallback(this, &rv);
-
-  PRUint32 caps = GetCapabilities();
-  LOG(("OBJLC [%p]: Capabilities: %04x\n", this, caps));
-
-  nsCAutoString overrideType;
-  if ((caps & eOverrideServerType) &&
-      ((!aTypeHint.IsEmpty() && NS_SUCCEEDED(IsPluginEnabledForType(aTypeHint))) ||
-       (aURI && IsPluginEnabledByExtension(aURI, overrideType)))) {
-    ObjectType newType;
-    if (overrideType.IsEmpty()) {
-      newType = GetTypeOfContent(aTypeHint);
-    } else {
-      mContentType = overrideType;
-      newType = eType_Plugin;
-    }
-
-    if (newType != mType) {
-      LOG(("OBJLC [%p]: (eOverrideServerType) Changing type from %u to %u\n", this, mType, newType));
-
-      UnloadContent();
-
-      // Must have a frameloader before creating a frame, or the frame will
-      // create its own.
-      if (!mFrameLoader && newType == eType_Document) {
-        mFrameLoader = nsFrameLoader::Create(thisContent->AsElement(),
-                                             mNetworkCreated);
-        if (!mFrameLoader) {
-          mURI = nullptr;
-          return NS_OK;
-        }
-      }
+  return NS_OK;
+}
 
-      // Must notify here for plugins
-      // If aNotify is false, we'll just wait until we get a frame and use the
-      // async instantiate path.
-      // XXX is this still needed? (for documents?)
-      mType = newType;
-      if (aNotify)
-        notifier.Notify();
-    }
-    switch (newType) {
-      case eType_Image:
-        // Don't notify, because we will take care of that ourselves.
-        if (aURI) {
-          rv = LoadImage(aURI, aForceLoad, false);
-        } else {
-          rv = NS_ERROR_NOT_AVAILABLE;
-        }
-        break;
-      case eType_Plugin:
-        rv = AsyncStartPluginInstance();
-        break;
-      case eType_Document:
-        if (aURI) {
-          rv = mFrameLoader->LoadURI(aURI);
-        } else {
-          rv = NS_ERROR_NOT_AVAILABLE;
-        }
-        break;
-      case eType_Loading:
-        NS_NOTREACHED("Should not have a loading type here!");
-      case eType_Null:
-        // No need to load anything, notify of the failure.
-        UpdateFallbackState(thisContent, fallback, aTypeHint);
-        break;
-    };
-    return NS_OK;
-  }
-
-  // If the class ID specifies a supported plugin, or if we have no explicit URI
-  // but a type, immediately instantiate the plugin.
-  bool isSupportedClassID = false;
-  nsCAutoString typeForID; // Will be set iff isSupportedClassID == true
-  bool hasID = false;
-  if (caps & eSupportClassID) {
-    nsAutoString classid;
-    thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::classid, classid);
-    if (!classid.IsEmpty()) {
-      hasID = true;
-      isSupportedClassID = NS_SUCCEEDED(TypeForClassID(classid, typeForID));
-    }
-  }
+nsresult
+nsObjectLoadingContent::OpenChannel(PRInt32 aPolicyType)
+{
+  nsCOMPtr<nsIContent> thisContent = 
+    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  NS_ASSERTION(thisContent, "must be a content");
+  nsIDocument* doc = thisContent->OwnerDoc();
+  NS_ASSERTION(doc, "No owner document?");
+  NS_ASSERTION(!mInstanceOwner && !mInstantiating,
+               "opening a new channel with already loaded content");
 
-  if (hasID && !isSupportedClassID) {
-    // We have a class ID and it's unsupported.  Fallback in that case.
-    rv = NS_ERROR_NOT_AVAILABLE;
-    return NS_OK;
-  }
-
-  if (isSupportedClassID ||
-      (!aURI && !aTypeHint.IsEmpty() &&
-       GetTypeOfContent(aTypeHint) == eType_Plugin)) {
-    // No URI, but we have a type. The plugin will handle the load.
-    // Or: supported class id, plugin will handle the load.
-    mType = eType_Plugin;
-
-    // At this point, the stored content type
-    // must be equal to our type hint. Similar,
-    // our URI must be the requested URI.
-    // (->Equals would suffice, but == is cheaper
-    // and handles NULL)
-    NS_ASSERTION(mContentType.Equals(aTypeHint), "mContentType wrong!");
-    NS_ASSERTION(mURI == aURI, "mURI wrong!");
-
-    if (isSupportedClassID) {
-      // Use the classid's type
-      NS_ASSERTION(!typeForID.IsEmpty(), "Must have a real type!");
-      mContentType = typeForID;
-      // XXX(biesi). The plugin instantiation code used to pass the base URI
-      // here instead of the plugin URI for instantiation via class ID, so I
-      // continue to do so. Why that is, no idea...
-      GetObjectBaseURI(thisContent, getter_AddRefs(mURI));
-      if (!mURI) {
-        mURI = aURI;
-      }
-    }
-
-    // rv is references by a stack-based object, need to assign here
-    rv = AsyncStartPluginInstance();
-
-    return rv;
-  }
-
-  if (!aURI) {
-    // No URI and if we have got this far no enabled plugin supports the type
-    rv = NS_ERROR_NOT_AVAILABLE;
-
-    // We should only notify the UI if there is at least a type to go on for
-    // finding a plugin to use, unless it's a supported image or document type.
-    if (!aTypeHint.IsEmpty() && GetTypeOfContent(aTypeHint) == eType_Null) {
-      UpdateFallbackState(thisContent, fallback, aTypeHint);
-    }
-
-    return NS_OK;
-  }
+  nsresult rv;
+  mChannel = nullptr;
 
   // E.g. mms://
-  if (!CanHandleURI(aURI)) {
-    if (aTypeHint.IsEmpty()) {
-      rv = NS_ERROR_NOT_AVAILABLE;
-      return NS_OK;
-    }
-
-    if (NS_SUCCEEDED(IsPluginEnabledForType(aTypeHint))) {
-      mType = eType_Plugin;
-    } else {
-      rv = NS_ERROR_NOT_AVAILABLE;
-      // No plugin to load, notify of the failure.
-      UpdateFallbackState(thisContent, fallback, aTypeHint);
-    }
-
-    return NS_OK;
+  if (!mURI || !CanHandleURI(mURI)) {
+    return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsCOMPtr<nsILoadGroup> group = doc->GetDocumentLoadGroup();
   nsCOMPtr<nsIChannel> chan;
   nsCOMPtr<nsIChannelPolicy> channelPolicy;
   nsCOMPtr<nsIContentSecurityPolicy> csp;
   rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
   NS_ENSURE_SUCCESS(rv, rv);
   if (csp) {
     channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
     channelPolicy->SetContentSecurityPolicy(csp);
-    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_OBJECT);
+    channelPolicy->SetLoadType(aPolicyType);
   }
-  rv = NS_NewChannel(getter_AddRefs(chan), aURI, nullptr, group, this,
+  rv = NS_NewChannel(getter_AddRefs(chan), mURI, nullptr, group, this,
                      nsIChannel::LOAD_CALL_CONTENT_SNIFFERS |
                      nsIChannel::LOAD_CLASSIFY_URI,
                      channelPolicy);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Referrer
   nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
   if (httpChan) {
     httpChan->SetReferrer(doc->GetDocumentURI());
   }
 
-  // MIME Type hint
-  if (!aTypeHint.IsEmpty()) {
-    nsCAutoString typeHint, dummy;
-    NS_ParseContentType(aTypeHint, typeHint, dummy);
-    if (!typeHint.IsEmpty()) {
-      chan->SetContentType(typeHint);
-    }
-  }
-
   // Set up the channel's principal and such, like nsDocShell::DoURILoad does
   nsContentUtils::SetUpChannelOwner(thisContent->NodePrincipal(),
-                                    chan, aURI, true);
+                                    chan, mURI, true);
 
   nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(chan);
   if (scriptChannel) {
     // Allow execution against our context if the principals match
-    scriptChannel->
-      SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
+    scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
   }
 
   // AsyncOpen can fail if a file does not exist.
-  // Show fallback content in that case.
   rv = chan->AsyncOpen(this, nullptr);
-  if (NS_SUCCEEDED(rv)) {
-    LOG(("OBJLC [%p]: Channel opened.\n", this));
-
-    mChannel = chan;
-    mType = eType_Loading;
-  }
+  NS_ENSURE_SUCCESS(rv, rv);
+  LOG(("OBJLC [%p]: Channel opened", this));
+  mChannel = chan;
   return NS_OK;
 }
 
 PRUint32
 nsObjectLoadingContent::GetCapabilities() const
 {
   return eSupportImages |
          eSupportPlugins |
          eSupportDocuments |
          eSupportSVG;
 }
 
 void
-nsObjectLoadingContent::Fallback(bool aNotify)
-{
-  AutoNotifier notifier(this, aNotify);
-
-  UnloadContent();
-}
-
-void
-nsObjectLoadingContent::RemovedFromDocument()
+nsObjectLoadingContent::DestroyContent()
 {
   if (mFrameLoader) {
-    // XXX This is very temporary and must go away
     mFrameLoader->Destroy();
     mFrameLoader = nullptr;
-
-    // Clear the current URI, so that LoadObject doesn't think that we
-    // have already loaded the content.
-    mURI = nullptr;
   }
 
-  // When a plugin instance node is removed from the document we'll
-  // let the plugin continue to run at least until we get back to
-  // the event loop. If we get back to the event loop and the node
-  // has still not been added back to the document then we stop
-  // the plugin.
-  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
-  nsCOMPtr<nsIRunnable> event = new InDocCheckEvent(thisContent);
-
-  nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
-  if (appShell) {
-    appShell->RunInStableState(event);
-  }
+  StopPluginInstance();
 }
 
 /* static */
 void
 nsObjectLoadingContent::Traverse(nsObjectLoadingContent *tmp,
                                  nsCycleCollectionTraversalCallback &cb)
 {
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameLoader");
   cb.NoteXPCOMChild(static_cast<nsIFrameLoader*>(tmp->mFrameLoader));
 }
 
-// <private>
-/* static */ bool
-nsObjectLoadingContent::IsSuccessfulRequest(nsIRequest* aRequest)
-{
-  nsresult status;
-  nsresult rv = aRequest->GetStatus(&status);
-  if (NS_FAILED(rv) || NS_FAILED(status)) {
-    return false;
-  }
-
-  // This may still be an error page or somesuch
-  nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(aRequest));
-  if (httpChan) {
-    bool success;
-    rv = httpChan->GetRequestSucceeded(&success);
-    if (NS_FAILED(rv) || !success) {
-      return false;
-    }
-  }
-
-  // Otherwise, the request is successful
-  return true;
-}
-
-/* static */ bool
-nsObjectLoadingContent::CanHandleURI(nsIURI* aURI)
+void
+nsObjectLoadingContent::UnloadObject(bool aResetState)
 {
-  nsCAutoString scheme;
-  if (NS_FAILED(aURI->GetScheme(scheme))) {
-    return false;
-  }
-
-  nsIIOService* ios = nsContentUtils::GetIOService();
-  if (!ios)
-    return false;
-  
-  nsCOMPtr<nsIProtocolHandler> handler;
-  ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
-  if (!handler) {
-    return false;
-  }
-  
-  nsCOMPtr<nsIExternalProtocolHandler> extHandler =
-    do_QueryInterface(handler);
-  // We can handle this URI if its protocol handler is not the external one
-  return extHandler == nullptr;
-}
-
-bool
-nsObjectLoadingContent::IsSupportedDocument(const nsCString& aMimeType)
-{
-  nsCOMPtr<nsIContent> thisContent = 
-    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
-  NS_ASSERTION(thisContent, "must be a content");
-
-  nsresult rv;
-  nsCOMPtr<nsIWebNavigationInfo> info(
-    do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID, &rv));
-  PRUint32 supported;
-  if (info) {
-    nsCOMPtr<nsIWebNavigation> webNav;
-    nsIDocument* currentDoc = thisContent->GetCurrentDoc();
-    if (currentDoc) {
-      webNav = do_GetInterface(currentDoc->GetScriptGlobalObject());
-    }
-    rv = info->IsTypeSupported(aMimeType, webNav, &supported);
-  }
-
-  if (NS_SUCCEEDED(rv)) {
-    if (supported == nsIWebNavigationInfo::UNSUPPORTED) {
-      // Try a stream converter
-      // NOTE: We treat any type we can convert from as a supported type. If a
-      // type is not actually supported, the URI loader will detect that and
-      // return an error, and we'll fallback.
-      nsCOMPtr<nsIStreamConverterService> convServ =
-        do_GetService("@mozilla.org/streamConverters;1");
-      bool canConvert = false;
-      if (convServ) {
-        rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert);
-      }
-
-      return NS_SUCCEEDED(rv) && canConvert;
-    }
-
-    // Don't want to support plugins as documents
-    return supported != nsIWebNavigationInfo::PLUGIN;
-  }
-
-  return false;
-}
-
-void
-nsObjectLoadingContent::UnloadContent()
-{
-  // Don't notify in CancelImageRequests. We do it ourselves.
+  // Don't notify in CancelImageRequests until we transition to a new loaded
+  // state
   CancelImageRequests(false);
   if (mFrameLoader) {
     mFrameLoader->Destroy();
     mFrameLoader = nullptr;
   }
-  mType = eType_Null;
-  mUserDisabled = mSuppressed = false;
-  mFallbackReason = ePluginOtherState;
+
+  if (aResetState) {
+    CloseChannel();
+    mType = eType_Loading;
+  }
+
+  // This call should be last as it may re-enter
+  StopPluginInstance();
 }
 
 void
 nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType,
                                            nsEventStates aOldState,
                                            bool aSync,
                                            bool aNotify)
 {
-  LOG(("OBJLC [%p]: Notifying about state change: (%u, %llx) -> (%u, %llx) (sync=%i)\n",
-       this, aOldType, aOldState.GetInternalValue(), mType,
-       ObjectState().GetInternalValue(), aSync));
+  LOG(("OBJLC [%p]: Notifying about state change: (%u, %llx) -> (%u, %llx)"
+       " (sync %i, notify %i)", this, aOldType, aOldState.GetInternalValue(),
+       mType, ObjectState().GetInternalValue(), aSync, aNotify));
 
   nsCOMPtr<nsIContent> thisContent = 
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   NS_ASSERTION(thisContent, "must be a content");
 
   NS_ASSERTION(thisContent->IsElement(), "Not an element?");
 
+  // XXX(johns): A good bit of the code below replicates UpdateState(true)
+
   // Unfortunately, we do some state changes without notifying
   // (e.g. in Fallback when canceling image requests), so we have to
   // manually notify object state changes.
   thisContent->AsElement()->UpdateState(false);
 
   if (!aNotify) {
     // We're done here
     return;
@@ -1813,43 +1986,50 @@ nsObjectLoadingContent::NotifyStateChang
     // Otherwise, need to do that here
     nsCOMPtr<nsIPresShell> shell = doc->GetShell();
     if (shell) {
       shell->RecreateFramesFor(thisContent);
     }
   }
 }
 
-/* static */ void
-nsObjectLoadingContent::FirePluginError(nsIContent* thisContent,
-                                        PluginSupportState state)
+void
+nsObjectLoadingContent::FirePluginError(FallbackType aFallbackType)
 {
-  LOG(("OBJLC []: Dispatching nsPluginErrorEvent for content %p\n",
-       thisContent));
+  nsCOMPtr<nsIContent> thisContent = 
+    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  NS_ASSERTION(thisContent, "must be a content");
 
-  nsCOMPtr<nsIRunnable> ev = new nsPluginErrorEvent(thisContent, state);
+  LOG(("OBJLC [%p]: Dispatching nsPluginErrorEvent for content %p\n",
+       this));
+
+  nsCOMPtr<nsIRunnable> ev = new nsPluginErrorEvent(thisContent, aFallbackType);
   nsresult rv = NS_DispatchToCurrentThread(ev);
   if (NS_FAILED(rv)) {
     NS_WARNING("failed to dispatch nsPluginErrorEvent");
   }
 }
 
 nsObjectLoadingContent::ObjectType
 nsObjectLoadingContent::GetTypeOfContent(const nsCString& aMIMEType)
 {
+  if (aMIMEType.IsEmpty()) {
+    return eType_Null;
+  }
+
   PRUint32 caps = GetCapabilities();
 
   if ((caps & eSupportImages) && IsSupportedImage(aMIMEType)) {
     return eType_Image;
   }
 
+  // SVGs load as documents, but are their own capability
   bool isSVG = aMIMEType.LowerCaseEqualsLiteral("image/svg+xml");
-  bool supportedSVG = isSVG && (caps & eSupportSVG);
-  if (((caps & eSupportDocuments) || supportedSVG) &&
-      IsSupportedDocument(aMIMEType)) {
+  bool supportType = isSVG ? eSupportSVG : eSupportDocuments;
+  if ((caps & supportType) && IsSupportedDocument(aMIMEType)) {
     return eType_Document;
   }
 
   if ((caps & eSupportPlugins) && NS_SUCCEEDED(IsPluginEnabledForType(aMIMEType))) {
     return eType_Plugin;
   }
 
   return eType_Null;
@@ -1878,135 +2058,26 @@ nsObjectLoadingContent::TypeForClassID(c
       aType.AssignLiteral("application/oleobject");
       return NS_OK;
     }
   }
 
   return NS_ERROR_NOT_AVAILABLE;
 }
 
-void
-nsObjectLoadingContent::GetObjectBaseURI(nsIContent* thisContent, nsIURI** aURI)
-{
-  // We want to use swap(); since this is just called from this file,
-  // we can assert this (callers use comptrs)
-  NS_PRECONDITION(*aURI == nullptr, "URI must be inited to zero");
-
-  // For plugins, the codebase attribute is the base URI
-  nsCOMPtr<nsIURI> baseURI = thisContent->GetBaseURI();
-  nsAutoString codebase;
-  thisContent->GetAttr(kNameSpaceID_None, nsGkAtoms::codebase,
-                       codebase);
-  if (!codebase.IsEmpty()) {
-    nsContentUtils::NewURIWithDocumentCharset(aURI, codebase,
-                                              thisContent->OwnerDoc(),
-                                              baseURI);
-  } else {
-    baseURI.swap(*aURI);
-  }
-}
-
 nsObjectFrame*
 nsObjectLoadingContent::GetExistingFrame()
 {
   nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   nsIFrame* frame = thisContent->GetPrimaryFrame();
   nsIObjectFrame* objFrame = do_QueryFrame(frame);
   return static_cast<nsObjectFrame*>(objFrame);
 }
 
 void
-nsObjectLoadingContent::HandleBeingBlockedByContentPolicy(nsresult aStatus,
-                                                          PRInt16 aRetval)
-{
-  // Must call UnloadContent first, as it overwrites
-  // mSuppressed/mUserDisabled. It also takes care of setting the type to
-  // eType_Null.
-  UnloadContent();
-  if (NS_SUCCEEDED(aStatus)) {
-    if (aRetval == nsIContentPolicy::REJECT_TYPE) {
-      mUserDisabled = true;
-    } else if (aRetval == nsIContentPolicy::REJECT_SERVER) {
-      mSuppressed = true;
-    }
-  }
-}
-
-PluginSupportState
-nsObjectLoadingContent::GetPluginSupportState(nsIContent* aContent,
-                                              const nsCString& aContentType)
-{
-  if (!aContent->IsHTML()) {
-    return ePluginOtherState;
-  }
-
-  if (aContent->Tag() == nsGkAtoms::embed ||
-      aContent->Tag() == nsGkAtoms::applet) {
-    return GetPluginDisabledState(aContentType);
-  }
-
-  bool hasAlternateContent = false;
-
-  // Search for a child <param> with a pluginurl name
-  for (nsIContent* child = aContent->GetFirstChild();
-       child;
-       child = child->GetNextSibling()) {
-    if (child->IsHTML(nsGkAtoms::param)) {
-      if (child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
-                             NS_LITERAL_STRING("pluginurl"), eIgnoreCase)) {
-        return GetPluginDisabledState(aContentType);
-      }
-    } else if (!hasAlternateContent) {
-      hasAlternateContent =
-        nsStyleUtil::IsSignificantChild(child, true, false);
-    }
-  }
-
-  PluginSupportState pluginDisabledState = GetPluginDisabledState(aContentType);
-  if (pluginDisabledState == ePluginClickToPlay ||
-      pluginDisabledState == ePluginVulnerableUpdatable ||
-      pluginDisabledState == ePluginVulnerableNoUpdate) {
-    return pluginDisabledState;
-  } else if (hasAlternateContent) {
-    return ePluginOtherState;
-  } else {
-    return pluginDisabledState;
-  }
-}
-
-PluginSupportState
-nsObjectLoadingContent::GetPluginDisabledState(const nsCString& aContentType)
-{
-  nsresult rv = IsPluginEnabledForType(aContentType);
-  if (rv == NS_ERROR_PLUGIN_DISABLED) {
-    return ePluginDisabled;
-  }
-  if (rv == NS_ERROR_PLUGIN_CLICKTOPLAY) {
-    PRUint32 state;
-    nsCOMPtr<nsIPluginHost> pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID));
-    nsPluginHost *pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
-    if (pluginHost) {
-      rv = pluginHost->GetBlocklistStateForType(aContentType.get(), &state);
-      if (NS_SUCCEEDED(rv)) {
-        if (state == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE) {
-          return ePluginVulnerableUpdatable;
-        } else if (state == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
-          return ePluginVulnerableNoUpdate;
-        }
-      }
-    }
-    return ePluginClickToPlay;
-  }
-  if (rv == NS_ERROR_PLUGIN_BLOCKLISTED) {
-    return ePluginBlocklisted;
-  }
-  return ePluginUnsupported;
-}
-
-void
 nsObjectLoadingContent::CreateStaticClone(nsObjectLoadingContent* aDest) const
 {
   nsImageLoadingContent::CreateStaticImageClone(aDest);
 
   aDest->mType = mType;
   nsObjectLoadingContent* thisObj = const_cast<nsObjectLoadingContent*>(this);
   if (thisObj->mPrintFrame.IsAlive()) {
     aDest->mPrintFrame = thisObj->mPrintFrame;
@@ -2033,34 +2104,44 @@ nsObjectLoadingContent::GetPrintFrame(ns
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::PluginCrashed(nsIPluginTag* aPluginTag,
                                       const nsAString& pluginDumpID,
                                       const nsAString& browserDumpID,
                                       bool submittedCrashReport)
 {
-  AutoNotifier notifier(this, true);
-  UnloadContent();
-  mFallbackReason = ePluginCrashed;
-  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  LOG(("OBJLC [%p]: Plugin Crashed, queuing crash event", this));
+  NS_ASSERTION(mType == eType_Plugin, "PluginCrashed at non-plugin type");
+
+  // Instance is dead, clean up
+  mInstanceOwner = nullptr;
+  CloseChannel();
+
+  // Switch to fallback/crashed state, notify
+  LoadFallback(eFallbackCrashed, true);
+
+  // send nsPluginCrashedEvent
+  nsCOMPtr<nsIContent> thisContent =
+    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
 
   // Note that aPluginTag in invalidated after we're called, so copy 
   // out any data we need now.
   nsCAutoString pluginName;
   aPluginTag->GetName(pluginName);
   nsCAutoString pluginFilename;
   aPluginTag->GetFilename(pluginFilename);
 
-  nsCOMPtr<nsIRunnable> ev = new nsPluginCrashedEvent(thisContent,
-                                                      pluginDumpID,
-                                                      browserDumpID,
-                                                      NS_ConvertUTF8toUTF16(pluginName),
-                                                      NS_ConvertUTF8toUTF16(pluginFilename),
-                                                      submittedCrashReport);
+  nsCOMPtr<nsIRunnable> ev =
+    new nsPluginCrashedEvent(thisContent,
+                             pluginDumpID,
+                             browserDumpID,
+                             NS_ConvertUTF8toUTF16(pluginName),
+                             NS_ConvertUTF8toUTF16(pluginFilename),
+                             submittedCrashReport);
   nsresult rv = NS_DispatchToCurrentThread(ev);
   if (NS_FAILED(rv)) {
     NS_WARNING("failed to dispatch nsPluginCrashedEvent");
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -2074,17 +2155,17 @@ nsObjectLoadingContent::SyncStartPluginI
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   if (!thisContent->IsInDoc()) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIURI> kungFuURIGrip(mURI);
   nsCString contentType(mContentType);
-  return InstantiatePluginInstance(contentType.get(), mURI.get());
+  return InstantiatePluginInstance();
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::AsyncStartPluginInstance()
 {
   // OK to have an instance already.
   if (mInstanceOwner) {
     return NS_OK;
@@ -2124,17 +2205,17 @@ DoDelayedStop(nsPluginInstanceOwner* aIn
               nsObjectLoadingContent* aContent,
               bool aDelayedStop)
 {
 #if (MOZ_PLATFORM_MAEMO==5)
   // Don't delay stop on Maemo/Hildon (bug 530739).
   if (aDelayedStop && aInstanceOwner->MatchPluginName("Shockwave Flash"))
     return false;
 #endif
-  
+
   // Don't delay stopping QuickTime (bug 425157), Flip4Mac (bug 426524),
   // XStandard (bug 430219), CMISS Zinc (bug 429604).
   if (aDelayedStop
 #if !(defined XP_WIN || defined MOZ_X11)
       && !aInstanceOwner->MatchPluginName("QuickTime")
       && !aInstanceOwner->MatchPluginName("Flip4Mac")
       && !aInstanceOwner->MatchPluginName("XStandard plugin")
       && !aInstanceOwner->MatchPluginName("CMISS Zinc Plugin")
@@ -2144,23 +2225,98 @@ DoDelayedStop(nsPluginInstanceOwner* aIn
       new nsStopPluginRunnable(aInstanceOwner, aContent);
     NS_DispatchToCurrentThread(evt);
     return true;
   }
   return false;
 }
 
 void
+nsObjectLoadingContent::LoadFallback(FallbackType aType, bool aNotify) {
+  nsEventStates oldState = ObjectState();
+  ObjectType oldType = mType;
+
+  NS_ASSERTION(!mInstanceOwner && !mFrameLoader && !mChannel,
+               "LoadFallback called with loaded content");
+
+  //
+  // Fixup mFallbackType
+  //
+  nsCOMPtr<nsIContent> thisContent =
+  do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  NS_ASSERTION(thisContent, "must be a content");
+
+  if (!thisContent->IsHTML()) {
+    // Don't let custom fallback handlers run outside HTML
+    LOG(("OBJLC [%p]: Non-HTML content, forcing eFallbackAlternate", this));
+    aType = eFallbackAlternate;
+  }
+
+  /// XXX(johns): This block is just mimicing legacy behavior, not any spec
+  // Check if we have any significant content (excluding param tags) OR a
+  // param named 'pluginUrl'
+  bool hasAlternateContent = false;
+  bool hasPluginUrl = false;
+  if (thisContent->Tag() == nsGkAtoms::object &&
+      (aType == eFallbackUnsupported ||
+       aType == eFallbackDisabled ||
+       aType == eFallbackBlocklisted))
+  {
+    for (nsIContent* child = thisContent->GetFirstChild();
+         child; child = child->GetNextSibling())
+    {
+      if (child->IsHTML(nsGkAtoms::param)) {
+        if (child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
+          NS_LITERAL_STRING("pluginurl"), eIgnoreCase)) {
+          hasPluginUrl = true;
+        }
+      } else if (nsStyleUtil::IsSignificantChild(child, true, false)) {
+        hasAlternateContent = true;
+      }
+    }
+
+    // Show alternate content if it exists, unless we have a 'pluginurl' param,
+    // in which case the missing-plugin fallback handler will want to handle
+    // it
+    if (hasAlternateContent && !hasPluginUrl) {
+      LOG(("OBJLC [%p]: Unsupported/disabled/blocked plugin has alternate "
+      "content, showing instead of custom handler", this));
+      aType = eFallbackAlternate;
+    }
+  }
+
+  mType = eType_Null;
+  mFallbackType = aType;
+
+  //
+  // Notify & send events
+  //
+  if (!aNotify) {
+    return; // done
+  }
+
+  NotifyStateChanged(oldType, oldState, false, true);
+
+  if (mFallbackType != eFallbackCrashed &&
+      mFallbackType != eFallbackAlternate)
+  {
+    // Alternate content doesn't trigger a pluginError, and nsPluginCrashedEvent
+    // is only handled by ::PluginCrashed
+    FirePluginError(mFallbackType);
+  }
+}
+
+void
 nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner,
                                      bool aDelayedStop,
                                      bool aForcedReentry)
 {
   // DoStopPlugin can process events and there may be pending InDocCheckEvent
   // events which can drop in underneath us and destroy the instance we are
-  // about to destroy unless we prevent that with the mIsStopping flag.
+  // about to destroy unless we prevent that with the mPluginStopping flag.
   // (aForcedReentry is only true from the callback of an earlier delayed stop)
   if (mIsStopping && !aForcedReentry) {
     return;
   }
   mIsStopping = true;
 
   nsRefPtr<nsPluginInstanceOwner> kungFuDeathGrip(aInstanceOwner);
   nsRefPtr<nsNPAPIPluginInstance> inst;
@@ -2169,103 +2325,194 @@ nsObjectLoadingContent::DoStopPlugin(nsP
     if (DoDelayedStop(aInstanceOwner, this, aDelayedStop)) {
       return;
     }
 
 #if defined(XP_MACOSX)
     aInstanceOwner->HidePluginWindow();
 #endif
 
-    nsCOMPtr<nsIPluginHost> pluginHost = do_GetService(MOZ_PLUGIN_HOST_CONTRACTID);
-    NS_ASSERTION(pluginHost, "Without a pluginHost, how can we have an instance to destroy?");
-    static_cast<nsPluginHost*>(pluginHost.get())->StopPluginInstance(inst);
+    nsRefPtr<nsPluginHost> pluginHost =
+      already_AddRefed<nsPluginHost>(nsPluginHost::GetInst());
+    NS_ASSERTION(pluginHost, "No plugin host?");
+    pluginHost->StopPluginInstance(inst);
   }
-  
+
   aInstanceOwner->Destroy();
-  mIsStopping = false;
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::StopPluginInstance()
 {
+  // Prevents any pending plugin starts from running
+  mPendingInstantiateEvent = nullptr;
+
   if (!mInstanceOwner) {
     return NS_OK;
   }
 
+  if (mChannel) {
+    // The plugin has already used data from this channel, we'll need to
+    // re-open it to handle instantiating again, even if we don't invalidate
+    // our loaded state.
+    /// XXX(johns): Except currently, we don't, just leaving re-opening channels
+    ///             to plugins...
+    LOG(("OBJLC [%p]: StopPluginInstance - Closing used channel", this));
+    CloseChannel();
+  }
+
   DisconnectFrame();
 
   bool delayedStop = false;
 #ifdef XP_WIN
   // Force delayed stop for Real plugin only; see bug 420886, 426852.
   nsRefPtr<nsNPAPIPluginInstance> inst;
   mInstanceOwner->GetInstance(getter_AddRefs(inst));
   if (inst) {
     const char* mime = nullptr;
     if (NS_SUCCEEDED(inst->GetMIMEType(&mime)) && mime) {
       if (strcmp(mime, "audio/x-pn-realaudio-plugin") == 0) {
         delayedStop = true;
-      }      
+      }
     }
   }
 #endif
 
   DoStopPlugin(mInstanceOwner, delayedStop);
 
   mInstanceOwner = nullptr;
 
   return NS_OK;
 }
 
 void
 nsObjectLoadingContent::NotifyContentObjectWrapper()
 {
-  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  nsCOMPtr<nsIContent> thisContent =
+    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
 
   nsCOMPtr<nsIDocument> doc = thisContent->GetDocument();
   if (!doc)
     return;
-  
+
   nsIScriptGlobalObject *sgo = doc->GetScopeObject();
   if (!sgo)
     return;
-  
+
   nsIScriptContext *scx = sgo->GetContext();
   if (!scx)
     return;
-  
+
   JSContext *cx = scx->GetNativeContext();
-  
+
   nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
   nsContentUtils::XPConnect()->
   GetWrappedNativeOfNativeObject(cx, sgo->GetGlobalJSObject(), thisContent,
                                  NS_GET_IID(nsISupports),
                                  getter_AddRefs(wrapper));
-  
+
   if (!wrapper) {
     // Nothing to do here if there's no wrapper for mContent. The proto
     // chain will be fixed appropriately when the wrapper is created.
     return;
   }
-  
+
   JSObject *obj = nullptr;
   nsresult rv = wrapper->GetJSObject(&obj);
   if (NS_FAILED(rv))
     return;
-  
+
   nsHTMLPluginObjElementSH::SetupProtoChain(wrapper, cx, obj);
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::PlayPlugin()
 {
   if (!nsContentUtils::IsCallerChrome())
     return NS_OK;
 
-  mCTPPlayable = true;
-  return LoadObject(mURI, true, mContentType, true);
+  mActivated = true;
+  return LoadObject(true, true);
 }
 
 NS_IMETHODIMP
-nsObjectLoadingContent::GetActivated(bool* aActivated)
+nsObjectLoadingContent::GetActivated(bool *aActivated)
 {
-  *aActivated = mActivated;
+  FallbackType reason;
+  *aActivated = ShouldPlay(reason);
   return NS_OK;
 }
+
+bool
+nsObjectLoadingContent::ShouldPlay(FallbackType &aReason)
+{
+  // mActivated is true if we've been activated via PlayPlugin() (e.g. user has
+  // clicked through). Otherwise, only play if click-to-play is off or if page
+  // is whitelisted
+
+  nsRefPtr<nsPluginHost> pluginHost =
+    already_AddRefed<nsPluginHost>(nsPluginHost::GetInst());
+
+  bool isCTP = pluginHost->IsPluginClickToPlayForType(mContentType.get());
+
+  if (!isCTP || mActivated) {
+    return true;
+  }
+
+  aReason = eFallbackClickToPlay;
+
+  // If plugin type is click-to-play and we have not been explicitly clicked.
+  // check if permissions lets this page bypass - (e.g. user selected 'Always
+  // play plugins on this page')
+  nsresult rv = NS_ERROR_UNEXPECTED;
+
+  nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
+  MOZ_ASSERT(thisContent);
+  nsIDocument* ownerDoc = thisContent->OwnerDoc();
+
+  nsCOMPtr<nsIDOMWindow> window = ownerDoc->GetWindow();
+  if (!window) {
+    return false;
+  }
+  nsCOMPtr<nsIDOMWindow> topWindow;
+  rv = window->GetTop(getter_AddRefs(topWindow));
+  NS_ENSURE_SUCCESS(rv, false);
+  nsCOMPtr<nsIDOMDocument> topDocument;
+  rv = topWindow->GetDocument(getter_AddRefs(topDocument));
+  NS_ENSURE_SUCCESS(rv, false);
+  nsCOMPtr<nsIDocument> topDoc = do_QueryInterface(topDocument);
+  nsIURI* topUri = topDoc->GetDocumentURI();
+
+  nsCOMPtr<nsIPermissionManager> permissionManager = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  bool allowPerm = false;
+  // For now we always say that the system principal uses click-to-play since
+  // that maintains current behavior and we have tests that expect this.
+  // What we really should do is disable plugins entirely in pages that use
+  // the system principal, i.e. in chrome pages. That way the click-to-play
+  // code here wouldn't matter at all. Bug 775301 is tracking this.
+  if (!nsContentUtils::IsSystemPrincipal(topDoc->NodePrincipal())) {
+    PRUint32 permission;
+    rv = permissionManager->TestPermissionFromPrincipal(topDoc->NodePrincipal(),
+                                                        "plugins",
+                                                        &permission);
+    NS_ENSURE_SUCCESS(rv, false);
+    allowPerm = permission == nsIPermissionManager::ALLOW_ACTION;
+  }
+
+  PRUint32 state;
+  rv = pluginHost->GetBlocklistStateForType(mContentType.get(), &state);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  // Always c2p vulnerable plugins, regardless of permissions
+  if (state == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE) {
+    aReason = eFallbackVulnerableUpdatable;
+    return false;
+  }
+  if (state == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
+    aReason = eFallbackVulnerableNoUpdate;
+    return false;
+  }
+
+  return allowPerm;
+}
+
--- a/content/base/src/nsObjectLoadingContent.h
+++ b/content/base/src/nsObjectLoadingContent.h
@@ -26,68 +26,60 @@
 
 class nsAsyncInstantiateEvent;
 class nsStopPluginRunnable;
 class AutoNotifier;
 class AutoFallback;
 class AutoSetInstantiatingToFalse;
 class nsObjectFrame;
 
-enum PluginSupportState {
-  ePluginUnsupported,  // The plugin is not supported (e.g. not installed)
-  ePluginDisabled,     // The plugin has been explicitly disabled by the user
-  ePluginBlocklisted,  // The plugin is blocklisted and disabled
-  ePluginOutdated,     // The plugin is considered outdated, but not disabled
-  ePluginOtherState,   // Something else (e.g. uninitialized or not a plugin)
-  ePluginCrashed,
-  ePluginClickToPlay,  // The plugin is disabled until the user clicks on it
-  ePluginVulnerableUpdatable, // The plugin is vulnerable (update available)
-  ePluginVulnerableNoUpdate   // The plugin is vulnerable (no update available)
-};
-
-/**
- * INVARIANTS OF THIS CLASS
- * - mChannel is non-null between asyncOpen and onStopRequest (NOTE: Only needs
- *   to be valid until onStopRequest is called on mFinalListener, not
- *   necessarily until the channel calls onStopRequest on us)
- * - mChannel corresponds to the channel that gets passed to the
- *   nsIRequestObserver/nsIStreamListener methods
- * - mChannel can be cancelled and ODA calls will stop
- * - mFinalListener is non-null (only) after onStartRequest has been called on
- *   it and before onStopRequest has been called on it
- *   (i.e. calling onStopRequest doesn't violate the nsIRequestObserver
- *   contract)
- * - mFrameLoader is null while this node is not in a document (XXX this
- *   invariant only exists due to nsFrameLoader suckage and needs to go away)
- * - mInstantiating is true while in LoadObject (it may be true in other
- *   cases as well). Only the function that set mInstantiating should trigger
- *   frame construction or notifications like ContentStatesChanged or flushes.
- */
 class nsObjectLoadingContent : public nsImageLoadingContent
                              , public nsIStreamListener
                              , public nsIFrameLoaderOwner
                              , public nsIObjectLoadingContent
                              , public nsIInterfaceRequestor
                              , public nsIChannelEventSink
 {
-  friend class AutoNotifier;
-  friend class AutoFallback;
   friend class AutoSetInstantiatingToFalse;
+  friend class AutoSetLoadingToFalse;
+  friend class InDocCheckEvent;
   friend class nsStopPluginRunnable;
   friend class nsAsyncInstantiateEvent;
 
   public:
     // This enum's values must be the same as the constants on
     // nsIObjectLoadingContent
     enum ObjectType {
-      eType_Loading  = TYPE_LOADING,  ///< Type not yet known
-      eType_Image    = TYPE_IMAGE,    ///< This content is an image
-      eType_Plugin   = TYPE_PLUGIN,   ///< This content is a plugin
-      eType_Document = TYPE_DOCUMENT, ///< This is a document type (e.g. HTML)
-      eType_Null     = TYPE_NULL      ///< Type can't be handled
+      // Loading, type not yet known. We may be waiting for a channel to open.
+      eType_Loading        = TYPE_LOADING,
+      // Content is a *non-svg* image
+      eType_Image          = TYPE_IMAGE,
+      // Content is a plugin
+      eType_Plugin         = TYPE_PLUGIN,
+      // Content is a subdocument, possibly SVG
+      eType_Document       = TYPE_DOCUMENT,
+      // No content loaded (fallback). May be showing alternate content or
+      // a custom error handler - *including* click-to-play dialogs
+      eType_Null           = TYPE_NULL
+    };
+    enum FallbackType {
+      eFallbackUnsupported,  // The content type is not supported (e.g. plugin 
+                             // not installed)
+      eFallbackAlternate,    // Showing alternate content
+      eFallbackDisabled,     // The plugin exists, but is disabled
+      eFallbackBlocklisted,  // The plugin is blocklisted and disabled
+      eFallbackOutdated,     // The plugin is considered outdated, but not
+                             // disabled
+      eFallbackCrashed,      // The plugin has crashed
+      eFallbackSuppressed,   // Suppressed by security policy
+      eFallbackUserDisabled, // Blocked by content policy
+      eFallbackClickToPlay,  // The plugin is disabled until the user clicks on
+                             // it
+      eFallbackVulnerableUpdatable, // The plugin is vulnerable (update avail)
+      eFallbackVulnerableNoUpdate  // The plugin is vulnerable (no update avail)
     };
 
     nsObjectLoadingContent();
     virtual ~nsObjectLoadingContent();
 
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSIFRAMELOADEROWNER
@@ -97,309 +89,373 @@ class nsObjectLoadingContent : public ns
 
 #ifdef HAVE_CPP_AMBIGUITY_RESOLVING_USING
     // Fix gcc compile warnings
     using nsImageLoadingContent::OnStartRequest;
     using nsImageLoadingContent::OnDataAvailable;
     using nsImageLoadingContent::OnStopRequest;
 #endif
 
-    ObjectType Type() { return mType; }
-
     /**
-     * Object state. This is a bitmask consisting of a subset of
-     * NS_EVENT_STATE_BROKEN, NS_EVENT_STATE_USERDISABLED and
-     * NS_EVENT_STATE_SUPPRESSED representing the current state of the object.
+     * Object state. This is a bitmask of NS_EVENT_STATEs epresenting the
+     * current state of the object.
      */
     nsEventStates ObjectState() const;
 
+    ObjectType Type() { return mType; }
+
     void SetIsNetworkCreated(bool aNetworkCreated)
     {
       mNetworkCreated = aNetworkCreated;
     }
 
-    // Can flush layout.
-    nsresult InstantiatePluginInstance(const char* aMimeType, nsIURI* aURI);
+    /**
+     * Immediately instantiate a plugin instance. This is a no-op if
+     * mType != eType_Plugin or a plugin is already running.
+     */
+    nsresult InstantiatePluginInstance();
 
+    /**
+     * Notify this class the document state has changed
+     * Called by nsDocument so we may suspend plugins in inactive documents)
+     */
     void NotifyOwnerDocumentActivityChanged();
 
+    /**
+     * Used by pluginHost to know if we're loading with a channel, so it
+     * will not open its own.
+     */
     bool SrcStreamLoading() { return mSrcStreamLoading; };
 
   protected:
     /**
-     * Load the object from the given URI.
-     * @param aURI       The URI to load.
-     * @param aNotify If true, nsIDocumentObserver state change notifications
-     *                will be sent as needed.
-     * @param aTypeHint  MIME Type hint. Overridden by the server unless this
-     *                   class has the eOverrideServerType capability.
-     * @param aForceLoad If true, the object will be refetched even if the URI
-     *                   is the same as the currently-loaded object.
-     * @note Prefer the nsIURI-taking version of this function if a URI object
-     *       is already available.
-     * @see the URI-taking version of this function for a detailed description
-     *      of how a plugin will be found.
-     */
-    nsresult LoadObject(const nsAString& aURI,
-                        bool aNotify,
-                        const nsCString& aTypeHint = EmptyCString(),
-                        bool aForceLoad = false);
-    /**
-     * Loads the object from the given URI.
+     * Begins loading the object when called
+     *
+     * Attributes of |this| QI'd to nsIContent will be inspected, depending on
+     * the node type. This function currently assumes it is a <applet>,
+     * <object>, or <embed> tag.
      *
-     * The URI and type can both be null; if the URI is null a plugin will be
-     * instantiated in the hope that there is a <param> with a useful URI
-     * somewhere around. Other attributes of |this| QI'd to nsIContent will be
-     * inspected. This function attempts hard to find a suitable plugin.
-     *
-     * The instantiated plugin depends on three values:
-     * - The passed-in URI
-     * - The passed-in type hint
+     * The instantiated plugin depends on:
+     * - The URI (<embed src>, <object data>)
+     * - The type 'hint' (type attribute)
+     * - The mime type returned by opening the URI
+     * - Enabled plugins claiming the ultimate mime type
+     * - The capabilities returned by GetCapabilities
      * - The classid attribute, if eSupportClassID is among the capabilities
-     *   and such an attribute is present..
      *
-     * Supported class ID attributes override any other value.
-     *
-     * If no class ID is present and aForceType is true, the handler given by
-     * aTypeHint will be instantiated for this content.
-     * If the URI is null or not supported, and a type hint is given, the plugin
-     * corresponding to that type is instantiated.
+     * If eAllowPluginSkipChannel is true, we may skip opening the URI if our
+     * type hint points to a valid plugin, deferring that responsibility to the
+     * plugin.
+     * Similarly, if no URI is provided, but a type hint for a valid plugin is
+     * present, that plugin will be instantiated
      *
      * Otherwise a request to that URI is made and the type sent by the server
-     * is used to find a suitable handler.
+     * is used to find a suitable handler, EXCEPT when:
+     *  - The type hint refers to a *supported* plugin, in which case that
+     *    plugin will be instantiated regardless of the server provided type
+     *  - The server returns a binary-stream type, and our type hint refers to
+     *    a valid non-document type, we will use the type hint
      *
-     * @param aForceLoad If true, the object will be refetched even if the URI
-     *                   is the same as the currently-loaded object.
+     * @param aNotify    If we should send notifications. If false, content
+     *                   loading may be deferred while appropriate frames are
+     *                   created
+     * @param aForceLoad If we should reload this content (and re-attempt the
+     *                   channel open) even if our parameters did not change
      */
-    nsresult LoadObject(nsIURI* aURI,
-                        bool aNotify,
-                        const nsCString& aTypeHint = EmptyCString(),
+    nsresult LoadObject(bool aNotify,
                         bool aForceLoad = false);
 
     enum Capabilities {
-      eSupportImages    = PR_BIT(0), // Images are supported (imgILoader)
-      eSupportPlugins   = PR_BIT(1), // Plugins are supported (nsIPluginHost)
-      eSupportDocuments = PR_BIT(2), // Documents are supported
-                                     // (nsIDocumentLoaderFactory)
-                                     // This flag always includes SVG
-      eSupportSVG       = PR_BIT(3), // SVG is supported (image/svg+xml)
-      eSupportClassID   = PR_BIT(4), // The classid attribute is supported
-      eOverrideServerType = PR_BIT(5) // The server-sent MIME type is ignored
-                                      // (ignored if no type is specified)
+      eSupportImages       = PR_BIT(0), // Images are supported (imgILoader)
+      eSupportPlugins      = PR_BIT(1), // Plugins are supported (nsIPluginHost)
+      eSupportDocuments    = PR_BIT(2), // Documents are supported
+                                        // (nsIDocumentLoaderFactory)
+                                        // This flag always includes SVG
+      eSupportSVG          = PR_BIT(3), // SVG is supported (image/svg+xml)
+      eSupportClassID      = PR_BIT(4), // The classid attribute is supported
+
+      // Allows us to load a plugin if it matches a MIME type or file extension
+      // registered to a plugin without opening its specified URI first. Can
+      // result in launching plugins for URIs that return differing content
+      // types. Plugins without URIs may instantiate regardless.
+      // XXX(johns) this is our legacy behavior on <embed> tags, whereas object
+      // will always open a channel and check its MIME if a URI is present.
+      eAllowPluginSkipChannel  = PR_BIT(5)
     };
 
     /**
      * Returns the list of capabilities this content node supports. This is a
      * bitmask consisting of flags from the Capabilities enum.
      *
-     * The default implementation supports all types but no classids.
+     * The default implementation supports all types but not
+     * eSupportClassID or eAllowPluginSkipChannel
      */
     virtual PRUint32 GetCapabilities() const;
 
     /**
-     * Fall back to rendering the alternative content.
+     * Destroys all loaded documents/plugins and releases references
      */
-    void Fallback(bool aNotify);
-
-    /**
-     * Subclasses must call this function when they are removed from the
-     * document.
-     *
-     * XXX This is a temporary workaround for docshell suckyness
-     */
-    void RemovedFromDocument();
+    void DestroyContent();
 
     static void Traverse(nsObjectLoadingContent *tmp,
                          nsCycleCollectionTraversalCallback &cb);
 
     void CreateStaticClone(nsObjectLoadingContent* aDest) const;
 
     void DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner, bool aDelayedStop,
                       bool aForcedReentry = false);
 
     nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                         nsIContent* aBindingParent,
                         bool aCompileEventHandler);
     void UnbindFromTree(bool aDeep = true,
                         bool aNullParent = true);
 
   private:
+    // Object parameter changes returned by UpdateObjectParameters
+    enum ParameterUpdateFlags {
+      eParamNoChange           = 0,
+      // Parameters that potentially affect the channel changed
+      // - mOriginalURI, mOriginalContentType
+      eParamChannelChanged     = PR_BIT(0),
+      // Parameters that affect displayed content changed
+      // - mURI, mContentType, mType, mBaseURI
+      eParamStateChanged       = PR_BIT(1)
+    };
+
+    /**
+     * Loads fallback content with the specified FallbackType
+     *
+     * @param aType   FallbackType value for type of fallback we're loading
+     * @param aNotify Send notifications and events. If false, caller is
+     *                responsible for doing so
+     */
+    void LoadFallback(FallbackType aType, bool aNotify);
+
+    /**
+     * Internal version of LoadObject that should only be used by this class
+     * aLoadingChannel is passed by the LoadObject call from OnStartRequest,
+     * primarily for sanity-preservation
+     */
+    nsresult LoadObject(bool aNotify,
+                        bool aForceLoad,
+                        nsIRequest *aLoadingChannel);
+
+    /**
+     * Introspects the object and sets the following member variables:
+     * - mOriginalContentType : This is the type attribute on the element
+     * - mOriginalURI         : The src or data attribute on the element
+     * - mURI                 : The final URI, considering mChannel if
+     *                          mChannelLoaded is set
+     * - mContentType         : The final content type, considering mChannel if
+     *                          mChannelLoaded is set
+     * - mBaseURI             : The object's base URI, which may be set by the
+     *                          object (codebase attribute)
+     * - mType                : The type the object is determined to be based
+     *                          on the above
+     * 
+     * NOTE The class assumes that mType is the currently loaded type at various
+     *      points, so the caller of this function must take the appropriate
+     *      actions to ensure this
+     * 
+     * NOTE This function does not perform security checks, only determining the
+     *      requested type and parameters of the object.
+     * 
+     * @return Returns a bitmask of ParameterUpdateFlags values
+     */
+    ParameterUpdateFlags UpdateObjectParameters();
 
     void NotifyContentObjectWrapper();
 
     /**
-     * Check whether the given request represents a successful load.
+     * Opens the channel pointed to by mURI into mChannel.
+     *
+     * @param aPolicyType The value to be passed to channelPolicy->SetLoadType
+     */
+    nsresult OpenChannel(PRInt32 aPolicyType);
+
+    /**
+     * Closes and releases references to mChannel and, if opened, mFinalListener
      */
-    static bool IsSuccessfulRequest(nsIRequest* aRequest);
+    nsresult CloseChannel();
+
+    /**
+     * If this object is allowed to play plugin content, or if it would display
+     * click-to-play instead.
+     * NOTE that this does not actually check if the object is a loadable plugin
+     */
+    bool ShouldPlay(FallbackType &aReason);
 
     /**
-     * Check whether the URI can be handled internally.
+     * Checks if a URI passes security checks and content policy, relative to
+     * the current document's principal
+     *
+     * @param aURI                The URI to consider
+     * @param aContentPolicy      [in/out] A pointer to the initial content
+     *                            policy, that will be updated to contain the
+     *                            final determined policy
+     * @param aContentPolicyType  The 'contentType' parameter passed to
+     *                            NS_CheckContentLoadPolicy
+     *
+     * @return true if this URI is acceptable for loading
      */
-    static bool CanHandleURI(nsIURI* aURI);
+    bool CheckURILoad(nsIURI *aURI,
+                      PRInt16 *aContentPolicy,
+                      PRInt32 aContentPolicyType);
 
     /**
-     * Checks whether the given type is a supported document type.
+     * Checks if the current mURI and mBaseURI pass content policy and security
+     * checks for loading
+     *
+     * @param aContentPolicy     [in/out] A pointer to the initial content
+     *                           policy, that will be updated to contain the
+     *                           final determined policy if a URL is rejected
+     * @param aContentPolicyType The 'contentType' parameter passed to
+     *                           NS_CheckContentLoadPolicy
+     *
+     * @return true if the URIs are acceptable for loading
+     */
+    bool CheckObjectURIs(PRInt16 *aContentPolicy, PRInt32 aContentPolicyType);
+
+    /**
+     * Checks whether the given type is a supported document type
+     *
+     * NOTE Does not take content policy or capabilities into account
      */
     bool IsSupportedDocument(const nsCString& aType);
 
     /**
-     * Unload the currently loaded content. This removes all state related to
-     * the displayed content and sets the type to eType_Null.
-     * Note: This does not send any notifications.
+     * Unloads all content and resets the object to a completely unloaded state
+     *
+     * NOTE Calls StopPluginInstance() and may spin the event loop
+     *
+     * @param aResetState Reset the object type to 'loading' and destroy channel
+     *                    as well
      */
-    void UnloadContent();
+    void UnloadObject(bool aResetState = true);
 
     /**
      * Notifies document observes about a new type/state of this object.
      * Triggers frame construction as needed. mType must be set correctly when
      * this method is called. This method is cheap if the type and state didn't
      * actually change.
      *
      * @param aSync If a synchronous frame construction is required. If false,
      *              the construction may either be sync or async.
      * @param aNotify if false, only need to update the state of our element.
      */
     void NotifyStateChanged(ObjectType aOldType, nsEventStates aOldState,
                             bool aSync, bool aNotify);
 
     /**
-     * Fires the "Plugin not found" event. This function doesn't do any checks
-     * whether it should be fired, the caller should do that.
+     * Fires the nsPluginErrorEvent. This function doesn't do any checks
+     * whether it should be fired, or whether the given state translates to a
+     * meaningful event
      */
-    static void FirePluginError(nsIContent* thisContent, PluginSupportState state);
+    void FirePluginError(FallbackType aFallbackType);
 
+    /**
+     * Returns a ObjectType value corresponding to the type of content we would
+     * support the given MIME type as, taking capabilities and plugin state
+     * into account
+     * 
+     * NOTE this does not consider whether the content would be suppressed by
+     *      click-to-play or other content policy checks
+     */
     ObjectType GetTypeOfContent(const nsCString& aMIMEType);
 
     /**
      * For a classid, returns the MIME type that can be used to instantiate
      * a plugin for this ID.
      *
+     * @param aClassID The class ID in question
+     * @param aType    [out] The corresponding type, if the call is successful
      * @return NS_ERROR_NOT_AVAILABLE Unsupported class ID.
      */
     nsresult TypeForClassID(const nsAString& aClassID, nsACString& aType);
 
     /**
-     * Gets the base URI to be used for this object. This differs from
-     * nsIContent::GetBaseURI in that it takes codebase attributes into
-     * account.
-     */
-    void GetObjectBaseURI(nsIContent* thisContent, nsIURI** aURI);
-
-
-    /**
      * Gets the frame that's associated with this content node.
      * Does not flush.
      */
     nsObjectFrame* GetExistingFrame();
 
-    /**
-     * Handle being blocked by a content policy.  aStatus is the nsresult
-     * return value of the Should* call, while aRetval is what it returned in
-     * its out parameter.
-     */
-    void HandleBeingBlockedByContentPolicy(nsresult aStatus,
-                                           PRInt16 aRetval);
-
-    /**
-     * Get the plugin support state for the given content node and MIME type.
-     * This is used for purposes of determining whether to fire PluginNotFound
-     * events etc.  aContentType is the MIME type we ended up with.
-     *
-     * This should only be called if the type of this content is eType_Null.
-     */
-    PluginSupportState GetPluginSupportState(nsIContent* aContent, const nsCString& aContentType);
-
-    /**
-     * If the plugin for aContentType is disabled, return ePluginDisabled.
-     * Otherwise (including if there is no plugin for aContentType at all),
-     * return ePluginUnsupported.
-     *
-     * This should only be called if the type of this content is eType_Null.
-     */
-    PluginSupportState GetPluginDisabledState(const nsCString& aContentType);
-
-    /**
-     * When there is no usable plugin available this will send UI events and
-     * update the AutoFallback object appropriate to the reason for there being
-     * no plugin available.
-     */
-    void UpdateFallbackState(nsIContent* aContent, AutoFallback& fallback, const nsCString& aTypeHint);
-
-    nsresult IsPluginEnabledForType(const nsCString& aMIMEType);
-    bool IsPluginEnabledByExtension(nsIURI* uri, nsCString& mimeType);
-
-    /**
-     * The final listener to ship the data to (imagelib, uriloader, etc)
-     */
+    // The final listener for mChannel (uriloader, pluginstreamlistener, etc.)
     nsCOMPtr<nsIStreamListener> mFinalListener;
 
-    /**
-     * Frame loader, for content documents we load.
-     */
+    // Frame loader, for content documents we load.
     nsRefPtr<nsFrameLoader>     mFrameLoader;
 
-    /**
-     * A pending nsAsyncInstantiateEvent (may be null).  This is a weak ref.
-     */
+    // A pending nsAsyncInstantiateEvent (may be null).  This is a weak ref.
     nsIRunnable                *mPendingInstantiateEvent;
 
-    /**
-     * The content type of the resource we were last asked to load.
-     */
+    // The content type of our current load target, updated by
+    // UpdateObjectParameters(). Takes the channel's type into account once
+    // opened.
+    //
+    // May change if a channel is opened, does not imply a loaded state
     nsCString                   mContentType;
 
-    /**
-     * The channel that's currently being loaded. This is a weak reference.
-     * Non-null between asyncOpen and onStopRequest.
-     */
-    nsIChannel*                 mChannel;
+    // The content type 'hint' provided by the element's type attribute. May
+    // or may not be used as a final type
+    nsCString                   mOriginalContentType;
 
-    // The data we were last asked to load
+    // The channel that's currently being loaded. If set, but mChannelLoaded is
+    // false, has not yet reached OnStartRequest
+    nsCOMPtr<nsIChannel>        mChannel;
+
+    // The URI of the current content.
+    // May change as we open channels and encounter redirects - does not imply
+    // a loaded type
     nsCOMPtr<nsIURI>            mURI;
 
-    /**
-     * Type of the currently-loaded content.
-     */
-    ObjectType                  mType          : 16;
+    // The original URI obtained from inspecting the element (codebase, and
+    // src/data). May differ from mURI due to redirects
+    nsCOMPtr<nsIURI>            mOriginalURI;
+
+    // The baseURI used for constructing mURI, and used by some plugins (java)
+    // as a root for other resource requests.
+    nsCOMPtr<nsIURI>            mBaseURI;
+
+
 
-    /**
-     * Whether we are about to call instantiate on our frame. If we aren't,
-     * SetFrame needs to asynchronously call Instantiate.
-     */
+    // Type of the currently-loaded content.
+    ObjectType                  mType           : 8;
+    // The type of fallback content we're showing (see ObjectState())
+    FallbackType                mFallbackType : 8;
+
+    // If true, the current load has finished opening a channel. Does not imply
+    // mChannel -- mChannelLoaded && !mChannel may occur for a load that failed
+    bool                        mChannelLoaded    : 1;
+
+    // Whether we are about to call instantiate on our frame. If we aren't,
+    // SetFrame needs to asynchronously call Instantiate.
     bool                        mInstantiating : 1;
-    // Blocking status from content policy
-    bool                        mUserDisabled  : 1;
-    bool                        mSuppressed    : 1;
 
     // True when the object is created for an element which the parser has
     // created using NS_FROM_PARSER_NETWORK flag. If the element is modified,
     // it may lose the flag.
     bool                        mNetworkCreated : 1;
 
-    // Used to keep track of if a plugin is blocked by click-to-play.
-    // True indicates the plugin is not click-to-play or it has been clicked by
-    // the user.
-    // False indicates the plugin is click-to-play and has not yet been clicked.
-    bool                        mCTPPlayable    : 1;
-
-    // Used to keep track of whether or not a plugin has been played.
-    // This is used for click-to-play plugins.
+    // Used to keep track of whether or not a plugin has been explicitly
+    // activated by PlayPlugin(). (see ShouldPlay())
     bool                        mActivated : 1;
 
     // Protects DoStopPlugin from reentry (bug 724781).
     bool                        mIsStopping : 1;
 
+    // Protects LoadObject from re-entry
+    bool                        mIsLoading : 1;
+
     // Used to track when we might try to instantiate a plugin instance based on
-    // a src data stream being delivered to this object. When this is true we don't
-    // want plugin instance instantiation code to attempt to load src data again or
-    // we'll deliver duplicate streams. Should be cleared when we are not loading
-    // src data.
+    // a src data stream being delivered to this object. When this is true we
+    // don't want plugin instance instantiation code to attempt to load src data
+    // again or we'll deliver duplicate streams. Should be cleared when we are
+    // not loading src data.
     bool mSrcStreamLoading;
 
-    // A specific state that caused us to fallback
-    PluginSupportState          mFallbackReason;
 
     nsWeakFrame                 mPrintFrame;
 
     nsRefPtr<nsPluginInstanceOwner> mInstanceOwner;
 };
 
 #endif
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -3694,28 +3694,26 @@ nsXMLHttpRequest::MaybeDispatchProgressE
   }
 
   // We're uploading if our state is XML_HTTP_REQUEST_OPENED or
   // XML_HTTP_REQUEST_SENT
   if ((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState) {
     if (aFinalProgress) {
       mUploadTotal = mUploadTransferred;
       mUploadProgressMax = mUploadProgress;
-      mUploadLengthComputable = true;
     }
     if (mUpload && !mUploadComplete) {
       DispatchProgressEvent(mUpload, NS_LITERAL_STRING(PROGRESS_STR),
                             true, mUploadLengthComputable, mUploadTransferred,
                             mUploadTotal, mUploadProgress,
                             mUploadProgressMax);
     }
   } else {
     if (aFinalProgress) {
       mLoadTotal = mLoadTransferred;
-      mLoadLengthComputable = true;
     }
     mInLoadProgressEvent = true;
     DispatchProgressEvent(this, NS_LITERAL_STRING(PROGRESS_STR),
                           true, mLoadLengthComputable, mLoadTransferred,
                           mLoadTotal, mLoadTransferred, mLoadTotal);
     mInLoadProgressEvent = false;
     if (mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT ||
         mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER) {
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
@@ -3565,17 +3565,17 @@ nsCanvasRenderingContext2DAzure::SetMozD
   SetMozDash(cx, patternArray, rv);
   return rv.ErrorCode();
 }
 
 JS::Value
 nsCanvasRenderingContext2DAzure::GetMozDash(JSContext* cx, ErrorResult& error)
 {
   JS::Value mozDash;
-  DashArrayToJSVal(CurrentState().dash, cx, &mozDash);
+  error = DashArrayToJSVal(CurrentState().dash, cx, &mozDash);
   return mozDash;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2DAzure::GetMozDash(JSContext* cx, jsval* dashArray)
 {
   ErrorResult rv;
   *dashArray = GetMozDash(cx, rv);
--- a/content/events/src/nsDOMEvent.cpp
+++ b/content/events/src/nsDOMEvent.cpp
@@ -5,16 +5,17 @@
 
 #include "base/basictypes.h"
 
 /* This must occur *after* base/basictypes.h to avoid typedefs conflicts. */
 #include "mozilla/Util.h"
 
 #include "IPC/IPCMessageUtils.h"
 #include "nsCOMPtr.h"
+#include "nsDOMError.h"
 #include "nsDOMEvent.h"
 #include "nsEventStateManager.h"
 #include "nsIFrame.h"
 #include "nsIContent.h"
 #include "nsIPresShell.h"
 #include "nsIDocument.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
--- a/content/events/src/nsDOMNotifyAudioAvailableEvent.cpp
+++ b/content/events/src/nsDOMNotifyAudioAvailableEvent.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 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 "nsDOMError.h"
 #include "nsDOMNotifyAudioAvailableEvent.h"
 #include "nsDOMClassInfoID.h" // DOMCI_DATA, NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO
 #include "nsContentUtils.h" // NS_DROP_JS_OBJECTS
 #include "jsfriendapi.h"
 
 nsDOMNotifyAudioAvailableEvent::nsDOMNotifyAudioAvailableEvent(nsPresContext* aPresContext,
                                                                nsEvent* aEvent,
                                                                PRUint32 aEventType,
--- a/content/html/content/src/nsDOMStringMap.cpp
+++ b/content/html/content/src/nsDOMStringMap.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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 "nsDOMError.h"
 #include "nsDOMStringMap.h"
 
 #include "nsDOMClassInfoID.h"
 #include "nsGenericHTMLElement.h"
 #include "nsContentUtils.h"
 
 DOMCI_DATA(DOMStringMap, nsDOMStringMap)
 
--- a/content/html/content/src/nsHTMLAudioElement.cpp
+++ b/content/html/content/src/nsHTMLAudioElement.cpp
@@ -1,13 +1,14 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 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 "nsDOMError.h"
 #include "nsIDOMHTMLAudioElement.h"
 #include "nsHTMLAudioElement.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsIDocument.h"
 #include "jsfriendapi.h"
 #include "nsContentUtils.h"
 
--- a/content/html/content/src/nsHTMLMenuElement.cpp
+++ b/content/html/content/src/nsHTMLMenuElement.cpp
@@ -6,16 +6,17 @@
 #include "nsHTMLMenuElement.h"
 
 #include "nsIDOMHTMLMenuItemElement.h"
 #include "nsXULContextMenuBuilder.h"
 #include "nsGUIEvent.h"
 #include "nsEventDispatcher.h"
 #include "nsHTMLMenuItemElement.h"
 #include "nsContentUtils.h"
+#include "nsDOMError.h"
 
 enum MenuType
 {
   MENU_TYPE_CONTEXT = 1,
   MENU_TYPE_TOOLBAR,
   MENU_TYPE_LIST
 };
 
--- a/content/html/content/src/nsHTMLObjectElement.cpp
+++ b/content/html/content/src/nsHTMLObjectElement.cpp
@@ -263,57 +263,61 @@ nsHTMLObjectElement::BindToTree(nsIDocum
 
   return NS_OK;
 }
 
 void
 nsHTMLObjectElement::UnbindFromTree(bool aDeep,
                                     bool aNullParent)
 {
-  RemovedFromDocument();
   nsObjectLoadingContent::UnbindFromTree(aDeep, aNullParent);
   nsGenericHTMLFormElement::UnbindFromTree(aDeep, aNullParent);
 }
 
 
 
 nsresult
 nsHTMLObjectElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom *aName,
                              nsIAtom *aPrefix, const nsAString &aValue,
                              bool aNotify)
 {
-  // If we plan to call LoadObject, we want to do it first so that the
-  // object load kicks off _before_ the reflow triggered by the SetAttr.  But if
-  // aNotify is false, we are coming from the parser or some such place; we'll
-  // get bound after all the attributes have been set, so we'll do the
+  nsresult rv = nsGenericHTMLFormElement::SetAttr(aNameSpaceID, aName, aPrefix,
+                                                  aValue, aNotify);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // if aNotify is false, we are coming from the parser or some such place;
+  // we'll get bound after all the attributes have been set, so we'll do the
   // object load from BindToTree/DoneAddingChildren.
   // Skip the LoadObject call in that case.
   // We also don't want to start loading the object when we're not yet in
   // a document, just in case that the caller wants to set additional
   // attributes before inserting the node into the document.
   if (aNotify && IsInDoc() && mIsDoneAddingChildren &&
       aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::data) {
-    nsAutoString type;
-    GetAttr(kNameSpaceID_None, nsGkAtoms::type, type);
-    LoadObject(aValue, aNotify, NS_ConvertUTF16toUTF8(type), true);
+    return LoadObject(aNotify, true);
   }
 
-  return nsGenericHTMLFormElement::SetAttr(aNameSpaceID, aName, aPrefix,
-                                           aValue, aNotify);
+  return NS_OK;
 }
 
 nsresult
 nsHTMLObjectElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
                                bool aNotify)
 {
-  if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::data) {
-    Fallback(aNotify);
+  nsresult rv = nsGenericHTMLFormElement::UnsetAttr(aNameSpaceID,
+                                                    aAttribute, aNotify);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // See comment in SetAttr
+  if (aNotify && IsInDoc() && mIsDoneAddingChildren &&
+      aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::data) {
+    return LoadObject(aNotify, true);
   }
 
-  return nsGenericHTMLFormElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify);
+  return NS_OK;
 }
 
 bool
 nsHTMLObjectElement::IsFocusableForTabIndex()
 {
   nsIDocument* doc = GetCurrentDoc();
   if (!doc || doc->HasFlag(NODE_IS_EDITABLE)) {
     return false;
@@ -508,30 +512,17 @@ nsMapRuleToAttributesFunc
 nsHTMLObjectElement::GetAttributeMappingFunction() const
 {
   return &MapAttributesIntoRule;
 }
 
 void
 nsHTMLObjectElement::StartObjectLoad(bool aNotify)
 {
-  nsAutoString type;
-  GetAttr(kNameSpaceID_None, nsGkAtoms::type, type);
-  NS_ConvertUTF16toUTF8 ctype(type);
-
-  nsAutoString uri;
-  if (GetAttr(kNameSpaceID_None, nsGkAtoms::data, uri)) {
-    LoadObject(uri, aNotify, ctype);
-  }
-  else {
-    // Be sure to call the nsIURI version if we have no attribute
-    // That handles the case where no URI is specified. An empty string would
-    // get interpreted as the page itself, instead of absence of URI.
-    LoadObject(nullptr, aNotify, ctype);
-  }
+  LoadObject(aNotify);
   SetIsNetworkCreated(false);
 }
 
 nsEventStates
 nsHTMLObjectElement::IntrinsicState() const
 {
   return nsGenericHTMLFormElement::IntrinsicState() | ObjectState();
 }
@@ -540,17 +531,17 @@ PRUint32
 nsHTMLObjectElement::GetCapabilities() const
 {
   return nsObjectLoadingContent::GetCapabilities() | eSupportClassID;
 }
 
 void
 nsHTMLObjectElement::DestroyContent()
 {
-  RemovedFromDocument();
+  nsObjectLoadingContent::DestroyContent();
   nsGenericHTMLFormElement::DestroyContent();
 }
 
 nsresult
 nsHTMLObjectElement::CopyInnerTo(nsGenericElement* aDest)
 {
   nsresult rv = nsGenericHTMLFormElement::CopyInnerTo(aDest);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/content/html/content/src/nsHTMLSharedObjectElement.cpp
+++ b/content/html/content/src/nsHTMLSharedObjectElement.cpp
@@ -293,46 +293,43 @@ nsHTMLSharedObjectElement::BindToTree(ns
 
   return NS_OK;
 }
 
 void
 nsHTMLSharedObjectElement::UnbindFromTree(bool aDeep,
                                           bool aNullParent)
 {
-  RemovedFromDocument();
   nsObjectLoadingContent::UnbindFromTree(aDeep, aNullParent);
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 }
 
 
-
 nsresult
 nsHTMLSharedObjectElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom *aName,
                                    nsIAtom *aPrefix, const nsAString &aValue,
                                    bool aNotify)
 {
-  // If we plan to call LoadObject, we want to do it first so that the
-  // object load kicks off _before_ the reflow triggered by the SetAttr.  But if
-  // aNotify is false, we are coming from the parser or some such place; we'll
-  // get bound after all the attributes have been set, so we'll do the
+  nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix,
+                                              aValue, aNotify);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // if aNotify is false, we are coming from the parser or some such place;
+  // we'll get bound after all the attributes have been set, so we'll do the
   // object load from BindToTree/DoneAddingChildren.
   // Skip the LoadObject call in that case.
   // We also don't want to start loading the object when we're not yet in
   // a document, just in case that the caller wants to set additional
   // attributes before inserting the node into the document.
   if (aNotify && IsInDoc() && mIsDoneAddingChildren &&
       aNameSpaceID == kNameSpaceID_None && aName == URIAttrName()) {
-    nsCAutoString type;
-    GetTypeAttrValue(type);
-    LoadObject(aValue, aNotify, type, true);
+    return LoadObject(aNotify, true);
   }
 
-  return nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
-                                       aNotify);
+  return NS_OK;
 }
 
 bool
 nsHTMLSharedObjectElement::IsHTMLFocusable(bool aWithMouse,
                                            bool *aIsFocusable,
                                            PRInt32 *aTabIndex)
 {
   if (mNodeInfo->Equals(nsGkAtoms::embed) || Type() == eType_Plugin) {
@@ -463,53 +460,41 @@ nsHTMLSharedObjectElement::GetAttributeM
   }
 
   return &MapAttributesIntoRule;
 }
 
 void
 nsHTMLSharedObjectElement::StartObjectLoad(bool aNotify)
 {
-  nsCAutoString type;
-  GetTypeAttrValue(type);
-
-  nsAutoString uri;
-  if (!GetAttr(kNameSpaceID_None, URIAttrName(), uri)) {
-    // Be sure to call the nsIURI version if we have no attribute
-    // That handles the case where no URI is specified. An empty string would
-    // get interpreted as the page itself, instead of absence of URI.
-    LoadObject(nullptr, aNotify, type);
-  }
-  else {
-    LoadObject(uri, aNotify, type);
-  }
+  LoadObject(aNotify);
   SetIsNetworkCreated(false);
 }
 
 nsEventStates
 nsHTMLSharedObjectElement::IntrinsicState() const
 {
   return nsGenericHTMLElement::IntrinsicState() | ObjectState();
 }
 
 PRUint32
 nsHTMLSharedObjectElement::GetCapabilities() const
 {
-  PRUint32 capabilities = eSupportPlugins | eOverrideServerType;
+  PRUint32 capabilities = eSupportPlugins | eAllowPluginSkipChannel;
   if (mNodeInfo->Equals(nsGkAtoms::embed)) {
     capabilities |= eSupportSVG | eSupportImages;
   }
 
   return capabilities;
 }
 
 void
 nsHTMLSharedObjectElement::DestroyContent()
 {
-  RemovedFromDocument();
+  nsObjectLoadingContent::DestroyContent();
   nsGenericHTMLElement::DestroyContent();
 }
 
 nsresult
 nsHTMLSharedObjectElement::CopyInnerTo(nsGenericElement* aDest)
 {
   nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/content/html/document/src/PluginDocument.cpp
+++ b/content/html/document/src/PluginDocument.cpp
@@ -115,18 +115,17 @@ PluginStreamListener::SetupPlugin()
   // nsObjectFrame does that at the end of reflow.
   shell->FlushPendingNotifications(Flush_Layout);
 
   nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(embed));
   if (!olc) {
     return NS_ERROR_UNEXPECTED;
   }
   nsObjectLoadingContent* olcc = static_cast<nsObjectLoadingContent*>(olc.get());
-  nsresult rv = olcc->InstantiatePluginInstance(mPluginDoc->GetType().get(),
-                                                mDocument->nsIDocument::GetDocumentURI());
+  nsresult rv = olcc->InstantiatePluginInstance();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   // Now that we're done, allow normal instantiation in the future
   // (say if there's a reframe of this entire presentation).
   mPluginDoc->AllowNormalInstantiation();
 
--- a/content/smil/nsSMILTimedElement.h
+++ b/content/smil/nsSMILTimedElement.h
@@ -1,29 +1,29 @@
 /* -*- Mode: C++; tab-width: 2; 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 NS_SMILTIMEDELEMENT_H_
 #define NS_SMILTIMEDELEMENT_H_
 
+#include "nsISMILAnimationElement.h"
 #include "nsSMILInterval.h"
 #include "nsSMILInstanceTime.h"
 #include "nsSMILMilestone.h"
 #include "nsSMILTimeValueSpec.h"
 #include "nsSMILRepeatCount.h"
 #include "nsSMILTypes.h"
 #include "nsTArray.h"
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "nsAutoPtr.h"
 #include "nsAttrValue.h"
 
-class nsISMILAnimationElement;
 class nsSMILAnimationFunction;
 class nsSMILTimeContainer;
 class nsSMILTimeValue;
 class nsIAtom;
 
 //----------------------------------------------------------------------
 // nsSMILTimedElement
 
--- a/content/svg/content/src/DOMSVGTransformList.cpp
+++ b/content/svg/content/src/DOMSVGTransformList.cpp
@@ -5,16 +5,17 @@
 
 #include "DOMSVGTransformList.h"
 #include "DOMSVGTransform.h"
 #include "DOMSVGMatrix.h"
 #include "SVGAnimatedTransformList.h"
 #include "nsSVGElement.h"
 #include "nsContentUtils.h"
 #include "dombindings.h"
+#include "nsDOMError.h"
 
 // local helper functions
 namespace {
 
 void UpdateListIndicesFromIndex(
   nsTArray<mozilla::DOMSVGTransform*>& aItemsArray,
   PRUint32 aStartingIndex)
 {
--- a/content/svg/content/src/SVGAnimatedTransformList.cpp
+++ b/content/svg/content/src/SVGAnimatedTransformList.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; 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/. */
 
 #include "SVGAnimatedTransformList.h"
 #include "DOMSVGAnimatedTransformList.h"
 
+#include "nsISMILAnimationElement.h"
 #include "nsSMILValue.h"
 #include "SVGTransform.h"
 #include "SVGTransformListSMILType.h"
 #include "nsSVGUtils.h"
 #include "prdtoa.h"
 
 namespace mozilla {
 
--- a/content/svg/content/src/SVGMotionSMILAnimationFunction.cpp
+++ b/content/svg/content/src/SVGMotionSMILAnimationFunction.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; 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/. */
 
 #include "SVGMotionSMILAnimationFunction.h"
+#include "nsISMILAnimationElement.h"
 #include "nsSMILParserUtils.h"
 #include "nsSVGAngle.h"
 #include "SVGMotionSMILType.h"
 #include "SVGMotionSMILPathUtils.h"
 #include "nsSVGPathDataParser.h"
 #include "nsSVGPathElement.h" // for nsSVGPathList
 #include "nsSVGMpathElement.h"
 
--- a/content/svg/content/src/SVGTransform.cpp
+++ b/content/svg/content/src/SVGTransform.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
  * 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 "nsDOMError.h"
 #include "SVGTransform.h"
 #include "nsContentUtils.h"
 #include "nsTextFormatter.h"
 
 namespace {
   const double radPerDegree = 2.0*3.1415926535 / 360.0;
 }
 
--- a/content/svg/content/src/nsSVGBoolean.cpp
+++ b/content/svg/content/src/nsSVGBoolean.cpp
@@ -1,13 +1,14 @@
 /* -*- Mode: C++; tab-width: 2; 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/. */
 
+#include "nsDOMError.h"
 #include "nsSVGBoolean.h"
 #include "nsSMILValue.h"
 #include "SMILBoolType.h"
 
 using namespace mozilla;
 
 NS_SVG_VAL_IMPL_CYCLE_COLLECTION(nsSVGBoolean::DOMAnimatedBoolean, mSVGElement)
 
--- a/content/svg/content/src/nsSVGElement.cpp
+++ b/content/svg/content/src/nsSVGElement.cpp
@@ -51,16 +51,17 @@
 #include "nsIDOMSVGUnitTypes.h"
 #include "nsSVGRect.h"
 #include "nsIFrame.h"
 #include "prdtoa.h"
 #include <stdarg.h>
 #include "nsSMILMappedAttribute.h"
 #include "SVGMotionSMILAttr.h"
 #include "nsAttrValueOrString.h"
+#include "nsSMILAnimationController.h"
 
 using namespace mozilla;
 
 // This is needed to ensure correct handling of calls to the
 // vararg-list methods in this file:
 //   nsSVGElement::GetAnimated{Length,Number,Integer}Values
 // See bug 547964 for details:
 PR_STATIC_ASSERT(sizeof(void*) == sizeof(nullptr));
--- a/content/svg/content/src/nsSVGEnum.cpp
+++ b/content/svg/content/src/nsSVGEnum.cpp
@@ -1,13 +1,14 @@
 /* -*- Mode: C++; tab-width: 2; 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/. */
 
+#include "nsDOMError.h"
 #include "nsSVGEnum.h"
 #include "nsIAtom.h"
 #include "nsSVGElement.h"
 #include "nsSMILValue.h"
 #include "SMILEnumType.h"
 
 using namespace mozilla;
 
--- a/content/svg/content/src/nsSVGInteger.cpp
+++ b/content/svg/content/src/nsSVGInteger.cpp
@@ -1,13 +1,14 @@
 /* -*- Mode: C++; tab-width: 2; 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/. */
 
+#include "nsDOMError.h"
 #include "nsSVGInteger.h"
 #include "nsSMILValue.h"
 #include "SMILIntegerType.h"
 
 using namespace mozilla;
 
 NS_SVG_VAL_IMPL_CYCLE_COLLECTION(nsSVGInteger::DOMAnimatedInteger, mSVGElement)
 
--- a/content/svg/content/src/nsSVGNumber2.cpp
+++ b/content/svg/content/src/nsSVGNumber2.cpp
@@ -1,13 +1,14 @@
 /* -*- Mode: C++; tab-width: 2; 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/. */
 
+#include "nsDOMError.h"
 #include "nsSVGNumber2.h"
 #include "nsSVGUtils.h"
 #include "nsTextFormatter.h"
 #include "prdtoa.h"
 #include "nsMathUtils.h"
 #include "nsContentUtils.h" // NS_ENSURE_FINITE
 #include "nsSMILValue.h"
 #include "nsSMILFloatType.h"
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -776,17 +776,17 @@ nsXULElement::RemoveChildAt(PRUint32 aIn
       nsCOMPtr<nsIDOMElement> oldKidElem = do_QueryInterface(oldKid);
       if (controlElement && oldKidElem) {
         // Iterate over all of the items and find out if they are contained inside
         // the removed subtree.
         PRInt32 length;
         controlElement->GetSelectedCount(&length);
         for (PRInt32 i = 0; i < length; i++) {
           nsCOMPtr<nsIDOMXULSelectControlItemElement> node;
-          controlElement->GetSelectedItem(i, getter_AddRefs(node));
+          controlElement->MultiGetSelectedItem(i, getter_AddRefs(node));
           // we need to QI here to do an XPCOM-correct pointercompare
           nsCOMPtr<nsIDOMElement> selElem = do_QueryInterface(node);
           if (selElem == oldKidElem &&
               NS_SUCCEEDED(controlElement->RemoveItemFromSelection(node))) {
             length--;
             i--;
             fireSelectionHandler = true;
           }
--- a/content/xul/templates/src/nsXULTreeBuilder.cpp
+++ b/content/xul/templates/src/nsXULTreeBuilder.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "nscore.h"
+#include "nsDOMError.h"
 #include "nsIContent.h"
 #include "nsINodeInfo.h"
 #include "nsIDOMElement.h"
 #include "nsILocalStore.h"
 #include "nsIBoxObject.h"
 #include "nsITreeBoxObject.h"
 #include "nsITreeSelection.h"
 #include "nsITreeColumns.h"
new file mode 100644
--- /dev/null
+++ b/docshell/base/LoadContext.cpp
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* 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/LoadContext.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsServiceManagerUtils.h"
+#include "nsContentUtils.h"
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS1(LoadContext, nsILoadContext);
+
+//-----------------------------------------------------------------------------
+// LoadContext::nsILoadContext
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+LoadContext::GetAssociatedWindow(nsIDOMWindow**)
+{
+  MOZ_ASSERT(mIsNotNull);
+
+  // can't support this in the parent process
+  return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+LoadContext::GetTopWindow(nsIDOMWindow**)
+{
+  MOZ_ASSERT(mIsNotNull);
+
+  // can't support this in the parent process
+  return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+LoadContext::IsAppOfType(PRUint32, bool*)
+{
+  MOZ_ASSERT(mIsNotNull);
+
+  // don't expect we need this in parent (Thunderbird/SeaMonkey specific?)
+  return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+LoadContext::GetIsContent(bool* aIsContent)
+{
+  MOZ_ASSERT(mIsNotNull);
+
+  NS_ENSURE_ARG_POINTER(aIsContent);
+
+  *aIsContent = mIsContent;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadContext::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing)
+{
+  MOZ_ASSERT(mIsNotNull);
+
+  NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
+
+  *aUsePrivateBrowsing = mUsePrivateBrowsing;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadContext::SetUsePrivateBrowsing(bool aUsePrivateBrowsing)
+{
+  MOZ_ASSERT(mIsNotNull);
+
+  // We shouldn't need this on parent...
+  return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+LoadContext::GetIsInBrowserElement(bool* aIsInBrowserElement)
+{
+  MOZ_ASSERT(mIsNotNull);
+
+  NS_ENSURE_ARG_POINTER(aIsInBrowserElement);
+
+  *aIsInBrowserElement = mIsInBrowserElement;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadContext::GetAppId(PRUint32* aAppId)
+{
+  MOZ_ASSERT(mIsNotNull);
+
+  NS_ENSURE_ARG_POINTER(aAppId);
+
+  *aAppId = mAppId;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadContext::GetExtendedOrigin(nsIURI* aUri, nsACString& aResult)
+{
+  MOZ_ASSERT(mIsNotNull);
+
+  nsIScriptSecurityManager* ssmgr = nsContentUtils::GetSecurityManager();
+
+  return ssmgr->GetExtendedOrigin(aUri, mAppId, mIsInBrowserElement, aResult);
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/docshell/base/LoadContext.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* 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 LoadContext_h
+#define LoadContext_h
+
+#include "SerializedLoadContext.h"
+
+namespace mozilla {
+
+/**
+ * Class that provides nsILoadContext info in Parent process.  Typically copied
+ * from Child via SerializedLoadContext.
+ *
+ * Note: this is not the "normal" or "original" nsILoadContext.  That is
+ * typically provided by nsDocShell.  This is only used when the original
+ * docshell is in a different process and we need to copy certain values from
+ * it.
+ */
+
+class LoadContext : public nsILoadContext
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSILOADCONTEXT
+
+  LoadContext(const IPC::SerializedLoadContext& toCopy)
+    : mIsNotNull(toCopy.mIsNotNull)
+    , mIsContent(toCopy.mIsContent)
+    , mUsePrivateBrowsing(toCopy.mUsePrivateBrowsing)
+    , mIsInBrowserElement(toCopy.mIsInBrowserElement)
+    , mAppId(toCopy.mAppId)
+  {}
+
+private:
+  bool          mIsNotNull;
+  bool          mIsContent;
+  bool          mUsePrivateBrowsing;
+  bool          mIsInBrowserElement;
+  PRUint32      mAppId;
+};
+
+} // namespace mozilla
+
+#endif // LoadContext_h
+
--- a/docshell/base/Makefile.in
+++ b/docshell/base/Makefile.in
@@ -51,35 +51,39 @@ XPIDLSRCS = \
   nsIDocumentLoaderFactory.idl \
   nsIPrivacyTransitionObserver.idl \
   $(NULL)
 
 EXPORTS = \
   nsDocShellLoadTypes.h \
   nsILinkHandler.h \
   nsIWebShellServices.h \
+  SerializedLoadContext.h \
   $(NULL)
 
 EXPORTS_NAMESPACES = mozilla
 
 EXPORTS_mozilla = \
   IHistory.h \
+  LoadContext.h \
   $(NULL)
 
 CPPSRCS = \
   nsDocShell.cpp \
   nsDocShellLoadInfo.cpp \
   nsDocShellEditorData.cpp \
   nsDocShellTransferableHooks.cpp \
   nsDocShellEnumerator.cpp \
   nsDSURIContentListener.cpp \
   nsDefaultURIFixup.cpp \
   nsWebNavigationInfo.cpp \
   nsAboutRedirector.cpp \
   nsDownloadHistory.cpp \
+  SerializedLoadContext.cpp \
+  LoadContext.cpp \
   $(NULL)
 
 # we don't want the shared lib, but we want to force the creation of a
 # static lib.
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
new file mode 100644
--- /dev/null
+++ b/docshell/base/SerializedLoadContext.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* 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 "SerializedLoadContext.h"
+#include "nsNetUtil.h"
+#include "nsIChannel.h"
+#include "nsIWebSocketChannel.h"
+
+namespace IPC {
+
+SerializedLoadContext::SerializedLoadContext(nsILoadContext* aLoadContext)
+{
+  Init(aLoadContext);
+}
+
+SerializedLoadContext::SerializedLoadContext(nsIChannel* aChannel)
+{
+  nsCOMPtr<nsILoadContext> loadContext;
+  NS_QueryNotificationCallbacks(aChannel, loadContext);
+  Init(loadContext);
+}
+
+SerializedLoadContext::SerializedLoadContext(nsIWebSocketChannel* aChannel)
+{
+  nsCOMPtr<nsILoadContext> loadContext;
+  NS_QueryNotificationCallbacks(aChannel, loadContext);
+  Init(loadContext);
+}
+
+void
+SerializedLoadContext::Init(nsILoadContext* aLoadContext)
+{
+  if (aLoadContext) {
+    mIsNotNull = true;
+    aLoadContext->GetIsContent(&mIsContent);
+    aLoadContext->GetUsePrivateBrowsing(&mUsePrivateBrowsing);
+    aLoadContext->GetAppId(&mAppId);
+    aLoadContext->GetIsInBrowserElement(&mIsInBrowserElement);
+  } else {
+    mIsNotNull = false;
+    // none of below values really matter when mIsNotNull == false:
+    // we won't be GetInterfaced to nsILoadContext
+    mIsContent = true;
+    mUsePrivateBrowsing = false;
+    mAppId = 0;
+    mIsInBrowserElement = false;
+  }
+}
+
+} // namespace IPC
+
new file mode 100644
--- /dev/null
+++ b/docshell/base/SerializedLoadContext.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* 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 SerializedLoadContext_h
+#define SerializedLoadContext_h
+
+#include "base/basictypes.h"
+#include "IPC/IPCMessageUtils.h"
+#include "nsIIPCSerializable.h"
+#include "nsILoadContext.h"
+
+/*
+ *  This file contains the IPC::SerializedLoadContext class, which is used to
+ *  copy data across IPDL from Child process contexts so it is available in the
+ *  Parent.
+ */
+
+class nsIChannel;
+class nsIWebSocketChannel;
+
+namespace IPC {
+
+class SerializedLoadContext
+{
+public:
+  SerializedLoadContext()
+  {
+    Init(nsnull);
+  }
+
+  SerializedLoadContext(nsILoadContext* aLoadContext);
+  SerializedLoadContext(nsIChannel* aChannel);
+  SerializedLoadContext(nsIWebSocketChannel* aChannel);
+
+  void Init(nsILoadContext* aLoadContext);
+
+  bool IsNotNull() const 
+  {
+    return mIsNotNull;
+  }
+
+  // used to indicate if child-side LoadContext * was null.
+  bool          mIsNotNull;
+  bool          mIsContent;
+  bool          mUsePrivateBrowsing;
+  bool          mIsInBrowserElement;
+  PRUint32      mAppId;
+};
+
+// Function to serialize over IPDL
+template<>
+struct ParamTraits<SerializedLoadContext>
+{
+  typedef SerializedLoadContext paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mIsNotNull);
+    WriteParam(aMsg, aParam.mIsContent);
+    WriteParam(aMsg, aParam.mUsePrivateBrowsing);
+    WriteParam(aMsg, aParam.mAppId);
+    WriteParam(aMsg, aParam.mIsInBrowserElement);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &aResult->mIsNotNull) ||
+        !ReadParam(aMsg, aIter, &aResult->mIsContent)  ||
+        !ReadParam(aMsg, aIter, &aResult->mUsePrivateBrowsing)  ||
+        !ReadParam(aMsg, aIter, &aResult->mAppId)  ||
+        !ReadParam(aMsg, aIter, &aResult->mIsInBrowserElement)) {
+      return false;
+    }
+
+    return true;
+  }
+};
+
+} // namespace IPC
+
+#endif // SerializedLoadContext_h
+
--- a/dom/apps/src/Webapps.js
+++ b/dom/apps/src/Webapps.js
@@ -130,21 +130,23 @@ WebappsRegistry.prototype = {
     xhr.addEventListener("load", (function() {
       if (xhr.status == 200) {
         try {
           let manifest = JSON.parse(xhr.responseText, installOrigin);
           if (!this.checkManifest(manifest, installOrigin)) {
             Services.DOMRequest.fireError(request, "INVALID_MANIFEST");
           } else {
             let receipts = (aParams && aParams.receipts && Array.isArray(aParams.receipts)) ? aParams.receipts : [];
+            let categories = (aParams && aParams.categories && Array.isArray(aParams.categories)) ? aParams.categories : [];
             cpmm.sendAsyncMessage("Webapps:Install", { app: { installOrigin: installOrigin,
                                                               origin: this._getOrigin(aURL),
                                                               manifestURL: aURL,
                                                               manifest: manifest,
-                                                              receipts: receipts },
+                                                              receipts: receipts,
+                                                              categories: categories },
                                                               from: installURL,
                                                               oid: this._id,
                                                               requestID: requestID });
           }
         } catch(e) {
           Services.DOMRequest.fireError(request, "MANIFEST_PARSE_ERROR");
         }
       }
@@ -186,18 +188,21 @@ WebappsRegistry.prototype = {
   },
 
   installPackage: function(aPackageURL, aParams) {
     let request = this.createRequest();
     let requestID = this.getRequestId(request);
 
     let receipts = (aParams && aParams.receipts &&
                     Array.isArray(aParams.receipts)) ? aParams.receipts : [];
+    let categories = (aParams && aParams.categories &&
+                      Array.isArray(aParams.categories)) ? aParams.categories : [];
     cpmm.sendAsyncMessage("Webapps:InstallPackage", { url: aPackageURL,
                                                       receipts: receipts,
+                                                      categories: categories,
                                                       requestID: requestID,
                                                       oid: this._id,
                                                       from: this._window.location.href,
                                                       installOrigin: this._getOrigin(this._window.location.href) });
     return request;
   },
 
   get mgmt() {
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -497,28 +497,29 @@ let DOMApplicationRegistry = {
       NetUtil.asyncCopy(aInput, ostream, function (aResult) {
         if (!Components.isSuccessCode(aResult)) {
           // We failed to save the zip.
           cleanup("DOWNLOAD_ERROR");
           return;
         }
         // Build a data structure to call the webapps confirmation dialog :
         // - load the manifest from the zip
-        // - set data.app.(origin, install_origin, manifestURL, manifest, receipts)
+        // - set data.app.(origin, install_origin, manifestURL, manifest, receipts, categories)
         // - call notifyObservers(this, "webapps-ask-install", JSON.stringify(msg));
         let msg = {
           from: aData.from,
           oid: aData.oid,
           requestId: aData.requestId,
           app: {
             packageId: id,