Merge m-i to ionmonkey
authorBrian Hackett <bhackett1024@gmail.com>
Mon, 15 Apr 2013 05:37:53 -0600
changeset 129597 cb36ad241f80
parent 129596 75ff34ead9fc (current diff)
parent 128764 53c2e7b9753b (diff)
child 129598 d746d516bf55
push id24582
push userryanvm@gmail.com
push dateTue, 23 Apr 2013 19:03:31 +0000
treeherdermozilla-central@8b1a7228674a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone23.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 m-i to ionmonkey
addon-sdk/source/test/addons/require/packages/panel/main.js
addon-sdk/source/test/addons/require/packages/panel/package.json
addon-sdk/source/test/addons/require/packages/panel/page-mod.js
addon-sdk/source/test/addons/require/panel.js
browser/locales/en-US/webapp-uninstaller/webapp-uninstaller.properties
browser/locales/en-US/webapprt/webapp.dtd
browser/locales/en-US/webapprt/webapp.properties
build/unix/mddepend.pl
content/svg/content/src/DOMSVGAnimatedTransformList.cpp
content/svg/content/src/DOMSVGAnimatedTransformList.h
content/svg/content/src/SVGAnimatedTransformList.cpp
content/svg/content/src/SVGAnimatedTransformList.h
dbm/.cvsignore
dbm/include/.cvsignore
dbm/include/Makefile.in
dbm/include/Makefile.win
dbm/include/cdefs.h
dbm/include/extern.h
dbm/include/hash.h
dbm/include/hsearch.h
dbm/include/mcom_db.h
dbm/include/mpool.h
dbm/include/ncompat.h
dbm/include/page.h
dbm/include/queue.h
dbm/include/search.h
dbm/include/winfile.h
dbm/src/.cvsignore
dbm/src/Makefile.in
dbm/src/Makefile.win
dbm/src/db.c
dbm/src/h_bigkey.c
dbm/src/h_func.c
dbm/src/h_log2.c
dbm/src/h_page.c
dbm/src/hash.c
dbm/src/hash_buf.c
dbm/src/memmove.c
dbm/src/mktemp.c
dbm/src/snprintf.c
dbm/src/strerror.c
dbm/tests/.cvsignore
dbm/tests/Makefile.in
dbm/tests/dbmtest.pkg
dbm/tests/lots.c
dom/imptests/failures/html/html/dom/documents/dom-tree-accessors/Makefile.in
dom/imptests/failures/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/Makefile.in
dom/imptests/failures/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/moz.build
dom/imptests/failures/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/test_document.getElementsByName-newelements.html.json
dom/imptests/failures/html/html/dom/documents/dom-tree-accessors/moz.build
dom/imptests/failures/html/html/dom/documents/dom-tree-accessors/test_document.body-getter-frameset-and-body.html.json
dom/imptests/failures/html/html/dom/documents/dom-tree-accessors/test_document.title-03.html.json
dom/imptests/failures/html/html/dom/documents/dom-tree-accessors/test_document.title-04.xhtml.json
dom/imptests/failures/html/html/dom/documents/dom-tree-accessors/test_document.title-06.html.json
dom/imptests/failures/html/html/dom/documents/dom-tree-accessors/test_document.title-07.html.json
dom/imptests/failures/html/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/Makefile.in
dom/imptests/failures/html/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/moz.build
dom/imptests/failures/html/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/test_script-IDL-event-htmlfor.html.json
dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_CharacterData-remove.html.json
dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_DocumentType-remove.html.json
dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Element-remove.html.json
dom/imptests/html/html/dom/documents/dom-tree-accessors/Makefile.in
dom/imptests/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/Makefile.in
dom/imptests/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/moz.build
dom/imptests/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/test_document.getElementsByName-case.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/test_document.getElementsByName-case.xhtml
dom/imptests/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/test_document.getElementsByName-id.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/test_document.getElementsByName-id.xhtml
dom/imptests/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/test_document.getElementsByName-namespace.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/test_document.getElementsByName-namespace.xhtml
dom/imptests/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/test_document.getElementsByName-newelements.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/test_document.getElementsByName-newelements.xhtml
dom/imptests/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/test_document.getElementsByName-null-undef.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/test_document.getElementsByName-null-undef.xhtml
dom/imptests/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/test_document.getElementsByName-param.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/test_document.getElementsByName-param.xhtml
dom/imptests/html/html/dom/documents/dom-tree-accessors/document.getElementsByName/test_document.getElementsByName-same.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/moz.build
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_Document.getElementsByClassName-null-undef.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_Element.getElementsByClassName-null-undef.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_document.body-getter-body-and-frameset.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_document.body-getter-foreign-frameset.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_document.body-getter-frameset-and-body.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_document.body-setter-01.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_document.embeds-document.plugins-01.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_document.getElementsByClassName-same.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_document.head-01.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_document.head-02.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_document.title-01.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_document.title-02.xhtml
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_document.title-03.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_document.title-04.xhtml
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_document.title-05.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_document.title-06.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_document.title-07.html
dom/imptests/html/html/dom/documents/dom-tree-accessors/test_nameditem-01.html
dom/imptests/html/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/Makefile.in
dom/imptests/html/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/moz.build
dom/imptests/html/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/test_document-color-01.html
dom/imptests/html/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/test_document-color-02.html
dom/imptests/html/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/test_document-color-03.html
dom/imptests/html/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/test_document-color-04.html
dom/imptests/html/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/test_heading-obsolete-attributes-01.html
dom/imptests/html/html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/test_script-IDL-event-htmlfor.html
dom/interfaces/contacts/nsIDOMContactProperties.idl
dom/interfaces/geolocation/nsIGeolocation.idl
gfx/layers/d3d10/LayerManagerD3D10.cpp.orig
gfx/layers/d3d10/ThebesLayerD3D10.cpp.orig
gfx/layers/ipc/PTexture.ipdl
gfx/layers/ipc/TextureChild.cpp
gfx/layers/ipc/TextureChild.h
gfx/layers/ipc/TextureParent.cpp
gfx/layers/ipc/TextureParent.h
js/src/Makefile.in
js/src/ion/BaselineInspector.cpp
js/src/ion/BaselineInspector.h
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/Ion.cpp
js/src/ion/IonAnalysis.cpp
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/IonCaches.h
js/src/ion/IonMacroAssembler.h
js/src/ion/IonTypes.h
js/src/ion/JSONSpewer.cpp
js/src/ion/LIR-Common.h
js/src/ion/LIR.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/Lowering.h
js/src/ion/MCallOptimize.cpp
js/src/ion/MIR.cpp
js/src/ion/MIR.h
js/src/ion/MIRGraph.cpp
js/src/ion/MIRGraph.h
js/src/ion/MOpcodes.h
js/src/ion/ParallelArrayAnalysis.cpp
js/src/ion/RegisterSets.h
js/src/ion/TypePolicy.h
js/src/ion/arm/LIR-arm.h
js/src/ion/shared/Lowering-shared-inl.h
js/src/ion/x64/LIR-x64.h
js/src/ion/x86/LIR-x86.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsinterpinlines.h
js/src/jsopcode.cpp
js/src/methodjit/Compiler.cpp
js/src/vm/Stack.h
media/webrtc/trunk/src/common_video/vplib/main/source/moz.build
media/webrtc/trunk/src/modules/audio_conference_mixer/source/moz.build
media/webrtc/trunk/src/modules/media_file/source/moz.build
media/webrtc/trunk/src/modules/rtp_rtcp/source/moz.build
media/webrtc/trunk/src/modules/udp_transport/source/moz.build
media/webrtc/trunk/src/modules/utility/source/moz.build
media/webrtc/trunk/src/modules/video_capture/main/source/moz.build
media/webrtc/trunk/src/modules/video_coding/codecs/i420/main/source/moz.build
media/webrtc/trunk/src/modules/video_coding/codecs/vp8/main/source/moz.build
media/webrtc/trunk/src/modules/video_coding/main/source/moz.build
media/webrtc/trunk/src/modules/video_processing/main/source/moz.build
media/webrtc/trunk/src/system_wrappers/source/moz.build
media/webrtc/trunk/src/video_engine/main/source/moz.build
mobile/android/components/FormAutoComplete.js
mobile/android/modules/LocaleRepository.jsm
mobile/android/modules/contacts.jsm
mobile/android/modules/linuxTypes.jsm
mobile/android/modules/video.jsm
security/coreconf/AIX.mk
security/coreconf/Android.mk
security/coreconf/BSD_OS.mk
security/coreconf/BeOS.mk
security/coreconf/Darwin.mk
security/coreconf/FreeBSD.mk
security/coreconf/HP-UX.mk
security/coreconf/HP-UXA.09.03.mk
security/coreconf/HP-UXA.09.07.mk
security/coreconf/HP-UXA.09.mk
security/coreconf/HP-UXB.10.01.mk
security/coreconf/HP-UXB.10.10.mk
security/coreconf/HP-UXB.10.20.mk
security/coreconf/HP-UXB.10.30.mk
security/coreconf/HP-UXB.10.mk
security/coreconf/HP-UXB.11.00.mk
security/coreconf/HP-UXB.11.11.mk
security/coreconf/HP-UXB.11.20.mk
security/coreconf/HP-UXB.11.22.mk
security/coreconf/HP-UXB.11.23.mk
security/coreconf/HP-UXB.11.mk
security/coreconf/IRIX.mk
security/coreconf/IRIX5.2.mk
security/coreconf/IRIX5.3.mk
security/coreconf/IRIX5.mk
security/coreconf/IRIX6.2.mk
security/coreconf/IRIX6.3.mk
security/coreconf/IRIX6.5.mk
security/coreconf/IRIX6.mk
security/coreconf/Linux.mk
security/coreconf/Makefile
security/coreconf/NCR3.0.mk
security/coreconf/NEC4.2.mk
security/coreconf/NetBSD.mk
security/coreconf/OS2.mk
security/coreconf/OSF1.mk
security/coreconf/OSF1V2.0.mk
security/coreconf/OSF1V3.0.mk
security/coreconf/OSF1V3.2.mk
security/coreconf/OSF1V4.0.mk
security/coreconf/OSF1V4.0B.mk
security/coreconf/OSF1V4.0D.mk
security/coreconf/OSF1V5.0.mk
security/coreconf/OSF1V5.1.mk
security/coreconf/OpenBSD.mk
security/coreconf/OpenUNIX.mk
security/coreconf/QNX.mk
security/coreconf/README
security/coreconf/RISCOS.mk
security/coreconf/ReliantUNIX.mk
security/coreconf/ReliantUNIX5.4.mk
security/coreconf/SCOOS5.0.mk
security/coreconf/SCO_SV3.2.mk
security/coreconf/SunOS4.1.3_U1.mk
security/coreconf/SunOS5.10.mk
security/coreconf/SunOS5.10_i86pc.mk
security/coreconf/SunOS5.11.mk
security/coreconf/SunOS5.11_i86pc.mk
security/coreconf/SunOS5.8.mk
security/coreconf/SunOS5.8_i86pc.mk
security/coreconf/SunOS5.9.mk
security/coreconf/SunOS5.9_i86pc.mk
security/coreconf/SunOS5.mk
security/coreconf/UNIX.mk
security/coreconf/UNIXWARE2.1.mk
security/coreconf/WIN32.mk
security/coreconf/WIN95.mk
security/coreconf/WINNT.mk
security/coreconf/arch.mk
security/coreconf/command.mk
security/coreconf/config.mk
security/coreconf/coreconf.dep
security/coreconf/coreconf.pl
security/coreconf/cpdist.pl
security/coreconf/headers.mk
security/coreconf/import.pl
security/coreconf/jdk.mk
security/coreconf/jniregen.pl
security/coreconf/location.mk
security/coreconf/mkdepend/Makefile
security/coreconf/mkdepend/cppsetup.c
security/coreconf/mkdepend/def.h
security/coreconf/mkdepend/ifparser.c
security/coreconf/mkdepend/ifparser.h
security/coreconf/mkdepend/imakemdep.h
security/coreconf/mkdepend/include.c
security/coreconf/mkdepend/main.c
security/coreconf/mkdepend/mkdepend.man
security/coreconf/mkdepend/parse.c
security/coreconf/mkdepend/pr.c
security/coreconf/module.mk
security/coreconf/nsinstall/Makefile
security/coreconf/nsinstall/nsinstall.c
security/coreconf/nsinstall/pathsub.c
security/coreconf/nsinstall/pathsub.h
security/coreconf/nsinstall/sunos4.h
security/coreconf/outofdate.pl
security/coreconf/prefix.mk
security/coreconf/release.pl
security/coreconf/rules.mk
security/coreconf/ruleset.mk
security/coreconf/source.mk
security/coreconf/suffix.mk
security/coreconf/tree.mk
security/coreconf/version.mk
security/coreconf/version.pl
security/dbm/Makefile
security/dbm/config/config.mk
security/dbm/include/Makefile
security/dbm/include/manifest.mn
security/dbm/manifest.mn
security/dbm/src/Makefile
security/dbm/src/config.mk
security/dbm/src/dirent.c
security/dbm/src/dirent.h
security/dbm/src/manifest.mn
security/dbm/tests/Makefile
security/nss/lib/freebl/mpi/mpv_sparcv8x.s
security/patches/bug-832942.patch
security/patches/bug-834091.patch
toolkit/crashreporter/fileid/moz.build
--- a/CLOBBER
+++ b/CLOBBER
@@ -12,9 +12,9 @@
 #          O               O
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
-Bug 860503: Force CLOBBER because of PGO autoclobber issues.
+Bug 856358: Needs a clobber because it renames an IDL file. See also bug 860894.
--- a/Makefile.in
+++ b/Makefile.in
@@ -30,23 +30,25 @@ include $(topsrcdir)/config/config.mk
 
 GARBAGE_DIRS += dist _javagen _profile _tests staticlib
 DIST_GARBAGE = config.cache config.log config.status* config-defs.h \
    config/autoconf.mk \
    unallmakefiles mozilla-config.h \
    netwerk/necko-config.h xpcom/xpcom-config.h xpcom/xpcom-private.h \
    $(topsrcdir)/.mozconfig.mk $(topsrcdir)/.mozconfig.out
 
+ifndef MOZ_PROFILE_USE
 default alldep all:: CLOBBER $(topsrcdir)/configure config.status
 	$(RM) -r $(DIST)/sdk
 	$(RM) -r $(DIST)/include
 	$(RM) -r $(DIST)/private
 	$(RM) -r $(DIST)/public
 	$(RM) -r $(DIST)/bin
 	$(RM) -r _tests
+endif
 
 CLOBBER: $(topsrcdir)/CLOBBER
 	@echo "STOP!  The CLOBBER file has changed."
 	@echo "Please run the build through a sanctioned build wrapper, such as"
 	@echo "'mach build' or client.mk."
 	@exit 1
 
 $(topsrcdir)/configure: $(topsrcdir)/configure.in
--- a/addon-sdk/source/doc/dev-guide-source/credits.md
+++ b/addon-sdk/source/doc/dev-guide-source/credits.md
@@ -9,191 +9,153 @@ We'd like to thank our many Jetpack proj
 ### A ###
 
 * Adamantium
 * Ehsan Akhgari
 * arky
 * [Heather Arthur](https://github.com/harthur)
 * Dietrich Ayala
 
-<!--end-->
-
 ### B ###
 
 * [Romain B](https://github.com/Niamor)
 * [Louis-Rémi Babé](https://github.com/louisremi)
 * Will Bamberg
 * Thomas Bassetto
 * Tomaz Bevec
 * Zbigniew Braniecki
 * Daniel Buchner
 * James Burke
 
-<!--end-->
-
 ### C ###
 
 * [Shane Caraveo](https://github.com/mixedpuppy)
 * [Matěj Cepl](https://github.com/mcepl)
 * Marc Chevrier
+* [Timothy Guan-tin Chien](https://github.com/timdream)
 * Hernán Rodriguez Colmeiro
 * [David Creswick](https://github.com/dcrewi)
 
-<!--end-->
-
 ### D ###
 
 * dexter
 * Christopher Dorn
 * Connor Dunn
 * dynamis
 
-<!--end-->
-
 ### F ###
 
-* [Matteo Ferretti (ZER0)](https://github.com/ZER0)
+* [Matteo Ferretti](https://github.com/ZER0)
 * fuzzykiller
 
-<!--end-->
-
 ### G ###
 
 * [Marcio Galli](https://github.com/taboca)
 * [Ben Gillbanks](http://www.iconfinder.com/browse/iconset/circular_icons/)
 * Felipe Gomes
 * Irakli Gozalishvili
 * Luca Greco
 * Jeff Griffiths
 * [David Guo](https://github.com/dglol)
 
-<!--end-->
-
 ### H ###
 
 * Mark Hammond
 * Mark A. Hershberger
 * Lloyd Hilaiel
 * Bobby Holley
 
-<!--end-->
-
 ### I ###
 
 * Shun Ikejima
 
-<!--end-->
-
 ### J ###
 
 * Eric H. Jung
 
-<!--end-->
-
 ### K ###
 
 * Hrishikesh Kale
 * Wes Kocher
 * Lajos Koszti
 * [Vladimir Kukushkin](https://github.com/kukushechkin)
 
-<!--end-->
-
 ### L ###
 
 * Edward Lee
 * Gregg Lind
 
-<!--end-->
-
 ### M ###
 
 * [Nils Maier](https://github.com/nmaier)
 * Gervase Markham
 * Dave Mason
 * Myk Melez
 * Zandr Milewski
 * Noelle Murata
 
-<!--end-->
-
 ### N ###
 
 * Siavash Askari Nasr
 * Joe R. Nassimian ([placidrage](https://github.com/placidrage))
 * Dương H. Nguyễn
 * Nick Nguyen
 
-<!--end-->
-
 ### O ###
 
 * [ongaeshi](https://github.com/ongaeshi)
 * Paul O’Shannessy
 * Les Orchard
 
-<!--end-->
-
 ### P ###
 
 * Robert Pankowecki
 * [Jamie Phelps](https://github.com/jxpx777)
 * [Alexandre Poirot](https://github.com/ochameau)
 * Nickolay Ponomarev
 
-<!--end-->
-
 ### R ###
 
 * Aza Raskin
 
-<!--end-->
-
 ### S ###
 
+* [Jordan Santell](https://github.com/jsantell)
 * Till Schneidereit
 * Justin Scott
 * Ayan Shah
 * [skratchdot](https://github.com/skratchdot)
 * Henrik Skupin
 * slash
 * Markus Stange
 * Dan Stevens
 * [J. Ryan Stinnett](https://github.com/jryans)
 * [Mihai Sucan](https://github.com/mihaisucan)
 
-<!--end-->
-
 ### T ###
 
 * taku0
 * Clint Talbert
 * Tim Taubert
 * Shane Tomlinson
 * Dave Townsend
 * [Matthias Tylkowski](https://github.com/tylkomat)
 
-<!--end-->
-
 ### V ###
 
 * Peter Van der Beken
 * Sander van Veen
 * Atul Varma
 * [Erik Vold](https://github.com/erikvold)
 * Vladimir Vukicevic
 
-<!--end-->
-
 ### W ###
 
 * Brian Warner
 * [Henri Wiechers](https://github.com/hwiechers)
 * Drew Willcoxon
 * Blake Winton
 * Michal Wojciechowski
 
-<!--end-->
-
 ### Z ###
 
 * Piotr Zalewa
 * Brett Zamir
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/doc/module-source/sdk/content/mod.md
@@ -0,0 +1,105 @@
+<!-- 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/. -->
+
+The `mod` module provides functions to modify a page content.
+
+<api name="attachTo">
+@function
+  Function applies given `modification` to a given `window`.
+
+  For example, the following code applies a style to a content window, adding
+  a border to all divs in page:
+
+      var attachTo = require("sdk/content/mod").attachTo;
+      var Style = require("sdk/stylesheet/style").Style;
+
+      var style = Style({
+        source: "div { border: 4px solid gray }"
+      });
+
+      // assuming window points to the content page we want to modify
+      attachTo(style, window);
+
+@param modification {object}
+  The modification we want to apply to the target.
+
+@param window {nsIDOMWindow}
+  The window to be modified.
+</api>
+
+<api name="detachFrom">
+@function
+  Function removes attached `modification` from a given `window`.
+  If `window` is not specified, `modification` is removed from all the windows
+  it's being attached to.
+
+  For example, the following code applies and removes a style to a content
+  window, adding a border to all divs in page:
+
+      var { attachTo, detachFrom } = require("sdk/content/mod");
+      var Style = require("sdk/stylesheet/style").Style;
+
+      var style = Style({
+        source: "div { border: 4px solid gray }"
+      });
+
+      // assuming window points to the content page we want to modify
+      attachTo(style, window);
+      // ...
+      detachFrom(style, window);
+
+@param modification {object}
+  The modification we want to remove from the target
+
+@param window {nsIDOMWindow}
+  The window to be modified.
+  If `window` is not provided `modification` is removed from all targets it's
+  being attached to.
+</api>
+
+<api name="getTargetWindow">
+@function
+  Function takes `target`, value representing content (page) and returns
+  `nsIDOMWindow` for that content.
+  If `target` does not represents valid content `null` is returned.
+  For example target can be a content window itself in which case it's will be
+  returned back.
+
+@param target {object}
+  The object for which we want to obtain the window represented or contained.
+  If a `nsIDOMWindow` is given, it works as an identify function, returns
+  `target` itself.
+@returns {nsIDOMWindow|null}
+  The window represented or contained by the `target`, if any. Returns `null`
+  otherwise.
+</api>
+
+<api name="attach">
+@function
+  Function applies given `modification` to a given `target` representing a
+  content to be modified.
+
+@param modification {object}
+  The modification we want to apply to the target
+
+@param target {object}
+  Target is a value that representing content to be modified. It is valid only
+  when `getTargetWindow(target)` returns nsIDOMWindow of content it represents.
+</api>
+
+<api name="detach">
+@function
+  Function removes attached `modification`. If `target` is specified
+  `modification` is removed from that `target` only, otherwise `modification` is
+  removed from all the targets it's being attached to.
+
+@param modification {object}
+  The modification we want to remove from the target
+
+@param target {object}
+  Target is a value that representing content to be modified. It is valid only
+  when `getTargetWindow(target)` returns `nsIDOMWindow` of content it represents.
+  If `target` is not provided `modification` is removed from all targets it's
+  being attached to.
+</api>
--- a/addon-sdk/source/doc/module-source/sdk/panel.md
+++ b/addon-sdk/source/doc/module-source/sdk/panel.md
@@ -420,17 +420,17 @@ Creates a panel.
   @prop [width] {number}
     The width of the panel in pixels. Optional.
   @prop [height] {number}
     The height of the panel in pixels. Optional.
   @prop [focus] {boolean}
     Set to `false` to prevent taking the focus away when the panel is shown.
     Only turn this off if necessary, to prevent accessibility issue.
     Optional, default to `true`.
-  @prop [contentURL] {string}
+  @prop [contentURL] {string,URL}
     The URL of the content to load in the panel.
   @prop [allow] {object}
     An optional object describing permissions for the content.  It should
     contain a single key named `script` whose value is a boolean that indicates
     whether or not to execute script in the content.  `script` defaults to true.
   @prop [contentScriptFile] {string,array}
     A local file URL or an array of local file URLs of content scripts to load.
     Content scripts specified by this property are loaded *before* those
--- a/addon-sdk/source/doc/module-source/sdk/platform/xpcom.md
+++ b/addon-sdk/source/doc/module-source/sdk/platform/xpcom.md
@@ -43,17 +43,17 @@ interface to listen for and log all topi
     var StarObserver = Class({
       extends:  Unknown,
       interfaces: [ 'nsIObserver' ],
       topic: '*',
       register: function register() {
         observerService.addObserver(this, this.topic, false);
       },
       unregister: function() {
-        addObserver.removeObserver(this, this.topic, false);
+        addObserver.removeObserver(this, this.topic);
       },
       observe: function observe(subject, topic, data) {
         console.log('star observer:', subject, topic, data);
       }
     });
 
     var starobserver = StarObserver();
     starobserver.register();
--- a/addon-sdk/source/doc/module-source/sdk/request.md
+++ b/addon-sdk/source/doc/module-source/sdk/request.md
@@ -57,18 +57,18 @@ from the [@mozhacks](https://twitter.com
     }).get();
 
 <api name="Request">
 @constructor
 This constructor creates a request object that can be used to make network
 requests. The constructor takes a single parameter `options` which is used to
 set several properties on the resulting `Request`.
 @param options {object}
-    @prop url {string}
-    This is the url to which the request will be made.
+    @prop [url] {string,url}
+    This is the url to which the request will be made. Can either be a [String](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String) or an instance of the SDK's [URL](https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/url.html#URL).
 
     @prop [onComplete] {function}
     This function will be called when the request has received a response (or in
     terms of XHR, when `readyState == 4`). The function is passed a `Response`
     object.
 
     @prop [headers] {object}
     An unordered collection of name/value pairs representing headers to send
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/doc/module-source/sdk/stylesheet/style.md
@@ -0,0 +1,53 @@
+<!-- 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/. -->
+
+Module provides `Style` function that can be used to construct content style
+modification via stylesheet files or CSS rules.
+
+<api name="Style">
+@class
+<api name="Style">
+@constructor
+  The Style constructor creates an object that represents style modifications
+  via stylesheet file(s) or/and CSS rules. Stylesheet file URL(s) are verified
+  to be local to an add-on, while CSS rules are virified to be a string or
+  array of strings.
+
+  The style created can be applied to a content by calling `attach`,
+  and removed using `detach`. Those functions are part of [content/mod](modules/sdk/content/mod.html) module.
+@param options {object}
+  Options for the style. All these options are optional. Although if you
+  don't supply any stylesheet or CSS rules, your style won't be very useful.
+
+  @prop uri {string,array}
+    A string, or an array of strings, that represents local URI to stylesheet.
+  @prop source {string,array}
+    A string, or an array of strings, that contains CSS rules. Those rules
+    are applied after the rules in the stylesheet specified with `uri` options,
+    if provided.
+  @prop [type="author"] {string}
+    The type of the sheet. It accepts the following values: `"agent"`, `"user"`
+    and `"author"`.
+    If not provided, the default value is `"author"`.
+</api>
+
+<api name="source">
+@property {string}
+  An array of strings that contains the CSS rule(s) specified in the constructor's
+  option; `null` if no `source` option was given to the constructor.
+  This property is read-only.
+</api>
+<api name="uri">
+@property {string}
+  An array of strings that contains the stylesheet local URI(s) specified in the
+  constructor's option; `null` if no `uri` option was given to the
+  constructor.
+  This property is read-only.
+</api>
+<api name="type">
+  @property {string}
+    The type of the sheet. If no type is provided in constructor's option,
+    it returns the default value, `"author"`. This property is read-only.
+</api>
+</api>
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/doc/module-source/sdk/stylesheet/utils.md
@@ -0,0 +1,41 @@
+<!-- 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/. -->
+
+Module provides helper functions for working with stylesheets.
+
+<api name="loadSheet">
+  @function
+  Synchronously loads a style sheet from `uri` and adds it to the list of
+  additional style sheets of the document.
+  The sheets added takes effect immediately, and only on the document of the
+  `window` given.
+  @param window {nsIDOMWindow}
+  @param uri {string, nsIURI}
+  @param [type="author"] {string}
+    The type of the sheet. It accepts the following values: `"agent"`, `"user"`
+    and `"author"`.
+    If not provided, the default value is `"author"`.
+</api>
+
+<api name="removeSheet">
+  @function
+  Remove the document style sheet at `sheetURI` from the list of additional
+  style sheets of the document.  The removal takes effect immediately.
+  @param window {nsIDOMWindow}
+  @param uri {string, nsIURI}
+  @param [type="author"] {string}
+    The type of the sheet. It accepts the following values: `"agent"`, `"user"`
+    and `"author"`.
+    If not provided, the default value is `"author"`.
+</api>
+
+<api name="isTypeValid">
+  @function
+  Verifies that the `type` given is a valid stylesheet's type.
+  The values considered valid are: `"agent"`, `"user"` and `"author"`.
+  @param type {string}
+    The type of the sheet.
+  @returns {boolean}
+    `true` if the `type` given is valid, otherwise `false`.
+</api>
--- a/addon-sdk/source/doc/module-source/sdk/url.md
+++ b/addon-sdk/source/doc/module-source/sdk/url.md
@@ -78,16 +78,27 @@ The `url` module provides functionality 
 
 @param path {string}
   The native file path, as a string, to be converted.
 
 @returns {string}
   The converted URL as a string.
 </api>
 
+<api name="isValidURI">
+@function
+  Checks the validity of a URI. `isValidURI("http://mozilla.org")` would return `true`, whereas `isValidURI("mozilla.org")` would return `false`.
+
+@param uri {string}
+  The URI, as a string, to be tested.
+
+@returns {boolean}
+  A boolean indicating whether or not the URI is valid.
+</api>
+
 <api name="DataURL">
 @class
 <api name="DataURL">
 @constructor
   The DataURL constructor creates an object that represents a data: URL,
   verifying that the provided string is a valid data: URL in the process.
 
 @param uri {string}
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/doc/module-source/sdk/util/array.md
@@ -0,0 +1,156 @@
+<!-- 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/. -->
+
+The `util/array` module provides simple helper functions for working with 
+arrays.
+
+<api name="has">
+@function
+Returns `true` if the given [`Array`](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array) contains the element or `false` otherwise.
+A simplified version of `array.indexOf(element) >= 0`.
+
+    let { has } = require('sdk/util/array');
+    let a = ['rock', 'roll', 100];
+
+    has(a, 'rock'); // true
+    has(a, 'rush'); // false
+    has(a, 100); // true
+
+@param array {array}
+  The array to search.
+
+@param element {*}
+  The element to search for in the array.
+
+@returns {boolean}
+  A boolean indicating whether or not the element was found in the array.
+</api>
+
+<api name="hasAny">
+@function
+Returns `true` if the given [`Array`](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array) contains any of the elements in the 
+`elements` array, or `false` otherwise.
+
+    let { hasAny } = require('sdk/util/array');
+    let a = ['rock', 'roll', 100];
+
+    hasAny(a, ['rock', 'bright', 'light']); // true
+    hasAny(a, ['rush', 'coil', 'jet']); // false
+
+@param array {array}
+  The array to search for elements.
+
+@param elements {array}
+  An array of elements to search for in `array`.
+
+@returns {boolean}
+  A boolean indicating whether or not any of the elements were found
+  in the array.
+</api>
+
+<api name="add">
+@function
+If the given [array](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array) does not already contain the given element, this function
+adds the element to the array and returns `true`. Otherwise, this function
+does not add the element and returns `false`.
+
+    let { add } = require('sdk/util/array');
+    let a = ['alice', 'bob', 'carol'];
+
+    add(a, 'dave'); // true
+    add(a, 'dave'); // false
+    add(a, 'alice'); // false
+
+    console.log(a); // ['alice', 'bob', 'carol', 'dave']
+
+@param array {array}
+  The array to add the element to.
+
+@param element {*}
+  The element to add
+
+@returns {boolean}
+  A boolean indicating whether or not the element was added to the array.
+</api>
+
+<api name="remove">
+@function
+If the given [array](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array) contains the given element, this function
+removes the element from the array and returns `true`. Otherwise, this function
+does not alter the array and returns `false`.
+
+    let { remove } = require('sdk/util/array');
+    let a = ['alice', 'bob', 'carol'];
+
+    remove(a, 'dave'); // false
+    remove(a, 'bob'); // true 
+    remove(a, 'bob'); // false
+
+    console.log(a); // ['alice', 'carol']
+
+@param array {array}
+  The array to remove the element from.
+
+@param element {*}
+  The element to remove from the array if it contains it.
+
+@returns {boolean}
+  A boolean indicating whether or not the element was removed from the array.
+</api>
+
+<api name="unique">
+@function
+Produces a duplicate-free version of the given [array](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array).
+
+    let { unique } = require('sdk/util/array');
+    let a = [1, 1, 1, 1, 3, 4, 7, 7, 10, 10, 10, 10];
+    let b = unique(a);
+
+    console.log(a); // [1, 1, 1, 1, 3, 4, 7, 7, 10, 10, 10, 10];
+    console.log(b); // [1, 3, 4, 7, 10];
+
+@param array {array}
+  Source array.
+
+@returns {array}
+  The new, duplicate-free array.
+</api>
+
+<api name="flatten">
+@function
+Flattens a nested [array](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array) of any depth.
+
+    let { flatten } = require('sdk/util/array');
+    let a = ['cut', ['ice', ['fire']], 'elec'];
+    let b = flatten(a);
+
+    console.log(a); // ['cut', ['ice', ['fire']], 'elec']
+    console.log(b); // ['cut', 'ice', 'fire', 'elec'];
+
+@param array {array}
+  The array to flatten.
+
+@returns {array}
+  The new, flattened array.
+</api>
+
+<api name="fromIterator">
+@function
+Iterates over an [iterator](https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Iterators_and_Generators#Iterators) and returns the results as an [array](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array).
+
+    let { fromIterator } = require('sdk/util/array');
+    let i = new Set();
+    i.add('otoro');
+    i.add('unagi');
+    i.add('keon');
+
+    fromIterator(i) // ['otoro', 'unagi', 'keon']
+
+@param iterator {iterator}
+  The [`Iterator`](https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Iterators_and_Generators#Iterators) object over which to iterate and place results into an array.
+
+@returns {array}
+  The iterator's results in an array.
+</api>
+
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/doc/module-source/sdk/util/object.md
@@ -0,0 +1,75 @@
+<!-- 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/. -->
+
+The `util/object` module provides simple helper functions for working with 
+objects.
+
+<api name="merge">
+@function
+Merges all of the properties of all arguments into the first argument. If
+two or more argument objects have properties with the same name, the 
+property is overwritten with precedence from right to left, implying that
+properties of the object on the left are overwritten by a same named property
+of an object on the right. Properties are merged with [descriptors](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty#Description) onto the source object.
+
+Any argument given with "falsy" values (null, undefined) in case of objects
+are skipped.
+
+    let { merge } = require("sdk/util/object");
+    var a = { jetpacks: "are yes", foo: 10 }
+    var b = merge(a, { foo: 5, bar: 6 }, { foo: 50, location: "SF" });
+
+    b === a    // true
+    b.jetpacks // "are yes"
+    b.foo      // 50
+    b.bar      // 6
+    b.location // "SF"
+  
+    // Merge also translates property descriptors
+    var c = { "type": "addon" };
+    var d = {};
+    Object.defineProperty(d, "name", {
+      value: "jetpacks",
+      configurable: false
+    });
+    merge(c, d);
+    var props = Object.getOwnPropertyDescriptor(c, "name");
+    console.log(props.configurable); // true
+    
+@param source {object}
+  The object that other properties are merged into.
+
+@param arguments {object}
+  `n` amount of additional objects that are merged into `source` object.
+
+@returns {object}
+  The `source` object.
+</api>
+
+<api name="extend">
+@function
+Returns an object that inherits from the first argument and contains
+all of the properties from all following arguments, with precedence from
+right to left.
+
+`extend(source1, source2, source3)` is the equivalent of
+`merge(Object.create(source1), source2, source3)`.
+
+    let { extend } = require("sdk/util/object");
+    var a = { alpha: "a" };
+    var b = { beta: "b" };
+    var g = { gamma: "g", alpha: null };
+    var x = extend(a, b, g);
+
+    console.log(a); // { alpha: "a" }
+    console.log(b); // { beta: "b" }
+    console.log(g); // { gamma: "g", alpha: null }
+    console.log(x); // { alpha: null, beta: "b", gamma: "g" }
+
+@param arguments {object}
+  `n` arguments that get merged into a new object.
+
+@returns {object}
+  The new, merged object.
+</api>
old mode 100644
new mode 100755
--- a/addon-sdk/source/lib/sdk/addon/window.js
+++ b/addon-sdk/source/lib/sdk/addon/window.js
@@ -49,9 +49,12 @@ eventTarget.addEventListener("DOMContent
 }, false);
 
 
 
 exports.ready = promise;
 exports.window = window;
 
 // Still close window on unload to claim memory back early.
-unload(function() { window.close() });
+unload(function() {
+  window.close()
+  frame.parentNode.removeChild(frame);
+});
--- a/addon-sdk/source/lib/sdk/content/loader.js
+++ b/addon-sdk/source/lib/sdk/content/loader.js
@@ -7,37 +7,34 @@
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
 const { EventEmitter } = require('../deprecated/events');
 const { validateOptions } = require('../deprecated/api-utils');
-const { URL } = require('../url');
+const { isValidURI, URL } = require('../url');
 const file = require('../io/file');
+const { contract } = require('../util/contract');
 
 const LOCAL_URI_SCHEMES = ['resource', 'data'];
 
 // Returns `null` if `value` is `null` or `undefined`, otherwise `value`.
-function ensureNull(value) {
-  return value == null ? null : value;
-}
+function ensureNull(value) value == null ? null : value
 
 // map of property validations
 const valid = {
   contentURL: {
-    ok: function (value) {
-      try {
-        URL(value);
-      }
-      catch(e) {
-        return false;
-      }
-      return true;
+    map: function(url) !url ? ensureNull(url) : url.toString(), 
+    is: ['undefined', 'null', 'string'],
+    ok: function (url) {
+      if (url === null)
+        return true;
+      return isValidURI(url);
     },
     msg: 'The `contentURL` option must be a valid URL.'
   },
   contentScriptFile: {
     is: ['undefined', 'null', 'string', 'array'],
     map: ensureNull,
     ok: function(value) {
       if (value === null)
@@ -197,8 +194,10 @@ const Loader = EventEmitter.compose({
       this._emit('propertyChange', {
         contentScript: this._contentScript = value
       });
     }
   },
   _contentScript: null
 });
 exports.Loader = Loader;
+
+exports.contract = contract(valid);
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/content/mod.js
@@ -0,0 +1,60 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=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/. */
+"use strict";
+
+module.metadata = {
+  "stability": "experimental"
+};
+
+const { Ci } = require("chrome");
+const method = require("method/core");
+const { add, remove, iterator } = require("../lang/weak-set");
+
+let getTargetWindow = method("getTargetWindow");
+
+getTargetWindow.define(function (target) {
+  if (target instanceof Ci.nsIDOMWindow)
+    return target;
+  if (target instanceof Ci.nsIDOMDocument)
+    return target.defaultView || null;
+
+  return null;
+});
+
+exports.getTargetWindow = getTargetWindow;
+
+let attachTo = method("attachTo");
+exports.attachTo = attachTo;
+
+let detachFrom = method("detatchFrom");
+exports.detachFrom = detachFrom;
+
+function attach(modification, target) {
+  let window = getTargetWindow(target);
+
+  attachTo(modification, window);
+
+  // modification are stored per content; `window` reference can still be the
+  // same even if the content is changed, therefore `document` is used instead.
+  add(modification, window.document);
+}
+exports.attach = attach;
+
+function detach(modification, target) {
+  if (target) {
+    let window = getTargetWindow(target);
+    detachFrom(modification, window);
+    remove(modification, window.document);
+  }
+  else {
+    let documents = iterator(modification);
+    for (let document of documents) {
+      detachFrom(modification, document.defaultView);
+      remove(modification, document);
+    }
+  }
+}
+exports.detach = detach;
--- a/addon-sdk/source/lib/sdk/content/worker.js
+++ b/addon-sdk/source/lib/sdk/content/worker.js
@@ -458,58 +458,65 @@ const Worker = EventEmitter.compose({
 
   // Is worker being frozen? i.e related document is frozen in bfcache.
   // Content script should not be reachable if frozen.
   _frozen: true,
 
   constructor: function Worker(options) {
     options = options || {};
 
-    if ('window' in options)
-      this._window = options.window;
     if ('contentScriptFile' in options)
       this.contentScriptFile = options.contentScriptFile;
     if ('contentScriptOptions' in options)
       this.contentScriptOptions = options.contentScriptOptions;
     if ('contentScript' in options)
       this.contentScript = options.contentScript;
-    if ('onError' in options)
-      this.on('error', options.onError);
-    if ('onMessage' in options)
-      this.on('message', options.onMessage);
-    if ('onDetach' in options)
-      this.on('detach', options.onDetach);
+
+    this._setListeners(options);
 
     // Internal feature that is only used by SDK unit tests.
     // See `PRIVATE_KEY` definition for more information.
     if ('exposeUnlockKey' in options && options.exposeUnlockKey === PRIVATE_KEY)
       this._expose_key = true;
 
+    unload.ensure(this._public, "destroy");
+
+    // Ensure that worker._port is initialized for contentWorker to be able
+    // to send events during worker initialization.
+    this.port;
+
+    this._documentUnload = this._documentUnload.bind(this);
+    this._pageShow = this._pageShow.bind(this);
+    this._pageHide = this._pageHide.bind(this);
+
+    if ("window" in options) this._attach(options.window);
+  },
+
+  _setListeners: function(options) {
+    if ('onError' in options)
+      this.on('error', options.onError);
+    if ('onMessage' in options)
+      this.on('message', options.onMessage);
+    if ('onDetach' in options)
+      this.on('detach', options.onDetach);
+  },
+
+  _attach: function(window) {
+    this._window = window;
     // Track document unload to destroy this worker.
     // We can't watch for unload event on page's window object as it
     // prevents bfcache from working:
     // https://developer.mozilla.org/En/Working_with_BFCache
     this._windowID = getInnerId(this._window);
-    observers.add("inner-window-destroyed",
-                  this._documentUnload = this._documentUnload.bind(this));
+    observers.add("inner-window-destroyed", this._documentUnload);
 
     // Listen to pagehide event in order to freeze the content script
     // while the document is frozen in bfcache:
-    this._window.addEventListener("pageshow",
-                                  this._pageShow = this._pageShow.bind(this),
-                                  true);
-    this._window.addEventListener("pagehide",
-                                  this._pageHide = this._pageHide.bind(this),
-                                  true);
-
-    unload.ensure(this._public, "destroy");
-
-    // Ensure that worker._port is initialized for contentWorker to be able
-    // to send use event during WorkerSandbox(this)
-    this.port;
+    this._window.addEventListener("pageshow", this._pageShow, true);
+    this._window.addEventListener("pagehide", this._pageHide, true);
 
     // will set this._contentWorker pointing to the private API:
     this._contentWorker = WorkerSandbox(this);
 
     // Mainly enable worker.port.emit to send event to the content worker
     this._inited = true;
     this._frozen = false;
 
--- a/addon-sdk/source/lib/sdk/core/disposable.js
+++ b/addon-sdk/source/lib/sdk/core/disposable.js
@@ -8,51 +8,66 @@ module.metadata = {
   "stability": "experimental"
 };
 
 
 let { Class } = require("./heritage");
 let { on, off } = require('../system/events');
 let unloadSubject = require('@loader/unload');
 
-function DisposeHandler(disposable) {
-  return function onDisposal({subject}) {
-    if (subject.wrappedJSObject === unloadSubject) {
-      off("sdk:loader:destroy", onDisposal);
-      disposable.destroy();
+let disposables = WeakMap();
+
+function initialize(instance) {
+  // Create an event handler that will dispose instance on unload.
+  function handler(event) {
+    if (event.subject.wrappedJSObject === unloadSubject) {
+      dispose(instance);
+      instance.dispose();
     }
   }
+
+  // Form weak reference between disposable instance and an unload event
+  // handler associated with it. This will make sure that event handler can't
+  // be garbage collected as long as instance is referenced. Also note that
+  // system events intentionally hold weak reference to an event handler, this
+  // will let GC claim both instance and an unload handler before actual add-on
+  // unload if instance contains no other references.
+  disposables.set(instance, handler);
+  on("sdk:loader:destroy", handler);
 }
+exports.initialize = initialize;
+
+function dispose(instance) {
+  // Disposes given instance by removing it from weak map so that handler can
+  // be GC-ed even if references to instance are kept. Also unregister unload
+  // handler.
+
+  let handler = disposables.get(instance);
+  if (handler) off("sdk:loader:destroy", handler);
+  disposables.delete(instance);
+}
+exports.dispose = dispose;
 
 // Base type that takes care of disposing it's instances on add-on unload.
 // Also makes sure to remove unload listener if it's already being disposed.
 let Disposable = Class({
-  initialize: function dispose() {
-    this.setupDisposal();
+  initialize: function setupDisposable() {
+    // First setup instance before initializing it's disposal. If instance
+    // fails to initialize then there is no instance to be disposed at the
+    // unload.
     this.setup.apply(this, arguments);
+    initialize(this);
   },
-  setupDisposal: function setupDisposal() {
-    // Create `onDisposal` handler that will be invoked on unload of
-    // the add-on, unless this is destroyed earlier.
-    Object.defineProperty(this, "onDisposal", { value: DisposeHandler(this) });
-    on("sdk:loader:destroy", this.onDisposal);
-  },
-  teardownDisposable: function tearDisposal() {
-    // Removes `onDisposal` handler so that it won't be invoked on unload.
-    off("sdk:loader:destroy", this.onDisposal);
-  },
-
   setup: function setup() {
     // Implement your initialize logic here.
   },
   dispose: function dispose() {
     // Implement your cleanup logic here.
   },
 
   destroy: function destroy() {
     // Destroying disposable removes unload handler so that attempt to dispose
     // won't be made at unload & delegates to dispose.
-    this.teardownDisposable();
+    dispose(this);
     this.dispose();
   }
 });
-
 exports.Disposable = Disposable;
--- a/addon-sdk/source/lib/sdk/event/core.js
+++ b/addon-sdk/source/lib/sdk/event/core.js
@@ -12,16 +12,18 @@ module.metadata = {
 
 const UNCAUGHT_ERROR = 'An error event was emitted for which there was no listener.';
 const BAD_LISTENER = 'The event listener must be a function.';
 
 const { ns } = require('../core/namespace');
 
 const event = ns();
 
+const EVENT_TYPE_PATTERN = /^on([A-Z]\w+$)/;
+
 // Utility function to access given event `target` object's event listeners for
 // the specific event `type`. If listeners for this type does not exists they
 // will be created.
 const observers = function observers(target, type) {
   let listeners = event(target);
   return type in listeners ? listeners[type] : listeners[type] = [];
 };
 
@@ -153,8 +155,31 @@ exports.off = off;
 /**
  * Returns a number of event listeners registered for the given event `type`
  * on the given event `target`.
  */
 function count(target, type) {
   return observers(target, type).length;
 }
 exports.count = count;
+
+/**
+ * Registers listeners on the given event `target` from the given `listeners`
+ * dictionary. Iterates over the listeners and if property name matches name
+ * pattern `onEventType` and property is a function, then registers it as
+ * an `eventType` listener on `target`.
+ *
+ * @param {Object} target
+ *    The type of event.
+ * @param {Object} listeners
+ *    Dictionary of listeners.
+ */
+function setListeners(target, listeners) {
+  Object.keys(listeners || {}).forEach(function onEach(key) {
+    let match = EVENT_TYPE_PATTERN.exec(key);
+    let type = match && match[1].toLowerCase();
+    let listener = listeners[key];
+
+    if (type && typeof(listener) === 'function')
+      on(target, type, listener);
+  });
+}
+exports.setListeners = setListeners;
--- a/addon-sdk/source/lib/sdk/event/target.js
+++ b/addon-sdk/source/lib/sdk/event/target.js
@@ -5,45 +5,38 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 'use strict';
 
 module.metadata = {
   "stability": "stable"
 };
 
-const { on, once, off } = require('./core');
+const { on, once, off, setListeners } = require('./core');
 const { method } = require('../lang/functional');
 const { Class } = require('../core/heritage');
 
-const EVENT_TYPE_PATTERN = /^on([A-Z]\w+$)/;
-
 /**
  * `EventTarget` is an exemplar for creating an objects that can be used to
  * add / remove event listeners on them. Events on these objects may be emitted
  * via `emit` function exported by 'event/core' module.
  */
 const EventTarget = Class({
   /**
    * Method initializes `this` event source. It goes through properties of a
    * given `options` and registers listeners for the ones that look like an
    * event listeners.
    */
+  /**
+   * Method initializes `this` event source. It goes through properties of a
+   * given `options` and registers listeners for the ones that look like an
+   * event listeners.
+   */
   initialize: function initialize(options) {
-    options = options || {};
-    // Go through each property and registers event listeners for those
-    // that have a name matching following pattern (`onEventType`).
-    Object.keys(options).forEach(function onEach(key) {
-      let match = EVENT_TYPE_PATTERN.exec(key);
-      let type = match && match[1].toLowerCase();
-      let listener = options[key];
-
-      if (type && typeof(listener) === 'function')
-        this.on(type, listener);
-    }, this);
+    setListeners(this, options);
   },
   /**
    * Registers an event `listener` that is called every time events of
    * specified `type` are emitted.
    * @param {String} type
    *    The type of event.
    * @param {Function} listener
    *    The listener function that processes the event.
--- a/addon-sdk/source/lib/sdk/event/utils.js
+++ b/addon-sdk/source/lib/sdk/event/utils.js
@@ -94,8 +94,11 @@ function merge(inputs) {
   }
 
   return output;
 }
 exports.merge = merge;
 
 function expand(f, inputs) merge(map(f, inputs))
 exports.expand = expand;
+
+function pipe(from, to) on(from, "*", emit.bind(emit, to))
+exports.pipe = pipe;
--- a/addon-sdk/source/lib/sdk/frame/utils.js
+++ b/addon-sdk/source/lib/sdk/frame/utils.js
@@ -35,30 +35,34 @@ exports.getDocShell = getDocShell;
  *    case all the following options are ignored.
  * @params {Boolean} options.allowAuth
  *    Whether to allow auth dialogs. Defaults to `false`.
  * @params {Boolean} options.allowJavascript
  *    Whether to allow Javascript execution. Defaults to `false`.
  * @params {Boolean} options.allowPlugins
  *    Whether to allow plugin execution. Defaults to `false`.
  */
-function create(document, options) {
+function create(target, options) {
+  target = target instanceof Ci.nsIDOMDocument ? target.documentElement :
+           target instanceof Ci.nsIDOMWindow ? target.document.documentElement :
+           target;
   options = options || {};
   let remote = options.remote || false;
   let namespaceURI = options.namespaceURI || XUL;
   let isXUL = namespaceURI === XUL;
   let nodeName = isXUL && options.browser ? 'browser' : 'iframe';
+  let document = target.ownerDocument;
 
   let frame = document.createElementNS(namespaceURI, nodeName);
   // Type="content" is mandatory to enable stuff here:
   // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsFrameLoader.cpp#1776
   frame.setAttribute('type', options.type || 'content');
   frame.setAttribute('src', options.uri || 'about:blank');
 
-  document.documentElement.appendChild(frame);
+  target.appendChild(frame);
 
   // Load in separate process if `options.remote` is `true`.
   // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsFrameLoader.cpp#1347
   if (remote) {
     if (isXUL) {
       // We remove XBL binding to avoid execution of code that is not going to
       // work because browser has no docShell attribute in remote mode
       // (for example)
@@ -74,13 +78,23 @@ function create(document, options) {
 
 
   // If browser is remote it won't have a `docShell`.
   if (!remote) {
     let docShell = getDocShell(frame);
     docShell.allowAuth = options.allowAuth || false;
     docShell.allowJavascript = options.allowJavascript || false;
     docShell.allowPlugins = options.allowPlugins || false;
+
+    // Control whether the document can move/resize the window. Requires
+    // recently added platform capability, so we test to avoid exceptions
+    // in cases where capability is not present yet.
+    if ("allowWindowControl" in docShell && "allowWindowControl" in options)
+      docShell.allowWindowControl = !!options.allowWindowControl;
   }
 
   return frame;
 }
 exports.create = create;
+
+function swapFrameLoaders(from, to)
+  from.QueryInterface(Ci.nsIFrameLoaderOwner).swapFrameLoaders(to)
+exports.swapFrameLoaders = swapFrameLoaders;
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/lang/weak-set.js
@@ -0,0 +1,56 @@
+"use strict";
+
+const { Cu } = require("chrome");
+
+function makeGetterFor(Type) {
+  let cache = new WeakMap();
+
+  return function getFor(target) {
+    if (!cache.has(target))
+      cache.set(target, new Type());
+
+    return cache.get(target);
+  }
+}
+
+let getLookupFor = makeGetterFor(WeakMap);
+let getRefsFor = makeGetterFor(Set);
+
+function add(target, value) {
+  if (has(target, value))
+    return;
+
+  getLookupFor(target).set(value, true);
+  getRefsFor(target).add(Cu.getWeakReference(value));
+}
+exports.add = add;
+
+function remove(target, value) {
+  getLookupFor(target).delete(value);
+}
+exports.remove = remove;
+
+function has(target, value) {
+  return getLookupFor(target).has(value);
+}
+exports.has = has;
+
+function clear(target) {
+  getLookupFor(target).clear();
+  getRefsFor(target).clear();
+}
+exports.clear = clear;
+
+function iterator(target) {
+  let refs = getRefsFor(target);
+
+  for (let ref of refs) {
+    let value = ref.get();
+
+    if (has(target, value))
+      yield value;
+    else
+      refs.delete(ref);
+  }
+}
+exports.iterator = iterator;
--- a/addon-sdk/source/lib/sdk/page-mod.js
+++ b/addon-sdk/source/lib/sdk/page-mod.js
@@ -21,24 +21,18 @@ const { Cc, Ci } = require('chrome');
 const { merge } = require('./util/object');
 const { readURISync } = require('./net/url');
 const { windowIterator } = require('./deprecated/window-utils');
 const { isBrowser, getFrames } = require('./window/utils');
 const { getTabs, getTabContentWindow, getTabForContentWindow,
         getURI: getTabURI } = require('./tabs/utils');
 const { has, hasAny } = require('./util/array');
 const { ignoreWindow } = require('sdk/private-browsing/utils');
-
-const styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"].
-                            getService(Ci.nsIStyleSheetService);
-
-const USER_SHEET = styleSheetService.USER_SHEET;
-
-const io = Cc['@mozilla.org/network/io-service;1'].
-              getService(Ci.nsIIOService);
+const { Style } = require("./stylesheet/style");
+const { attach, detach } = require("./content/mod");
 
 // Valid values for `attachTo` option
 const VALID_ATTACHTO_OPTIONS = ['existing', 'top', 'frame'];
 
 // contentStyle* / contentScript* are sharing the same validation constraints,
 // so they can be mostly reused, except for the messages.
 const validStyleOptions = {
   contentStyle: merge(Object.create(validationAttributes.contentScript), {
@@ -130,61 +124,42 @@ const PageMod = Loader.compose(EventEmit
     rules.on('add', this._onRuleAdd = this._onRuleAdd.bind(this));
     rules.on('remove', this._onRuleRemove = this._onRuleRemove.bind(this));
 
     if (Array.isArray(include))
       rules.add.apply(null, include);
     else
       rules.add(include);
 
-    let styleRules = "";
-
-    if (contentStyleFile)
-      styleRules = [].concat(contentStyleFile).map(readURISync).join("");
-
-    if (contentStyle)
-      styleRules += [].concat(contentStyle).join("");
-
-    if (styleRules) {
-      this._onRuleUpdate = this._onRuleUpdate.bind(this);
-
-      this._styleRules = styleRules;
-
-      this._registerStyleSheet();
-      rules.on('add', this._onRuleUpdate);
-      rules.on('remove', this._onRuleUpdate);
+    if (contentStyle || contentStyleFile) {
+      this._style = Style({
+        uri: contentStyleFile,
+        source: contentStyle
+      });
     }
 
     this.on('error', this._onUncaughtError = this._onUncaughtError.bind(this));
     pageModManager.add(this._public);
 
-    this._loadingWindows = [];
-
     // `_applyOnExistingDocuments` has to be called after `pageModManager.add()`
     // otherwise its calls to `_onContent` method won't do anything.
     if ('attachTo' in options && has(options.attachTo, 'existing'))
       this._applyOnExistingDocuments();
   },
 
   destroy: function destroy() {
 
-    this._unregisterStyleSheet();
-
-    this.include.removeListener('add', this._onRuleUpdate);
-    this.include.removeListener('remove', this._onRuleUpdate);
+    if (this._style)
+      detach(this._style);
 
     for each (let rule in this.include)
       this.include.remove(rule);
     pageModManager.remove(this._public);
-    this._loadingWindows = [];
-
   },
 
-  _loadingWindows: [],
-
   _applyOnExistingDocuments: function _applyOnExistingDocuments() {
     let mod = this;
     // Returns true if the tab match one rule
     function isMatchingURI(uri) {
       // Use Array.some as `include` isn't a native array
       return Array.some(mod.include, function (rule) {
         return RULES[rule].test(uri);
       });
@@ -211,16 +186,19 @@ const PageMod = Loader.compose(EventEmit
     let isTopDocument = window.top === window;
     // Is a top level document and `top` is not set, ignore
     if (isTopDocument && !has(this.attachTo, "top"))
       return;
     // Is a frame document and `frame` is not set, ignore
     if (!isTopDocument && !has(this.attachTo, "frame"))
       return;
 
+    if (this._style)
+      attach(this._style, window);
+
     // Immediatly evaluate content script if the document state is already
     // matching contentScriptWhen expectations
     let state = window.document.readyState;
     if ('start' === this.contentScriptWhen ||
         // Is `load` event already dispatched?
         'complete' === state ||
         // Is DOMContentLoaded already dispatched and waiting for it?
         ('ready' === this.contentScriptWhen && state === 'interactive') ) {
@@ -256,69 +234,16 @@ const PageMod = Loader.compose(EventEmit
     pageModManager.on(url, this._onContent);
   },
   _onRuleRemove: function _onRuleRemove(url) {
     pageModManager.off(url, this._onContent);
   },
   _onUncaughtError: function _onUncaughtError(e) {
     if (this._listeners('error').length == 1)
       console.exception(e);
-  },
-  _onRuleUpdate: function _onRuleUpdate(){
-    this._registerStyleSheet();
-  },
-
-  _registerStyleSheet : function _registerStyleSheet() {
-    let rules = this.include;
-    let styleRules = this._styleRules;
-
-    let documentRules = [];
-
-    this._unregisterStyleSheet();
-
-    for each (let rule in rules) {
-      let pattern = RULES[rule];
-
-      if (!pattern)
-        continue;
-
-      if (pattern.regexp)
-        documentRules.push("regexp(\"" + pattern.regexp.source + "\")");
-      else if (pattern.exactURL)
-        documentRules.push("url(" + pattern.exactURL + ")");
-      else if (pattern.domain)
-        documentRules.push("domain(" + pattern.domain + ")");
-      else if (pattern.urlPrefix)
-        documentRules.push("url-prefix(" + pattern.urlPrefix + ")");
-      else if (pattern.anyWebPage)
-        documentRules.push("regexp(\"^(https?|ftp)://.*?\")");
-    }
-
-    let uri = "data:text/css;charset=utf-8,";
-    if (documentRules.length > 0)
-      uri += encodeURIComponent("@-moz-document " +
-        documentRules.join(",") + " {" + styleRules + "}");
-    else
-      uri += encodeURIComponent(styleRules);
-
-    this._registeredStyleURI = io.newURI(uri, null, null);
-
-    styleSheetService.loadAndRegisterSheet(
-      this._registeredStyleURI,
-      USER_SHEET
-    );
-  },
-
-  _unregisterStyleSheet : function () {
-    let uri = this._registeredStyleURI;
-
-    if (uri  && styleSheetService.sheetRegistered(uri, USER_SHEET))
-      styleSheetService.unregisterSheet(uri, USER_SHEET);
-
-    this._registeredStyleURI = null;
   }
 });
 exports.PageMod = function(options) PageMod(options)
 exports.PageMod.prototype = PageMod.prototype;
 
 const PageModManager = Registry.resolve({
   constructor: '_init',
   _destructor: '_registryDestructor'
--- a/addon-sdk/source/lib/sdk/page-mod/match-pattern.js
+++ b/addon-sdk/source/lib/sdk/page-mod/match-pattern.js
@@ -91,18 +91,25 @@ MatchPattern.prototype = {
     if (this.regexp && this.regexp.test(urlStr) &&
         this.regexp.exec(urlStr)[0] == urlStr)
       return true;
 
     if (this.anyWebPage && /^(https?|ftp)$/.test(url.scheme))
       return true;
     if (this.exactURL && this.exactURL == urlStr)
       return true;
+
+    // Tests the urlStr against domain and check if
+    // wildcard submitted (*.domain.com), it only allows
+    // subdomains (sub.domain.com) or from the root (http://domain.com)
+    // and reject non-matching domains (otherdomain.com)
+    // bug 856913
     if (this.domain && url.host &&
-        url.host.slice(-this.domain.length) == this.domain)
+         (url.host === this.domain ||
+          url.host.slice(-this.domain.length - 1) === "." + this.domain))
       return true;
     if (this.urlPrefix && 0 == urlStr.indexOf(this.urlPrefix))
       return true;
 
     return false;
   }
 
 };
--- a/addon-sdk/source/lib/sdk/panel.js
+++ b/addon-sdk/source/lib/sdk/panel.js
@@ -9,410 +9,243 @@ module.metadata = {
   "stability": "stable",
   "engines": {
     "Firefox": "*"
   }
 };
 
 const { Ci } = require("chrome");
 const { validateOptions: valid } = require('./deprecated/api-utils');
-const { Symbiont } = require('./content/content');
-const { EventEmitter } = require('./deprecated/events');
 const { setTimeout } = require('./timers');
-const { on, off, emit } = require('./system/events');
-const runtime = require('./system/runtime');
-const { getDocShell } = require("./frame/utils");
-const { getWindow } = require('./panel/window');
 const { isPrivateBrowsingSupported } = require('./self');
 const { isWindowPBSupported } = require('./private-browsing/utils');
-const { getNodeView } = require('./view/core');
+const { Class } = require("./core/heritage");
+const { merge } = require("./util/object");
+const { WorkerHost, Worker, detach, attach } = require("./worker/utils");
+const { Disposable } = require("./core/disposable");
+const { contract: loaderContract } = require("./content/loader");
+const { contract } = require("./util/contract");
+const { on, off, emit, setListeners } = require("./event/core");
+const { EventTarget } = require("./event/target");
+const domPanel = require("./panel/utils");
+const { events } = require("./panel/events");
+const systemEvents = require("./system/events");
+const { filter, pipe } = require("./event/utils");
+const { getNodeView } = require("./view/core");
+
+if (isPrivateBrowsingSupported && isWindowPBSupported)
+  throw Error('The panel module cannot be used with per-window private browsing at the moment, see Bug 816257');
+
+let isArray = Array.isArray;
+let assetsURI = require("./self").data.url();
 
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
-      ON_SHOW = 'popupshown',
-      ON_HIDE = 'popuphidden',
-      validNumber = { is: ['number', 'undefined', 'null'] },
-      validBoolean = { is: ['boolean', 'undefined', 'null'] },
-      ADDON_ID = require('./self').id;
+function isAddonContent({ contentURL }) {
+  return typeof(contentURL) === "string" && contentURL.indexOf(assetsURI) === 0;
+}
+
+function hasContentScript({ contentScript, contentScriptFile }) {
+  return (isArray(contentScript) ? contentScript.length > 0 :
+         !!contentScript) ||
+         (isArray(contentScriptFile) ? contentScriptFile.length > 0 :
+         !!contentScriptFile);
+}
 
-if (isPrivateBrowsingSupported && isWindowPBSupported) {
-  throw Error('The panel module cannot be used with per-window private browsing at the moment, see Bug 816257');
+function requiresAddonGlobal(model) {
+  return isAddonContent(model) && !hasContentScript(model);
+}
+
+function getAttachEventType(model) {
+  let when = model.contentScriptWhen;
+  return requiresAddonGlobal(model) ? "sdk-panel-content-changed" :
+         when === "start" ? "sdk-panel-content-changed" :
+         when === "end" ? "sdk-panel-document-loaded" :
+         when === "ready" ? "sdk-panel-content-loaded" :
+         null;
 }
 
-/**
- * Emits show and hide events.
- */
-const Panel = Symbiont.resolve({
-  constructor: '_init',
-  _onInit: '_onSymbiontInit',
-  destroy: '_symbiontDestructor',
-  _documentUnload: '_workerDocumentUnload'
-}).compose({
-  _frame: Symbiont.required,
-  _init: Symbiont.required,
-  _onSymbiontInit: Symbiont.required,
-  _symbiontDestructor: Symbiont.required,
-  _emit: Symbiont.required,
-  on: Symbiont.required,
-  removeListener: Symbiont.required,
+
+let number = { is: ['number', 'undefined', 'null'] };
+let boolean = { is: ['boolean', 'undefined', 'null'] };
 
-  _inited: false,
+let panelContract = contract(merge({
+  width: number,
+  height: number,
+  focus: boolean,
+}, loaderContract.rules));
+
+
+function isDisposed(panel) !views.has(panel);
+
+let panels = new WeakMap();
+let models = new WeakMap();
+let views = new WeakMap();
+let workers = new WeakMap();
 
-  /**
-   * If set to `true` frame loaders between xul panel frame and
-   * hidden frame are swapped. If set to `false` frame loaders are
-   * set back to normal. Setting the value that was already set will
-   * have no effect.
-   */
-  set _frameLoadersSwapped(value) {
-    if (this.__frameLoadersSwapped == value) return;
-    this._frame.QueryInterface(Ci.nsIFrameLoaderOwner)
-      .swapFrameLoaders(this._viewFrame);
-    this.__frameLoadersSwapped = value;
-  },
-  __frameLoadersSwapped: false,
+function viewFor(panel) views.get(panel)
+exports.viewFor = viewFor;
 
-  constructor: function Panel(options) {
-    this._onShow = this._onShow.bind(this);
-    this._onHide = this._onHide.bind(this);
-    this._onAnyPanelShow = this._onAnyPanelShow.bind(this);
-    on('sdk-panel-show', this._onAnyPanelShow);
+function modelFor(panel) models.get(panel)
+function panelFor(view) panels.get(view)
+function workerFor(panel) workers.get(panel)
 
-    this.on('inited', this._onSymbiontInit.bind(this));
-    this.on('propertyChange', this._onChange.bind(this));
+// Utility function takes `panel` instance and makes sure it will be
+// automatically hidden as soon as other panel is shown.
+let setupAutoHide = new function() {
+  let refs = new WeakMap();
 
-    options = options || {};
-    if ('onShow' in options)
-      this.on('show', options.onShow);
-    if ('onHide' in options)
-      this.on('hide', options.onHide);
-    if ('width' in options)
-      this.width = options.width;
-    if ('height' in options)
-      this.height = options.height;
-    if ('contentURL' in options)
-      this.contentURL = options.contentURL;
-    if ('focus' in options) {
-      var value = options.focus;
-      var validatedValue = valid({ $: value }, { $: validBoolean }).$;
-      this._focus =
-        (typeof validatedValue == 'boolean') ? validatedValue : this._focus;
+  return function setupAutoHide(panel) {
+    // Create system event listener that reacts to any panel showing and
+    // hides given `panel` if it's not the one being shown.
+    function listener({subject}) {
+      // It could be that listener is not GC-ed in the same cycle as
+      // panel in such case we remove listener manually.
+      let view = viewFor(panel);
+      if (!view) systemEvents.off("sdk-panel-show", listener);
+      else if (subject !== view) panel.hide();
     }
 
-    this._init(options);
+    // system event listener is intentionally weak this way we'll allow GC
+    // to claim panel if it's no longer referenced by an add-on code. This also
+    // helps minimizing cleanup required on unload.
+    systemEvents.on("sdk-panel-show", listener);
+    // To make sure listener is not claimed by GC earlier than necessary we
+    // associate it with `panel` it's associated with. This way it won't be
+    // GC-ed earlier than `panel` itself.
+    refs.set(panel, listener);
+  }
+}
+
+const Panel = Class({
+  implements: [
+    // Generate accessors for the validated properties that update model on
+    // set and return values from model on get.
+    panelContract.properties(modelFor),
+    EventTarget,
+    Disposable
+  ],
+  extends: WorkerHost(workerFor),
+  setup: function setup(options) {
+    let model = merge({
+      width: 320,
+      height: 240,
+      focus: true,
+    }, panelContract(options));
+    models.set(this, model);
+
+    // Setup listeners.
+    setListeners(this, options);
+
+    // Setup view
+    let view = domPanel.make();
+    panels.set(view, this);
+    views.set(this, view);
+
+    // Load panel content.
+    domPanel.setURL(view, model.contentURL);
+
+    setupAutoHide(this);
+
+    let worker = new Worker(options);
+    workers.set(this, worker);
+
+    // pipe events from worker to a panel.
+    pipe(worker, this);
   },
-  _destructor: function _destructor() {
+  dispose: function dispose() {
     this.hide();
-    this._removeAllListeners('show');
-    this._removeAllListeners('hide');
-    this._removeAllListeners('propertyChange');
-    this._removeAllListeners('inited');
-    off('sdk-panel-show', this._onAnyPanelShow);
-    // defer cleanup to be performed after panel gets hidden
-    this._xulPanel = null;
-    this._symbiontDestructor(this);
-    this._removeAllListeners();
-  },
-  destroy: function destroy() {
-    this._destructor();
+    off(this);
+
+    detach(workerFor(this));
+
+    domPanel.dispose(viewFor(this));
+
+    // Release circular reference between view and panel instance. This
+    // way view will be GC-ed. And panel as well once all the other refs
+    // will be removed from it.
+    views.delete(this);
   },
   /* Public API: Panel.width */
-  get width() this._width,
-  set width(value)
-    this._width = valid({ $: value }, { $: validNumber }).$ || this._width,
-  _width: 320,
+  get width() modelFor(this).width,
+  set width(value) this.resize(value, this.height),
   /* Public API: Panel.height */
-  get height() this._height,
-  set height(value)
-    this._height =  valid({ $: value }, { $: validNumber }).$ || this._height,
-  _height: 240,
+  get height() modelFor(this).height,
+  set height(value) this.resize(this.width, value),
+
   /* Public API: Panel.focus */
-  get focus() this._focus,
-  _focus: true,
+  get focus() modelFor(this).focus,
+
+  get contentURL() modelFor(this).contentURL,
+  set contentURL(value) {
+    let model = modelFor(this);
+    model.contentURL = panelContract({ contentURL: value }).contentURL;
+    domPanel.setURL(viewFor(this), model.contentURL);
+  },
 
   /* Public API: Panel.isShowing */
-  get isShowing() !!this._xulPanel && this._xulPanel.state == "open",
+  get isShowing() !isDisposed(this) && domPanel.isOpen(viewFor(this)),
 
   /* Public API: Panel.show */
   show: function show(anchor) {
-    anchor = anchor ? getNodeView(anchor) : null;
-    let anchorWindow = getWindow(anchor);
-
-    // If there is no open window, or the anchor is in a private window
-    // then we will not be able to display the panel
-    if (!anchorWindow) {
-      return;
-    }
-
-    let document = anchorWindow.document;
-    let xulPanel = this._xulPanel;
-    let panel = this;
-    if (!xulPanel) {
-      xulPanel = this._xulPanel = document.createElementNS(XUL_NS, 'panel');
-      xulPanel.setAttribute("type", "arrow");
-
-      // One anonymous node has a big padding that doesn't work well with
-      // Jetpack, as we would like to display an iframe that completely fills
-      // the panel.
-      // -> Use a XBL wrapper with inner stylesheet to remove this padding.
-      let css = ".panel-inner-arrowcontent, .panel-arrowcontent {padding: 0;}";
-      let originalXBL = "chrome://global/content/bindings/popup.xml#arrowpanel";
-      let binding =
-      '<bindings xmlns="http://www.mozilla.org/xbl">' +
-        '<binding id="id" extends="' + originalXBL + '">' +
-          '<resources>' +
-            '<stylesheet src="data:text/css;charset=utf-8,' +
-              document.defaultView.encodeURIComponent(css) + '"/>' +
-          '</resources>' +
-        '</binding>' +
-      '</bindings>';
-      xulPanel.style.MozBinding = 'url("data:text/xml;charset=utf-8,' +
-        document.defaultView.encodeURIComponent(binding) + '")';
-
-      let frame = document.createElementNS(XUL_NS, 'iframe');
-      frame.setAttribute('type', 'content');
-      frame.setAttribute('flex', '1');
-      frame.setAttribute('transparent', 'transparent');
-
-      if (runtime.OS === "Darwin") {
-        frame.style.borderRadius = "6px";
-        frame.style.padding = "1px";
-      }
-
-      // Load an empty document in order to have an immediatly loaded iframe,
-      // so swapFrameLoaders is going to work without having to wait for load.
-      frame.setAttribute("src","data:;charset=utf-8,");
-
-      xulPanel.appendChild(frame);
-      document.getElementById("mainPopupSet").appendChild(xulPanel);
-    }
-    let { width, height, focus } = this, x, y, position;
+    let model = modelFor(this);
+    let view = viewFor(this);
+    let anchorView = getNodeView(anchor);
 
-    if (!anchor) {
-      // Open the popup in the middle of the window.
-      x = document.documentElement.clientWidth / 2 - width / 2;
-      y = document.documentElement.clientHeight / 2 - height / 2;
-      position = null;
-    }
-    else {
-      // Open the popup by the anchor.
-      let rect = anchor.getBoundingClientRect();
-
-      let window = anchor.ownerDocument.defaultView;
-
-      let zoom = window.mozScreenPixelsPerCSSPixel;
-      let screenX = rect.left + window.mozInnerScreenX * zoom;
-      let screenY = rect.top + window.mozInnerScreenY * zoom;
-
-      // Set up the vertical position of the popup relative to the anchor
-      // (always display the arrow on anchor center)
-      let horizontal, vertical;
-      if (screenY > window.screen.availHeight / 2 + height)
-        vertical = "top";
-      else
-        vertical = "bottom";
-
-      if (screenY > window.screen.availWidth / 2 + width)
-        horizontal = "left";
-      else
-        horizontal = "right";
-
-      let verticalInverse = vertical == "top" ? "bottom" : "top";
-      position = vertical + "center " + verticalInverse + horizontal;
+    if (!isDisposed(this))
+      domPanel.show(view, model.width, model.height, model.focus, anchorView);
 
-      // Allow panel to flip itself if the panel can't be displayed at the
-      // specified position (useful if we compute a bad position or if the
-      // user moves the window and panel remains visible)
-      xulPanel.setAttribute("flip","both");
-    }
-
-    // Resize the iframe instead of using panel.sizeTo
-    // because sizeTo doesn't work with arrow panels
-    xulPanel.firstChild.style.width = width + "px";
-    xulPanel.firstChild.style.height = height + "px";
-
-    // Only display xulPanel if Panel.hide() was not called
-    // after Panel.show(), but before xulPanel.openPopup
-    // was loaded
-    emit('sdk-panel-show', { data: ADDON_ID, subject: xulPanel });
+    return this;
+  },
 
-    // Prevent the panel from getting focus when showing up
-    // if focus is set to false
-    xulPanel.setAttribute("noautofocus",!focus);
-
-    // Wait for the XBL binding to be constructed
-    function waitForBinding() {
-      if (!xulPanel.openPopup) {
-        setTimeout(waitForBinding, 50);
-        return;
-      }
-
-      if (xulPanel.state !== 'hiding') {
-        xulPanel.openPopup(anchor, position, x, y);
-      }
-    }
-    waitForBinding();
-
-    return this._public;
-  },
   /* Public API: Panel.hide */
   hide: function hide() {
-    // The popuphiding handler takes care of swapping back the frame loaders
-    // and removing the XUL panel from the application window, we just have to
-    // trigger it by hiding the popup.
-    // XXX Sometimes I get "TypeError: xulPanel.hidePopup is not a function"
-    // when quitting the host application while a panel is visible.  To suppress
-    // them, this now checks for "hidePopup" in xulPanel before calling it.
-    // It's not clear if there's an actual issue or the error is just normal.
-    let xulPanel = this._xulPanel;
-    if (xulPanel && "hidePopup" in xulPanel)
-      xulPanel.hidePopup();
-    return this._public;
+    // Quit immediately if panel is disposed or there is no state change.
+    domPanel.close(viewFor(this));
+
+    return this;
   },
 
   /* Public API: Panel.resize */
   resize: function resize(width, height) {
-    this.width = width;
-    this.height = height;
-    // Resize the iframe instead of using panel.sizeTo
-    // because sizeTo doesn't work with arrow panels
-    let xulPanel = this._xulPanel;
-    if (xulPanel) {
-      xulPanel.firstChild.style.width = width + "px";
-      xulPanel.firstChild.style.height = height + "px";
-    }
-  },
-
-  // While the panel is visible, this is the XUL <panel> we use to display it.
-  // Otherwise, it's null.
-  get _xulPanel() this.__xulPanel,
-  set _xulPanel(value) {
-    let xulPanel = this.__xulPanel;
-    if (value === xulPanel) return;
-    if (xulPanel) {
-      xulPanel.removeEventListener(ON_HIDE, this._onHide, false);
-      xulPanel.removeEventListener(ON_SHOW, this._onShow, false);
-      xulPanel.parentNode.removeChild(xulPanel);
-    }
-    if (value) {
-      value.addEventListener(ON_HIDE, this._onHide, false);
-      value.addEventListener(ON_SHOW, this._onShow, false);
-    }
-    this.__xulPanel = value;
-  },
-  __xulPanel: null,
-  get _viewFrame() this.__xulPanel.children[0],
-  /**
-   * When the XUL panel becomes hidden, we swap frame loaders back to move
-   * the content of the panel to the hidden frame & remove panel element.
-   */
-  _onHide: function _onHide() {
-    try {
-      this._frameLoadersSwapped = false;
-      this._xulPanel = null;
-      this._emit('hide');
-    } catch(e) {
-      this._emit('error', e);
-    }
-  },
-
-  /**
-   * Retrieve computed text color style in order to apply to the iframe
-   * document. As MacOS background is dark gray, we need to use skin's
-   * text color.
-   */
-  _applyStyleToDocument: function _applyStyleToDocument() {
-    if (this._defaultStyleApplied)
-      return;
-    try {
-      let win = this._xulPanel.ownerDocument.defaultView;
-      let node = win.document.getAnonymousElementByAttribute(
-        this._xulPanel, "class", "panel-arrowcontent");
-      if (!node) {
-        // Before bug 764755, anonymous content was different:
-        // TODO: Remove this when targeting FF16+
-        node = win.document.getAnonymousElementByAttribute(
-          this._xulPanel, "class", "panel-inner-arrowcontent");
-      }
-      let textColor = win.getComputedStyle(node).getPropertyValue("color");
-      let doc = this._xulPanel.firstChild.contentDocument;
-      let style = doc.createElement("style");
-      style.textContent = "body { color: " + textColor + "; }";
-      let container = doc.head ? doc.head : doc.documentElement;
+    let model = modelFor(this);
+    let view = viewFor(this);
+    let change = panelContract({
+      width: width || model.width,
+      height: height || model.height
+    });
 
-      if (container.firstChild)
-        container.insertBefore(style, container.firstChild);
-      else
-        container.appendChild(style);
-      this._defaultStyleApplied = true;
-    }
-    catch(e) {
-      console.error("Unable to apply panel style");
-      console.exception(e);
-    }
-  },
-
-  /**
-   * When the XUL panel becomes shown, we swap frame loaders between panel
-   * frame and hidden frame to preserve state of the content dom.
-   */
-  _onShow: function _onShow() {
-    try {
-      if (!this._inited) { // defer if not initialized yet
-        this.on('inited', this._onShow.bind(this));
-      } else {
-        this._frameLoadersSwapped = true;
-        this._applyStyleToDocument();
-        this._emit('show');
-      }
-    } catch(e) {
-      this._emit('error', e);
-    }
-  },
+    model.width = change.width
+    model.height = change.height
 
-  /**
-   * When any panel is displayed, system-wide, close `this`
-   * panel unless it's the most recently displayed panel
-   */
-  _onAnyPanelShow: function _onAnyPanelShow(e) {
-    if (e.subject !== this._xulPanel)
-      this.hide();
-  },
-
-  /**
-   * Notification that panel was fully initialized.
-   */
-  _onInit: function _onInit() {
-    this._inited = true;
-
-    // Avoid panel document from resizing the browser window
-    // New platform capability added through bug 635673
-    let docShell = getDocShell(this._frame);
-    if (docShell && "allowWindowControl" in docShell)
-      docShell.allowWindowControl = false;
+    domPanel.resize(view, model.width, model.height);
 
-    // perform all deferred tasks like initSymbiont, show, hide ...
-    // TODO: We're publicly exposing a private event here; this
-    // 'inited' event should really be made private, somehow.
-    this._emit('inited');
-  },
-
-  // Catch document unload event in order to rebind load event listener with
-  // Symbiont._initFrame if Worker._documentUnload destroyed the worker
-  _documentUnload: function(subject, topic, data) {
-    if (this._workerDocumentUnload(subject, topic, data)) {
-      this._initFrame(this._frame);
-      return true;
-    }
-    return false;
-  },
-
-  _onChange: function _onChange(e) {
-    this._frameLoadersSwapped = false;
-    if ('contentURL' in e && this._frame) {
-      // Cleanup the worker before injecting the content script in the new
-      // document
-      this._workerCleanup();
-      this._initFrame(this._frame);
-    }
+    return this;
   }
 });
-exports.Panel = function(options) Panel(options)
-exports.Panel.prototype = Panel.prototype;
+exports.Panel = Panel;
+
+// Filter panel events to only panels that are create by this module.
+let panelEvents = filter(function({target}) panelFor(target), events);
+
+// Panel events emitted after panel has being shown.
+let shows = filter(function({type}) type === "sdk-panel-shown", panelEvents);
+
+// Panel events emitted after panel became hidden.
+let hides = filter(function({type}) type === "sdk-panel-hidden", panelEvents);
+
+// Panel events emitted after content inside panel is ready. For different
+// panels ready may mean different state based on `contentScriptWhen` attribute.
+// Weather given event represents readyness is detected by `getAttachEventType`
+// helper function.
+let ready = filter(function({type, target})
+  getAttachEventType(modelFor(panelFor(target))) === type, panelEvents);
+
+// Panel events emitted after content document in the panel has changed.
+let change = filter(function({type}) type === "sdk-panel-content-changed",
+                    panelEvents);
+
+// Forward panel show / hide events to panel's own event listeners.
+on(shows, "data", function({target}) emit(panelFor(target), "show"));
+on(hides, "data", function({target}) emit(panelFor(target), "hide"));
+
+on(ready, "data", function({target}) {
+  let worker = workerFor(panelFor(target));
+  attach(worker, domPanel.getContentDocument(target).defaultView);
+});
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/panel/events.js
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// This module basically translates system/events to a SDK standard events
+// so that `map`, `filter` and other utilities could be used with them.
+
+module.metadata = {
+  "stability": "experimental"
+};
+
+const events = require("../system/events");
+const { emit } = require("../event/core");
+
+let channel = {};
+
+function forward({ subject, type, data })
+  emit(channel, "data", { target: subject, type: type, data: data });
+
+["sdk-panel-show", "sdk-panel-hide", "sdk-panel-shown",
+ "sdk-panel-hidden", "sdk-panel-content-changed", "sdk-panel-content-loaded",
+ "sdk-panel-document-loaded"
+].forEach(function(type) events.on(type, forward));
+
+exports.events = channel;
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/panel/utils.js
@@ -0,0 +1,322 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+module.metadata = {
+  "stability": "unstable"
+};
+
+const { Cc, Ci } = require("chrome");
+const { setTimeout } = require("../timers");
+const { platform } = require("../system");
+const { getMostRecentBrowserWindow, getOwnerBrowserWindow,
+        getHiddenWindow } = require("../window/utils");
+const { create: createFrame, swapFrameLoaders } = require("../frame/utils");
+const { window: addonWindow } = require("../addon/window");
+const events = require("../system/events");
+
+
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+function open(panel, width, height, anchor) {
+  // Wait for the XBL binding to be constructed
+  if (!panel.openPopup) setTimeout(open, 50, panel, width, height, anchor);
+  else display(panel, width, height, anchor);
+}
+exports.open = open;
+
+function isOpen(panel) {
+  return panel.state === "open"
+}
+exports.isOpen = isOpen;
+
+
+function close(panel) {
+  // Sometimes "TypeError: panel.hidePopup is not a function" is thrown
+  // when quitting the host application while a panel is visible.  To suppress
+  // these errors, check for "hidePopup" in panel before calling it.
+  // It's not clear if there's an issue or it's expected behavior.
+
+  return panel.hidePopup && panel.hidePopup();
+}
+exports.close = close
+
+
+function resize(panel, width, height) {
+  // Resize the iframe instead of using panel.sizeTo
+  // because sizeTo doesn't work with arrow panels
+  panel.firstChild.style.width = width + "px";
+  panel.firstChild.style.height = height + "px";
+}
+exports.resize = resize
+
+function display(panel, width, height, anchor) {
+  let document = panel.ownerDocument;
+  let x = null;
+  let y = null;
+  let position = null;
+
+  // Panel XBL has some SDK incompatible styling decisions. We shim panel
+  // instances until proper fix for Bug 859504 is shipped.
+  shimDefaultStyle(panel);
+
+  if (!anchor) {
+    // Open the popup in the middle of the window.
+    x = document.documentElement.clientWidth / 2 - width / 2;
+    y = document.documentElement.clientHeight / 2 - height / 2;
+    position = null;
+  }
+  else {
+    // Open the popup by the anchor.
+    let rect = anchor.getBoundingClientRect();
+
+    let window = anchor.ownerDocument.defaultView;
+
+    let zoom = window.mozScreenPixelsPerCSSPixel;
+    let screenX = rect.left + window.mozInnerScreenX * zoom;
+    let screenY = rect.top + window.mozInnerScreenY * zoom;
+
+    // Set up the vertical position of the popup relative to the anchor
+    // (always display the arrow on anchor center)
+    let horizontal, vertical;
+    if (screenY > window.screen.availHeight / 2 + height)
+      vertical = "top";
+    else
+      vertical = "bottom";
+
+    if (screenY > window.screen.availWidth / 2 + width)
+      horizontal = "left";
+    else
+      horizontal = "right";
+
+    let verticalInverse = vertical == "top" ? "bottom" : "top";
+    position = vertical + "center " + verticalInverse + horizontal;
+
+    // Allow panel to flip itself if the panel can't be displayed at the
+    // specified position (useful if we compute a bad position or if the
+    // user moves the window and panel remains visible)
+    panel.setAttribute("flip", "both");
+  }
+
+  // Resize the iframe instead of using panel.sizeTo
+  // because sizeTo doesn't work with arrow panels
+  panel.firstChild.style.width = width + "px";
+  panel.firstChild.style.height = height + "px";
+
+  panel.openPopup(anchor, position, x, y);
+}
+exports.display = display;
+
+// This utility function is just a workaround until Bug 859504 has shipped.
+function shimDefaultStyle(panel) {
+  let document = panel.ownerDocument;
+  // Please note that `panel` needs to be part of document in order to reach
+  // it's anonymous nodes. One of the anonymous node has a big padding which
+  // doesn't work well since panel frame needs to fill all of the panel.
+  // XBL binding is a not the best option as it's applied asynchronously, and
+  // makes injected frames behave in strange way. Also this feels a lot
+  // cheaper to do.
+  ["panel-inner-arrowcontent", "panel-arrowcontent"].forEach(function(value) {
+    let node = document.getAnonymousElementByAttribute(panel, "class", value);
+      if (node) node.style.padding = 0;
+  });
+}
+
+function show(panel, width, height, focus, anchor) {
+  // Prevent the panel from getting focus when showing up
+  // if focus is set to false
+  panel.setAttribute("noautofocus", !focus);
+
+
+  let window = anchor && getOwnerBrowserWindow(anchor);
+  let { document } = window ? window : getMostRecentBrowserWindow();
+  attach(panel, document);
+  open(panel, width, height, anchor);
+}
+exports.show = show
+
+function setupPanelFrame(frame) {
+  frame.setAttribute("flex", 1);
+  frame.setAttribute("transparent", "transparent");
+  frame.setAttribute("showcaret", true);
+  frame.setAttribute("autocompleteenabled", true);
+  if (platform === "darwin") {
+    frame.style.borderRadius = "6px";
+    frame.style.padding = "1px";
+  }
+}
+
+let EVENT_NAMES = {
+  "popupshowing": "sdk-panel-show",
+  "popuphiding": "sdk-panel-hide",
+  "popupshown": "sdk-panel-shown",
+  "popuphidden": "sdk-panel-hidden",
+  "document-element-inserted": "sdk-panel-content-changed",
+  "DOMContentLoaded": "sdk-panel-content-loaded",
+  "load": "sdk-panel-document-loaded"
+};
+
+function make(document) {
+  document = document || getMostRecentBrowserWindow().document;
+  let panel = document.createElementNS(XUL_NS, "panel");
+  panel.setAttribute("type", "arrow");
+
+  // Note that panel is a parent of `viewFrame` who's `docShell` will be
+  // configured at creation time. If `panel` and there for `viewFrame` won't
+  // have an owner document attempt to access `docShell` will throw. There
+  // for we attach panel to a document.
+  attach(panel, document);
+
+  let frameOptions =  {
+    allowJavascript: true,
+    allowPlugins: true,
+    allowAuth: true,
+    allowWindowControl: false,
+    // Need to override `nodeName` to use `iframe` as `browsers` save session
+    // history and in consequence do not dispatch "inner-window-destroyed"
+    // notifications.
+    browser: false,
+    // Note that use of this URL let's use swap frame loaders earlier
+    // than if we used default "about:blank".
+    uri: "data:text/plain;charset=utf-8,"
+  };
+
+  let backgroundFrame = createFrame(addonWindow, frameOptions);
+  setupPanelFrame(backgroundFrame);
+
+  let viewFrame = createFrame(panel, frameOptions);
+  setupPanelFrame(viewFrame);
+
+  function onDisplayChange({type}) {
+    try { swapFrameLoaders(backgroundFrame, viewFrame); }
+    catch(error) { console.exception(error); }
+    events.emit(EVENT_NAMES[type], { subject: panel });
+  }
+
+  function onContentReady({target, type}) {
+    if (target === getContentDocument(panel)) {
+      style(panel);
+      events.emit(EVENT_NAMES[type], { subject: panel });
+    }
+  }
+
+  function onContentLoad({target, type}) {
+    if (target === getContentDocument(panel))
+      events.emit(EVENT_NAMES[type], { subject: panel });
+  }
+
+  function onContentChange({subject, type}) {
+    let document = subject;
+    if (document === getContentDocument(panel) && document.defaultView)
+      events.emit(EVENT_NAMES[type], { subject: panel });
+  }
+
+  function onPanelStateChange({type}) {
+    events.emit(EVENT_NAMES[type], { subject: panel })
+  }
+
+  panel.addEventListener("popupshowing", onDisplayChange, false);
+  panel.addEventListener("popuphiding", onDisplayChange, false);
+  panel.addEventListener("popupshown", onPanelStateChange, false);
+  panel.addEventListener("popuphidden", onPanelStateChange, false);
+
+  // Panel content document can be either in panel `viewFrame` or in
+  // a `backgroundFrame` depending on panel state. Listeners are set
+  // on both to avoid setting and removing listeners on panel state changes.
+
+  panel.addEventListener("DOMContentLoaded", onContentReady, true);
+  backgroundFrame.addEventListener("DOMContentLoaded", onContentReady, true);
+
+  panel.addEventListener("load", onContentLoad, true);
+  backgroundFrame.addEventListener("load", onContentLoad, true);
+
+  events.on("document-element-inserted", onContentChange);
+
+
+  panel.backgroundFrame = backgroundFrame;
+
+  // Store event listener on the panel instance so that it won't be GC-ed
+  // while panel is alive.
+  panel.onContentChange = onContentChange;
+
+  return panel;
+}
+exports.make = make;
+
+function attach(panel, document) {
+  document = document || getMostRecentBrowserWindow().document;
+  let container = document.getElementById("mainPopupSet");
+  if (container !== panel.parentNode) {
+    detach(panel);
+    document.getElementById("mainPopupSet").appendChild(panel);
+  }
+}
+exports.attach = attach;
+
+function detach(panel) {
+  if (panel.parentNode) panel.parentNode.removeChild(panel);
+}
+exports.detach = detach;
+
+function dispose(panel) {
+  panel.backgroundFrame.parentNode.removeChild(panel.backgroundFrame);
+  panel.backgroundFrame = null;
+  events.off("document-element-inserted", panel.onContentChange);
+  panel.onContentChange = null;
+  detach(panel);
+}
+exports.dispose = dispose;
+
+function style(panel) {
+  /**
+  Injects default OS specific panel styles into content document that is loaded
+  into given panel. Optionally `document` of the browser window can be
+  given to inherit styles from it, by default it will use either panel owner
+  document or an active browser's document. It should not matter though unless
+  Firefox decides to style windows differently base on profile or mode like
+  chrome for example.
+  **/
+
+  try {
+    let document = panel.ownerDocument;
+    let contentDocument = getContentDocument(panel);
+    let window = document.defaultView;
+    let node = document.getAnonymousElementByAttribute(panel, "class",
+                                                       "panel-arrowcontent") ||
+               // Before bug 764755, anonymous content was different:
+               // TODO: Remove this when targeting FF16+
+                document.getAnonymousElementByAttribute(panel, "class",
+                                                        "panel-inner-arrowcontent");
+
+    let color = window.getComputedStyle(node).getPropertyValue("color");
+
+    let style = contentDocument.createElement("style");
+    style.id = "sdk-panel-style";
+    style.textContent = "body { color: " + color + "; }";
+
+    let container = contentDocument.head ? contentDocument.head :
+                    contentDocument.documentElement;
+
+    if (container.firstChild)
+      container.insertBefore(style, container.firstChild);
+    else
+      container.appendChild(style);
+  }
+  catch (error) {
+    console.error("Unable to apply panel style");
+    console.exception(error);
+  }
+}
+exports.style = style;
+
+function getContentFrame(panel) isOpen(panel) ? panel.firstChild :
+                                                panel.backgroundFrame
+exports.getContentFrame = getContentFrame;
+
+function getContentDocument(panel) getContentFrame(panel).contentDocument
+exports.getContentDocument = getContentDocument;
+
+function setURL(panel, url) getContentFrame(panel).setAttribute("src", url)
+exports.setURL = setURL;
--- a/addon-sdk/source/lib/sdk/request.js
+++ b/addon-sdk/source/lib/sdk/request.js
@@ -20,16 +20,18 @@ const { isValidURI } = require("./url.js
 
 const response = ns();
 const request = ns();
 
 // Instead of creating a new validator for each request, just make one and
 // reuse it.
 const { validateOptions, validateSingleOption } = new OptionsValidator({
   url: {
+    // Also converts a URL instance to string, bug 857902
+    map: function (url) url.toString(),
     ok: isValidURI
   },
   headers: {
     map: function (v) v || {},
     is:  ["object"],
   },
   content: {
     map: function (v) v || null,
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/stylesheet/style.js
@@ -0,0 +1,83 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=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/. */
+"use strict";
+
+module.metadata = {
+  "stability": "experimental"
+};
+
+const { Cc, Ci } = require("chrome");
+const { Class } = require("../core/heritage");
+const { ns } = require("../core/namespace");
+const { URL } = require('../url');
+const events = require("../system/events");
+const { loadSheet, removeSheet, isTypeValid } = require("./utils");
+const { isString } = require("../lang/type");
+const { attachTo, detachFrom, getTargetWindow } = require("../content/mod");
+
+const { freeze, create } = Object;
+const LOCAL_URI_SCHEMES = ['resource', 'data'];
+
+function isLocalURL(item) {
+  try {
+    return LOCAL_URI_SCHEMES.indexOf(URL(item).scheme) > -1;
+  }
+  catch(e) {}
+
+  return false;
+}
+
+function Style({ source, uri, type }) {
+  source = source == null ? null : freeze([].concat(source));
+  uri = uri == null ? null : freeze([].concat(uri));
+  type = type == null ? "author" : type;
+
+  if (source && !source.every(isString))
+    throw new Error('Style.source must be a string or an array of strings.');
+
+  if (uri && !uri.every(isLocalURL))
+    throw new Error('Style.uri must be a local URL or an array of local URLs');
+
+  if (type && !isTypeValid(type))
+    throw new Error('Style.type must be "agent", "user" or "author"');
+
+  return freeze(create(Style.prototype, {
+    "source": { value: source, enumerable: true },
+    "uri": { value: uri, enumerable: true },
+    "type": { value: type, enumerable: true }
+  }));
+};
+
+exports.Style = Style;
+
+attachTo.define(Style, function (style, window) {
+  if (style.uri) {
+    for (let uri of style.uri)
+      loadSheet(window, uri, style.type);
+  }
+
+  if (style.source) {
+    let uri = "data:text/css;charset=utf-8,";
+
+    uri += encodeURIComponent(style.source.join(""));
+
+    loadSheet(window, uri, style.type);
+  }
+});
+
+detachFrom.define(Style, function (style, window) {
+  if (style.uri)
+    for (let uri of style.uri)
+      removeSheet(window, uri);
+
+  if (style.source) {
+    let uri = "data:text/css;charset=utf-8,";
+
+    uri += encodeURIComponent(style.source.join(""));
+
+    removeSheet(window, uri, style.type);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/stylesheet/utils.js
@@ -0,0 +1,79 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+module.metadata =  {
+  "stability": "experimental"
+};
+
+const { Cc, Ci } = require("chrome");
+
+const io = Cc['@mozilla.org/network/io-service;1'].
+            getService(Ci.nsIIOService);
+
+const SHEET_TYPE = {
+  "agent": "AGENT_SHEET",
+  "user": "USER_SHEET",
+  "author": "AUTHOR_SHEET"
+};
+
+function getDOMWindowUtils(window) {
+  return window.QueryInterface(Ci.nsIInterfaceRequestor).
+                getInterface(Ci.nsIDOMWindowUtils);
+};
+
+/**
+ * Synchronously loads a style sheet from `uri` and adds it to the list of
+ * additional style sheets of the document.
+ * The sheets added takes effect immediately, and only on the document of the
+ * `window` given.
+ */
+function loadSheet(window, url, type) {
+  if (!(type && type in SHEET_TYPE))
+    type = "author";
+
+  type = SHEET_TYPE[type];
+
+  if (!(url instanceof Ci.nsIURI))
+    url = io.newURI(url, null, null);
+
+  let winUtils = getDOMWindowUtils(window);
+  try {
+    winUtils.loadSheet(url, winUtils[type]);
+  }
+  catch (e) {};
+};
+exports.loadSheet = loadSheet;
+
+/**
+ * Remove the document style sheet at `sheetURI` from the list of additional
+ * style sheets of the document.  The removal takes effect immediately.
+ */
+function removeSheet(window, url, type) {
+  if (!(type && type in SHEET_TYPE))
+    type = "author";
+
+  type = SHEET_TYPE[type];
+
+  if (!(url instanceof Ci.nsIURI))
+    url = io.newURI(url, null, null);
+
+  let winUtils = getDOMWindowUtils(window);
+
+  try {
+    winUtils.removeSheet(url, winUtils[type]);
+  }
+  catch (e) {};
+};
+exports.removeSheet = removeSheet;
+
+/**
+ * Returns `true` if the `type` given is valid, otherwise `false`.
+ * The values currently accepted are: "agent", "user" and "author".
+ */
+function isTypeValid(type) {
+  return type in SHEET_TYPE;
+}
+exports.isTypeValid = isTypeValid;
--- a/addon-sdk/source/lib/sdk/system/environment.js
+++ b/addon-sdk/source/lib/sdk/system/environment.js
@@ -36,17 +36,20 @@ exports.env = Proxy.create({
       configurable: true,   // Configurable as it may be deleted.
       writable: true        // Writable as we do support set.
     }
   },
 
   // New environment variables can be defined just by defining properties
   // on this object.
   defineProperty: function(name, { value }) set(name, value),
-  delete: function(name) set(name, null),
+  delete: function(name) {
+    set(name, null);
+    return true;
+  },
 
   // We present all properties as own, there for we just delegate to `hasOwn`.
   has: function(name) this.hasOwn(name),
   // We do support checks for existence of an environment variable, via `in`
   // operator on this object.
   hasOwn: function(name) exists(name),
 
   // On property get / set we do read / write appropriate environment variables,
--- a/addon-sdk/source/lib/sdk/util/array.js
+++ b/addon-sdk/source/lib/sdk/util/array.js
@@ -96,8 +96,20 @@ function fromIterator(iterator) {
   }
   else {
     for (let item of iterator)
       array.push(item);
   }
   return array;
 }
 exports.fromIterator = fromIterator;
+
+
+function find(array, predicate) {
+  var index = 0;
+  var count = array.length;
+  while (index < count) {
+    var value = array[index];
+    if (predicate(value)) return value;
+    else index = index + 1;
+  }
+}
+exports.find = find;
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/util/contract.js
@@ -0,0 +1,51 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+module.metadata = {
+  "stability": "unstable"
+};
+
+const { validateOptions: valid } = require("../deprecated/api-utils");
+
+// Function takes property validation rules and returns function that given
+// an `options` object will return validated / normalized options back. If
+// option(s) are invalid validator will throw exception described by rules.
+// Returned will also have contain `rules` property with a given validation
+// rules and `properties` function that can be used to generate validated
+// property getter and setters can be mixed into prototype. For more details
+// see `properties` function below.
+function contract(rules) {
+  function validator(options) {
+    return valid(options || {}, rules);
+  }
+  validator.rules = rules
+  validator.properties = function(modelFor) {
+    return properties(modelFor, rules);
+  }
+  return validator;
+}
+exports.contract = contract
+
+// Function takes `modelFor` instance state model accessor functions and
+// a property validation rules and generates object with getters and setters
+// that can be mixed into prototype. Property accessors update model for the
+// given instance. If you wish to react to property updates you can always
+// override setters to put specific logic.
+function properties(modelFor, rules) {
+  let descriptor = Object.keys(rules).reduce(function(descriptor, name) {
+    descriptor[name] = {
+      get: function() { return modelFor(this)[name] },
+      set: function(value) {
+        let change = {};
+        change[name] = value;
+        modelFor(this)[name] = valid(change, rules)[name];
+      }
+    }
+    return descriptor
+  }, {});
+  return Object.create(Object.prototype, descriptor);
+}
+exports.properties = properties
--- a/addon-sdk/source/lib/sdk/view/core.js
+++ b/addon-sdk/source/lib/sdk/view/core.js
@@ -5,42 +5,22 @@
 
 "use strict";
 
 module.metadata = {
   "stability": "unstable"
 };
 
 var { Ci } = require("chrome");
-
-/**
-Temporarily emulate method so we don't have to uplift whole method
-implementation.
-
 var method = require("method/core");
 
 // Returns DOM node associated with a view for
 // the given `value`. If `value` has no view associated
 // it returns `null`. You can implement this method for
 // this type to define what the result should be for it.
 let getNodeView = method("getNodeView");
 getNodeView.define(function(value) {
   if (value instanceof Ci.nsIDOMNode)
     return value;
   return null;
 });
-**/
-
-let implementations = new WeakMap();
-
-function getNodeView(value) {
-  if (value instanceof Ci.nsIDOMNode)
-    return value;
-  if (implementations.has(value))
-    return implementations.get(value)(value);
-
-  return null;
-}
-getNodeView.implement = function(value, implementation) {
-  implementations.set(value, implementation)
-}
 
 exports.getNodeView = getNodeView;
--- a/addon-sdk/source/lib/sdk/window/utils.js
+++ b/addon-sdk/source/lib/sdk/window/utils.js
@@ -335,8 +335,26 @@ function getFocusedElement() {
 exports.getFocusedElement = getFocusedElement;
 
 function getFrames(window) {
   return Array.slice(window.frames).reduce(function(frames, frame) {
     return frames.concat(frame, getFrames(frame))
   }, [])
 }
 exports.getFrames = getFrames;
+
+function getOwnerBrowserWindow(node) {
+  /**
+  Takes DOM node and returns browser window that contains it.
+  **/
+
+  let window = node.ownerDocument.defaultView.top;
+  // If anchored window is browser then it's target browser window.
+  if (isBrowser(window)) return window;
+  // Otherwise iterate over each browser window and find a one that
+  // contains browser for the anchored window document.
+  let document = window.document;
+  let browsers = windows("navigator:browser", { includePrivate: true });
+  return array.find(browsers, function isTargetBrowser(window) {
+    return !!window.gBrowser.getBrowserForDocument(document);
+  });
+}
+exports.getOwnerBrowserWindow = getOwnerBrowserWindow;
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/worker/utils.js
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+module.metadata = {
+  "stability": "unstable"
+};
+
+// This module attempts to hide trait based nature of the worker so that
+// code depending on workers could be de-trait-ified without changing worker
+// implementation.
+
+const { Worker: WorkerTrait } = require("../content/worker");
+const { Loader } = require("../content/loader");
+const { merge } = require("../util/object");
+const { emit } = require("../event/core");
+
+const LegacyWorker = WorkerTrait.resolve({
+  _setListeners: "__setListeners",
+}).compose(Loader, {
+  _setListeners: function() {},
+  attach: function(window) this._attach(window),
+  detach: function() this._workerCleanup()
+});
+
+// Weak map that stores mapping between regular worker instances and
+// legacy trait based worker instances.
+let traits = new WeakMap();
+
+function traitFor(worker) traits.get(worker, null);
+
+function WorkerHost(workerFor) {
+  // Define worker properties that just proxy to a wrapped trait.
+  return ["postMessage", "port", "url", "tab"].reduce(function(proto, name) {
+    Object.defineProperty(proto, name, {
+      enumerable: true,
+      configurable: false,
+      get: function() traitFor(workerFor(this))[name],
+      set: function(value) traitFor(workerFor(this))[name] = value
+    });
+    return proto;
+  }, {});
+}
+exports.WorkerHost = WorkerHost;
+
+// Type representing worker instance.
+function Worker(options) {
+  let worker = Object.create(Worker.prototype);
+  let trait = new LegacyWorker(options);
+  ["pageshow", "pagehide", "detach", "message", "error"].forEach(function(key) {
+    trait.on(key, function() {
+      emit.apply(emit, [worker, key].concat(Array.slice(arguments)));
+      // Workaround lack of ability to listen on all events by emulating
+      // such ability. This will become obsolete once Bug 821065 is fixed.
+      emit.apply(emit, [worker, "*", key].concat(Array.slice(arguments)));
+    });
+  });
+  traits.set(worker, trait);
+  return worker;
+}
+exports.Worker = Worker;
+
+function detach(worker) {
+  let trait = traitFor(worker);
+  if (trait) trait.detach();
+}
+exports.detach = detach;
+
+function attach(worker, window) {
+  let trait = traitFor(worker);
+  // Cleanup the worker before injecting the content script into a new document.
+  trait.attach(window);
+}
+exports.attach = attach;
--- a/addon-sdk/source/python-lib/cuddlefish/mobile-utils/bootstrap.js
+++ b/addon-sdk/source/python-lib/cuddlefish/mobile-utils/bootstrap.js
@@ -35,17 +35,17 @@ function startup(data, reason) {
     log("MU: console redirected to adb logcat.\n");
   } catch(e) {
     Cu.reportError("MU: unable to execute jsctype hack: "+e);
   }
 
   try {
     let QuitObserver = {
       observe: function (aSubject, aTopic, aData) {
-        Services.obs.removeObserver(QuitObserver, "quit-application", false);
+        Services.obs.removeObserver(QuitObserver, "quit-application");
         dump("MU: APPLICATION-QUIT\n");
       }
     };
     Services.obs.addObserver(QuitObserver, "quit-application", false);
     log("MU: ready to watch firefox exit.\n");
   } catch(e) {
     log("MU: unable to register quit-application observer: " + e + "\n");
   }
deleted file mode 100644
--- a/addon-sdk/source/test/addons/require/packages/panel/main.js
+++ /dev/null
@@ -1,5 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-exports.id = "panel-main";
deleted file mode 100644
--- a/addon-sdk/source/test/addons/require/packages/panel/package.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-  "id": "test-panel"
-}
\ No newline at end of file
deleted file mode 100644
--- a/addon-sdk/source/test/addons/require/packages/panel/page-mod.js
+++ /dev/null
@@ -1,5 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-exports.id = "page-mod";
deleted file mode 100644
--- a/addon-sdk/source/test/addons/require/panel.js
+++ /dev/null
@@ -1,5 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-exports.id = "local-panel";
--- a/addon-sdk/source/test/tabs/test-firefox-tabs.js
+++ b/addon-sdk/source/test/tabs/test-firefox-tabs.js
@@ -1035,57 +1035,41 @@ exports.testOnLoadEventWithImage = funct
       inBackground: true
     });
   });
 };
 
 exports.testOnPageShowEvent = function (test) {
   test.waitUntilDone();
 
-  let firstUrl = 'about:home';
-  let secondUrl = 'about:newtab';
+  let firstUrl = 'data:text/html;charset=utf-8,First';
+  let secondUrl = 'data:text/html;charset=utf-8,Second';
 
   openBrowserWindow(function(window, browser) {
     let tabs = require('sdk/tabs');
 
-    let wait = 500;
-    let counter = 1;
-    tabs.on('pageshow', function setup(tab, persisted) {
-      if (counter === 1)
+    let counter = 0;
+    tabs.on('pageshow', function onPageShow(tab, persisted) {
+      counter++;
+      if (counter === 1) {
         test.assert(!persisted, 'page should not be cached on initial load');
-
-      if (wait > 5000) {
-        test.fail('Page was not cached after 5s')
-        closeBrowserWindow(window, function() test.done());
+        tab.url = secondUrl;
       }
-
-      if (tab.url === firstUrl) {
-        // If first page has persisted, pass
-        if (persisted) {
-          tabs.removeListener('pageshow', setup);
-          test.pass('pageshow event called on history.back()');
-          closeBrowserWindow(window, function() test.done());
-        }
-        // On the first run, or if the page wasn't cached
-        // the first time due to not waiting long enough,
-        // try again with a longer delay (this is terrible
-        // and ugly)
-        else {
-          counter++;
-          timer.setTimeout(function () {
-            tab.url = secondUrl;
-            wait *= 2;
-          }, wait);
-        }
-      }
-      else {
+      else if (counter === 2) {
+        test.assert(!persisted, 'second test page should not be cached either');
         tab.attach({
           contentScript: 'setTimeout(function () { window.history.back(); }, 0)'
         });
       }
+      else {
+        test.assert(persisted, 'when we get back to the fist page, it has to' +
+                               'come from cache');
+        tabs.removeListener('pageshow', onPageShow);
+        closeBrowserWindow(window, function() test.done());
+      }
     });
 
     tabs.open({
       url: firstUrl
     });
   });
 };
 
--- a/addon-sdk/source/test/test-content-loader.js
+++ b/addon-sdk/source/test/test-content-loader.js
@@ -6,26 +6,16 @@
 const { Loader } = require('sdk/content/loader');
 const self = require("sdk/self");
 
 exports['test:contentURL'] = function(test) {
   let loader = Loader(),
       value, emitted = 0, changes = 0;
 
   test.assertRaises(
-    function() loader.contentURL = undefined,
-    'The `contentURL` option must be a valid URL.',
-    'Must throw an exception if `contentURL` is not URL.'
-  );
-   test.assertRaises(
-    function() loader.contentURL = null,
-    'The `contentURL` option must be a valid URL.',
-    'Must throw an exception if `contentURL` is not URL.'
-  );
-  test.assertRaises(
     function() loader.contentURL = 4,
     'The `contentURL` option must be a valid URL.',
     'Must throw an exception if `contentURL` is not URL.'
   );
  test.assertRaises(
     function() loader.contentURL = { toString: function() 'Oops' },
     'The `contentURL` option must be a valid URL.',
     'Must throw an exception if `contentURL` is not URL.'
--- a/addon-sdk/source/test/test-disposable.js
+++ b/addon-sdk/source/test/test-disposable.js
@@ -158,9 +158,34 @@ exports["test loader unloads do not affe
 
   assert.equal(disposals, 1, "1 destroy calls");
 
   loader2.unload();
 
   assert.equal(disposals, 2, "2 destroy calls");
 }
 
+exports["test disposables that throw"] = function(assert) {
+  let loader = Loader(module);
+  let { Disposable } = loader.require("sdk/core/disposable");
+
+  let disposals = 0
+
+  let Foo = Class({
+    extends: Disposable,
+    setup: function setup(a, b) {
+      throw Error("Boom!")
+    },
+    dispose: function dispose() {
+      disposals = disposals + 1
+    }
+  })
+
+  assert.throws(function() {
+    let foo1 = Foo()
+  }, /Boom/, "disposable constructors may throw");
+
+  loader.unload();
+
+  assert.equal(disposals, 0, "no disposal if constructor threw");
+}
+
 require('test').run(exports);
--- a/addon-sdk/source/test/test-environment.js
+++ b/addon-sdk/source/test/test-environment.js
@@ -36,15 +36,15 @@ exports['test set'] = function(assert) {
   assert.equal(env.BAZ3, undefined, 'BAZ3 is not set');
   env.BAZ3 = 'baz';
   assert.equal(env.BAZ3, get('BAZ3'), 'BAZ3 env variable is set');
   assert.equal(get('BAZ3'), 'baz', 'BAZ3 env variable was set to "baz"');
 };
 
 exports['test unset'] = function(assert) {
   env.BLA4 = 'bla';
-  assert.equal(env.BLA4, 'bla', 'BLA4 env varibale is set');
-  delete env.BLA4;
+  assert.equal(env.BLA4, 'bla', 'BLA4 env variable is set');
+  assert.equal(delete env.BLA4, true, 'BLA4 env variable is removed');
   assert.equal(env.BLA4, undefined, 'BLA4 env variable is unset');
   assert.equal('BLA4' in env, false, 'BLA4 env variable no longer exists' );
 };
 
 require('test').run(exports);
--- a/addon-sdk/source/test/test-match-pattern.js
+++ b/addon-sdk/source/test/test-match-pattern.js
@@ -28,16 +28,18 @@ exports.testMatchPatternTestTrue = funct
   ok("http://example.com/pickles/*", "http://example.com/pickles/");
   ok("http://example.com/pickles/*", "http://example.com/pickles/lemonade");
 
   ok("http://example.com", "http://example.com");
   ok("http://example.com/ice-cream", "http://example.com/ice-cream");
 
   ok(/.*zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753");
   ok(/https:.*zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753");
+  ok('*.sample.com', 'http://ex.sample.com/foo.html');
+  ok('*.amp.le.com', 'http://ex.amp.le.com');
 };
 
 exports.testMatchPatternTestFalse = function(test) {
   function ok(pattern, url) {
     let mp = new MatchPattern(pattern);
     test.assert(!mp.test(url), pattern + " should not match " + url);
   }
 
@@ -65,16 +67,24 @@ exports.testMatchPatternTestFalse = func
   ok("http://example.com", "");
   ok("http://example.com", "bogus");
   ok("http://example.com", "http://example.com/");
 
   ok(/zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753");
   ok(/.*zilla/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753");
   ok(/.*Zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=655464"); // bug 655464
   ok(/https:.*zilla/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753");
+
+  // bug 856913
+  ok('*.ign.com', 'http://www.design.com');
+  ok('*.ign.com', 'http://design.com');
+  ok('*.zilla.com', 'http://bugzilla.mozilla.com');
+  ok('*.zilla.com', 'http://mo-zilla.com');
+  ok('*.amp.le.com', 'http://amp-le.com');
+  ok('*.amp.le.com', 'http://examp.le.com');
 };
 
 exports.testMatchPatternErrors = function(test) {
   test.assertRaises(
     function() new MatchPattern("*.google.com/*"),
     /There can be at most one/,
     "MatchPattern throws when supplied multiple '*'"
   );
--- a/addon-sdk/source/test/test-page-mod.js
+++ b/addon-sdk/source/test/test-page-mod.js
@@ -551,17 +551,17 @@ exports.testAttachToTabsOnly = function(
         element.addEventListener('DOMContentLoaded', function onload() {
           element.removeEventListener('DOMContentLoaded', onload, false);
           hiddenFrames.remove(hiddenFrame);
 
           if (!xulApp.is("Fennec")) {
             openToplevelWindow();
           }
           else {
-            openBrowserIframe(); 
+            openBrowserIframe();
           }
         }, false);
         element.setAttribute('src', 'data:text/html;charset=utf-8,foo');
       }
     }));
   }
 
   function openToplevelWindow() {
@@ -746,17 +746,17 @@ exports.testPageModCssList = function(te
   let [pageMod] = testPageMod(test,
     'data:text/html;charset=utf-8,<div style="width:320px; max-width: 480px!important">css test</div>', [{
       include: "data:*",
       contentStyleFile: [
         // Highlight evaluation order in this list
         "data:text/css;charset=utf-8,div { border: 1px solid black; }",
         "data:text/css;charset=utf-8,div { border: 10px solid black; }",
         // Highlight evaluation order between contentStylesheet & contentStylesheetFile
-        "data:text/cs;charset=utf-8s,div { height: 1000px; }",
+        "data:text/css;charset=utf-8s,div { height: 1000px; }",
         // Highlight precedence between the author and user style sheet
         "data:text/css;charset=utf-8,div { width: 200px; max-width: 640px!important}",
       ],
       contentStyle: [
         "div { height: 10px; }",
         "div { height: 100px; }"
       ]
     }],
@@ -774,23 +774,23 @@ exports.testPageModCssList = function(te
         div.offsetHeight,
         120,
         "PageMod contentStyleFile list works"
       );
 
       test.assertEqual(
         style.width,
         "320px",
-        "PageMod author/user style sheet precedence works"
+        "PageMod add-on author/page author style sheet precedence works"
       );
 
       test.assertEqual(
         style.maxWidth,
-        "640px",
-        "PageMod author/user style sheet precedence with !important works"
+        "480px",
+        "PageMod add-on author/page author style sheet precedence with !important works"
       );
 
       done();
     }
   );
 };
 
 exports.testPageModCssDestroy = function(test) {
--- a/addon-sdk/source/test/test-panel.js
+++ b/addon-sdk/source/test/test-panel.js
@@ -10,16 +10,17 @@ const timer = require("sdk/timers");
 const self = require('sdk/self');
 const { open, close, focus } = require('sdk/window/helpers');
 const { isPrivate } = require('sdk/private-browsing');
 const { isWindowPBSupported, isGlobalPBSupported } = require('sdk/private-browsing/utils');
 const { defer } = require('sdk/core/promise');
 const { getMostRecentBrowserWindow } = require('sdk/window/utils');
 const { getWindow } = require('sdk/panel/window');
 const { pb } = require('./private-browsing/helper');
+const { URL } = require('sdk/url');
 
 const SVG_URL = self.data.url('mofo_logo.SVG');
 
 function makeEmptyPrivateBrowserWindow(options) {
   options = options || {};
   return open('chrome://browser/content/browser.xul', {
     features: {
       chrome: true,
@@ -120,17 +121,18 @@ exports["test Document Reload"] = functi
   let content =
     "<script>" +
     "setTimeout(function () {" +
     "  window.location = 'about:blank';" +
     "}, 250);" +
     "</script>";
   let messageCount = 0;
   let panel = Panel({
-    contentURL: "data:text/html;charset=utf-8," + encodeURIComponent(content),
+    // using URL here is intentional, see bug 859009
+    contentURL: URL("data:text/html;charset=utf-8," + encodeURIComponent(content)),
     contentScript: "self.postMessage(window.location.href)",
     onMessage: function (message) {
       messageCount++;
       if (messageCount == 1) {
         assert.ok(/data:text\/html/.test(message), "First document had a content script");
       }
       else if (messageCount == 2) {
         assert.equal(message, "about:blank", "Second document too");
@@ -511,29 +513,20 @@ exports["test Automatic Destroy"] = func
 
   panel.port.on("event-back", function () {
     assert.fail("Panel should have been destroyed on module unload");
   });
   panel.port.emit("event");
   assert.pass("check automatic destroy");
 };
 
-exports["test Wait For Init Then Show Then Destroy"] = makeEventOrderTest({
-  test: function(assert, done, expect, panel) {
-    expect('inited', function() { panel.show(); }).
-      then('show', function() { panel.destroy(); }).
-      then('hide', function() { done(); });
-  }
-});
-
-exports["test Show Then Wait For Init Then Destroy"] = makeEventOrderTest({
+exports["test Show Then Destroy"] = makeEventOrderTest({
   test: function(assert, done, expect, panel) {
     panel.show();
-    expect('inited').
-      then('show', function() { panel.destroy(); }).
+    expect('show', function() { panel.destroy(); }).
       then('hide', function() { done(); });
   }
 });
 
 exports["test Show Then Hide Then Destroy"] = makeEventOrderTest({
   test: function(assert, done, expect, panel) {
     panel.show();
     expect('show', function() { panel.hide(); }).
--- a/addon-sdk/source/test/test-request.js
+++ b/addon-sdk/source/test/test-request.js
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const { Request } = require("sdk/request");
 const { pathFor } = require("sdk/system");
 const file = require("sdk/io/file");
-
+const { URL } = require("sdk/url");
+const { extend } = require("sdk/util/object");
 const { Loader } = require("sdk/test/loader");
 const options = require("@test/options");
 
 const loader = Loader(module);
 const httpd = loader.require("sdk/test/httpd");
 if (options.parseable || options.verbose)
   loader.sandbox("sdk/test/httpd").DEBUG = true;
 const { startServerAsync } = httpd;
@@ -41,24 +42,23 @@ exports.testOptionsValidator = function(
     req.url = 'www.mozilla.org';
   }, 'The option "url" is invalid.');
   // The url shouldn't have changed, so check that
   test.assertEqual(req.url, "http://playground.zpao.com/jetpack/request/text.php");
 }
 
 exports.testContentValidator = function(test) {
   test.waitUntilDone();
-  Request({
+  runMultipleURLs(null, test, {
     url: "data:text/html;charset=utf-8,response",
     content: { 'key1' : null, 'key2' : 'some value' },
     onComplete: function(response) {
       test.assertEqual(response.text, "response?key1=null&key2=some+value");
-      test.done();
     }
-  }).get();
+  });
 };
 
 // This is a request to a file that exists.
 exports.testStatus200 = function (test) {
   let srv = startServerAsync(port, basePath);
   let content = "Look ma, no hands!\n";
   let basename = "test-request.txt"
   prepareFile(basename, content);
@@ -77,47 +77,45 @@ exports.testStatus200 = function (test) 
   }).get();
 }
 
 // This tries to get a file that doesn't exist
 exports.testStatus404 = function (test) {
   var srv = startServerAsync(port, basePath);
 
   test.waitUntilDone();
-  Request({
+  runMultipleURLs(srv, test, {
     // the following URL doesn't exist
     url: "http://localhost:" + port + "/test-request-404.txt",
     onComplete: function (response) {
       test.assertEqual(response.status, 404);
       test.assertEqual(response.statusText, "Not Found");
-      srv.stop(function() test.done());
     }
-  }).get();
+  });
 }
 
 // a simple file with a known header
 exports.testKnownHeader = function (test) {
   var srv = startServerAsync(port, basePath);
 
  // Create the file that will be requested with the associated headers file
   let content = "This tests adding headers to the server's response.\n";
   let basename = "test-request-headers.txt";
   let headerContent = "x-jetpack-header: Jamba Juice\n";
   let headerBasename = "test-request-headers.txt^headers^";
   prepareFile(basename, content);
   prepareFile(headerBasename, headerContent);
 
   test.waitUntilDone();
-  Request({
+  runMultipleURLs(srv, test, {
     url: "http://localhost:" + port + "/test-request-headers.txt",
     onComplete: function (response) {
       test.assertEqual(response.headers["x-jetpack-header"], "Jamba Juice");
-      srv.stop(function() test.done());
     }
-  }).get();
+  });
 }
 
 // complex headers
 exports.testComplexHeader = function (test) {
   let srv = startServerAsync(port, basePath);
 
   let basename = "test-request-complex-headers.sjs";
   let content = handleRequest.toString();
@@ -127,25 +125,24 @@ exports.testComplexHeader = function (te
     "x-jetpack-header": "Jamba Juice is: delicious",
     "x-jetpack-header-2": "foo,bar",
     "x-jetpack-header-3": "sup dawg, i heard you like x, so we put a x in " +
       "yo x so you can y while you y",
     "Set-Cookie": "foo=bar\nbaz=foo"
   }
 
   test.waitUntilDone();
-  Request({
+  runMultipleURLs(srv, test, {
     url: "http://localhost:" + port + "/test-request-complex-headers.sjs",
     onComplete: function (response) {
       for (k in headers) {
         test.assertEqual(response.headers[k], headers[k]);
       }
-      srv.stop(function() test.done());
     }
-  }).get();
+  });
 }
 
 // Force Allow Third Party cookies
 exports.test3rdPartyCookies = function (test) {
   let srv = startServerAsync(port, basePath);
 
   let basename = "test-request-3rd-party-cookies.sjs";
 
@@ -194,38 +191,50 @@ exports.test3rdPartyCookies = function (
 
 exports.testSimpleJSON = function (test) {
   let srv = startServerAsync(port, basePath);
   let json = { foo: "bar" };
   let basename = "test-request.json";
   prepareFile(basename, JSON.stringify(json));
 
   test.waitUntilDone();
-  Request({
+  runMultipleURLs(srv, test, {
     url: "http://localhost:" + port + "/" + basename,
     onComplete: function (response) {
       assertDeepEqual(test, response.json, json);
-      srv.stop(function() test.done());
     }
-  }).get();
+  });
 }
 
 exports.testInvalidJSON = function (test) {
   let srv = startServerAsync(port, basePath);
   let basename = "test-request-invalid.json";
   prepareFile(basename, '"this": "isn\'t JSON"');
 
   test.waitUntilDone();
-  Request({
+  runMultipleURLs(srv, test, {
     url: "http://localhost:" + port + "/" + basename,
     onComplete: function (response) {
       test.assertEqual(response.json, null);
-      srv.stop(function() test.done());
     }
-  }).get();
+  });
+}
+
+function runMultipleURLs (srv, test, options) {
+  let urls = [options.url, URL(options.url)];
+  let cb = options.onComplete;
+  let ran = 0;
+  let onComplete = function (res) {
+    cb(res);
+    if (++ran === urls.length)
+      srv ? srv.stop(function () test.done()) : test.done();
+  }
+  urls.forEach(function (url) {
+    Request(extend(options, { url: url, onComplete: onComplete })).get();
+  });
 }
 
 // All tests below here require a network connection. They will be commented out
 // when checked in. If you'd like to run them, simply uncomment them.
 //
 // When we have the means, these tests will be converted so that they don't
 // require an external server nor a network connection.
 
--- a/addon-sdk/source/test/test-selection.js
+++ b/addon-sdk/source/test/test-selection.js
@@ -23,17 +23,17 @@ const { setTabURL } = require("sdk/tabs/
 const { getActiveTab, getTabContentWindow, closeTab } = require("sdk/tabs/utils")
 const { getMostRecentBrowserWindow } = require("sdk/window/utils");
 const { open: openNewWindow } = require("sdk/window/helpers");
 const { Loader } = require("sdk/test/loader");
 const { setTimeout } = require("sdk/timers");
 const { Cu } = require("chrome");
 const { merge } = require("sdk/util/object");
 const { isPrivate } = require("sdk/private-browsing");
-
+const events = require("sdk/system/events");
 // General purpose utility functions
 
 /**
  * Opens the url given and return a promise, that will be resolved with the
  * content window when the document is ready.
  *
  * I believe this approach could be useful in most of our unit test, that
  * requires to open a tab and need to access to its content.
@@ -156,20 +156,26 @@ function getFrameWindow(window) {
 function hideAndShowFrame(window) {
   let { promise, resolve } = defer();
   let iframe = window.document.querySelector("iframe");
 
   iframe.style.display = "none";
 
   Cu.forceGC();
 
-  setTimeout(function(){
+  setTimeout(function() {
+    events.on("document-shown", function shown(event) {
+      if (iframe.contentWindow !== event.subject.defaultView)
+        return;
+
+      events.off("document-shown", shown);
+      setTimeout(resolve, 0, window);
+    }, true);
+
     iframe.style.display = "";
-
-    setTimeout(resolve, 500, window);
   }, 0)
 
   return promise;
 }
 
 /**
  * Select the first div in the page, adding the range to the selection.
  */
@@ -823,44 +829,46 @@ exports["test Textarea OnSelect Listener
 };
 
 exports["test Selection Listener on frame"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   selection.once("select", function() {
     assert.equal(selection.text, "fo");
+    close();
+    loader.unload();
     done();
   });
 
   open(FRAME_URL).
     then(hideAndShowFrame).
     then(getFrameWindow).
     then(selectContentFirstDiv).
     then(dispatchSelectionEvent).
-    then(close).
-    then(loader.unload, assert.fail);
+    then(null, assert.fail);
 };
 
 exports["test Textarea onSelect Listener on frame"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   selection.once("select", function() {
     assert.equal(selection.text, "noodles");
+    close();
+    loader.unload();
     done();
   });
 
   open(FRAME_URL).
     then(hideAndShowFrame).
     then(getFrameWindow).
     then(selectTextarea).
     then(dispatchOnSelectEvent).
-    then(close).
-    then(loader.unload, assert.fail);
+    then(null, assert.fail);
 };
 
 
 exports["test PBPW Selection Listener"] = function(assert, done) {
   let loader = Loader(module);
   let selection = loader.require("sdk/selection");
 
   selection.once("select", function() {
--- a/addon-sdk/source/test/test-system-events.js
+++ b/addon-sdk/source/test/test-system-events.js
@@ -186,17 +186,17 @@ exports["test emit to nsIObserverService
   events.emit(topic, { subject: customSubject, data: customData });
 
   assert.equal(timesCalled, 2, "emit notifies observers");
   assert.equal(lastTopic, topic, "event.type is notification");
   assert.equal(lastSubject.wrappedJSObject.object, customSubject,
                "event.subject is notification subject");
   assert.equal(lastData, customData, "event.data is notification data");
 
-  nsIObserverService.removeObserver(nsIObserver, topic, false);
+  nsIObserverService.removeObserver(nsIObserver, topic);
 
   events.emit(topic, { data: "more data" });
 
   assert.equal(timesCalled, 2, "removed observers no longer invoked");
 
   nsIObserverService.addObserver(nsIObserver, "*", false);
 
   events.emit(topic, { data: "data again" });
--- a/addon-sdk/source/test/test-url.js
+++ b/addon-sdk/source/test/test-url.js
@@ -250,16 +250,22 @@ exports.testIsValidURI = function (test)
 };
 
 exports.testIsInvalidURI = function (test) {
   invalidURIs().forEach(function (aUri) {
     test.assertEqual(url.isValidURI(aUri), false, aUri + ' is an invalid URL');
   });
 };
 
+exports.testURLFromURL = function(test) {
+  let aURL = url.URL('http://mozilla.org');
+  let bURL = url.URL(aURL);
+  test.assertEqual(aURL.toString(), bURL.toString(), 'Making a URL from a URL works');
+};
+
 function validURIs() {
   return [
   'http://foo.com/blah_blah',
   'http://foo.com/blah_blah/',
   'http://foo.com/blah_blah_(wikipedia)',
   'http://foo.com/blah_blah_(wikipedia)_(again)',
   'http://www.example.com/wpstyle/?p=364',
   'https://www.example.com/foo/?bar=baz&amp;inga=42&amp;quux',
--- a/b2g/chrome/content/forms.js
+++ b/b2g/chrome/content/forms.js
@@ -485,16 +485,20 @@ let FormAssistant = {
       });
     }
   }
 };
 
 FormAssistant.init();
 
 function isContentEditable(element) {
+  if (!element) {
+    return false;
+  }
+
   if (element.isContentEditable || element.designMode == "on")
     return true;
 
   // If a body element is editable and the body is the child of an
   // iframe we can assume this is an advanced HTML editor
   if (element instanceof HTMLIFrameElement &&
       element.contentDocument &&
       (element.contentDocument.body.isContentEditable ||
--- a/b2g/components/TelURIParser.jsm
+++ b/b2g/components/TelURIParser.jsm
@@ -6,21 +6,16 @@
 
 this.EXPORTED_SYMBOLS = ["TelURIParser"];
 
 /**
  * Singleton providing functionality for parsing tel: and sms: URIs
  */
 this.TelURIParser = {
   parseURI: function(scheme, uri) {
-    // Ignore MWI and USSD codes. See 794034.
-    if (uri.indexOf('*') != -1 || uri.indexOf('#') != -1) {
-      return null;
-    }
-
     // https://www.ietf.org/rfc/rfc2806.txt
     let subscriber = uri.slice((scheme + ':').length);
 
     if (!subscriber.length) {
       return null;
     }
 
     let number = '';
@@ -109,12 +104,17 @@ this.TelURIParser = {
       if (subscriber.substring(pos, pos + 15) == ';phone-context=') {
         pos += 15;
 
         // global-network-prefix | local-network-prefix | private-prefi
         number = subscriber.substring(pos, subscriber.length) + number;
       }
     }
 
+    // Ignore MWI and USSD codes. See 794034.
+    if (number.match(/[#\*]/) && !number.match(/^[#\*]\d+$/)) {
+      return null;
+    }
+
     return number || null;
   }
 };
 
new file mode 100644
--- /dev/null
+++ b/b2g/components/test/unit/test_bug832946.js
@@ -0,0 +1,18 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+  Components.utils.import("resource:///modules/TelURIParser.jsm")
+
+  // blocked numbers
+  do_check_eq(TelURIParser.parseURI('tel', 'tel:#1234*'), null);
+  do_check_eq(TelURIParser.parseURI('tel', 'tel:*1234#'), null);
+  do_check_eq(TelURIParser.parseURI('tel', 'tel:*1234*'), null);
+  do_check_eq(TelURIParser.parseURI('tel', 'tel:#1234#'), null);
+  do_check_eq(TelURIParser.parseURI('tel', 'tel:*#*#7780#*#*'), null);
+  do_check_eq(TelURIParser.parseURI('tel', 'tel:*1234AB'), null);
+
+  // white list
+  do_check_eq(TelURIParser.parseURI('tel', 'tel:*1234'), '*1234');
+  do_check_eq(TelURIParser.parseURI('tel', 'tel:#1234'), '#1234');
+}
--- a/b2g/components/test/unit/xpcshell.ini
+++ b/b2g/components/test/unit/xpcshell.ini
@@ -1,11 +1,13 @@
 [DEFAULT]
 head =
 tail =
 
 [test_bug793310.js]
 
+[test_bug832946.js]
+
 [test_signintowebsite.js]
 head = head_identity.js
 tail =
 
 
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -643,23 +643,37 @@
                   </pluginItem>
       <pluginItem  blockID="p176">
                   <match name="filename" exp="(NPSWF32\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="10.3" maxVersion="10.3.183.18.999" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="19.0a1" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
+      <pluginItem  blockID="p176">
+                  <match name="filename" exp="(NPSWF32\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="10.3" maxVersion="10.3.183.18.999" severity="0" vulnerabilitystatus="1">
+                                <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="17.0.4" maxVersion="17.0.*" />
+                          </targetApplication>
+                  </versionRange>
+                  </pluginItem>
       <pluginItem  blockID="p178">
                   <match name="filename" exp="(NPSWF[0-9_]*\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="11.0" maxVersion="11.2.202.274.9999" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="19.0a1" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
+      <pluginItem  blockID="p178">
+                  <match name="filename" exp="(NPSWF[0-9_]*\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="11.0" maxVersion="11.2.202.274.9999" severity="0" vulnerabilitystatus="1">
+                                <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="17.0.4" maxVersion="17.0.*" />
+                          </targetApplication>
+                  </versionRange>
+                  </pluginItem>
       <pluginItem  blockID="p180">
                   <match name="filename" exp="JavaAppletPlugin\.plugin" />                                    <versionRange  minVersion="Java 7 Update 0" maxVersion="Java 7 Update 11" severity="0" vulnerabilitystatus="2">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="17.0" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
       <pluginItem  blockID="p182">
@@ -746,23 +760,37 @@
                   </pluginItem>
       <pluginItem  blockID="p260">
                   <match name="filename" exp="(NPSWF32\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="0" maxVersion="10.2.9999" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="18.0a1" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
+      <pluginItem  blockID="p260">
+                  <match name="filename" exp="(NPSWF32\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="0" maxVersion="10.2.9999" severity="0" vulnerabilitystatus="1">
+                                <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="17.0.4" maxVersion="17.0.*" />
+                          </targetApplication>
+                  </versionRange>
+                  </pluginItem>
       <pluginItem  blockID="p290">
                   <match name="filename" exp="(NPSWF32\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="10.3.183.19" maxVersion="10.3.183.66" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="19.0a1" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
+      <pluginItem  blockID="p290">
+                  <match name="filename" exp="(NPSWF32\.dll)|(Flash\ Player\.plugin)" />                                    <versionRange  minVersion="10.3.183.19" maxVersion="10.3.183.66" severity="0" vulnerabilitystatus="1">
+                                <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="17.0.4" maxVersion="17.0.*" />
+                          </targetApplication>
+                  </versionRange>
+                  </pluginItem>
       <pluginItem  blockID="p292">
                   <match name="filename" exp="JavaAppletPlugin\.plugin" />                                    <versionRange  minVersion="Java 7 Update 12" maxVersion="Java 7 Update 15" severity="0" vulnerabilitystatus="2">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="17.0" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
       <pluginItem  blockID="p294">
@@ -809,27 +837,48 @@
                   </pluginItem>
       <pluginItem  blockID="p328">
                   <match name="filename" exp="Silverlight\.plugin" />                                    <versionRange  minVersion="5.1" maxVersion="5.1.20124.9999" severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="19.0a1" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
+      <pluginItem  blockID="p328">
+                  <match name="filename" exp="Silverlight\.plugin" />                                    <versionRange  minVersion="5.1" maxVersion="5.1.20124.9999" severity="0" vulnerabilitystatus="1">
+                                <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="17.0.4" maxVersion="17.0.*" />
+                          </targetApplication>
+                  </versionRange>
+                  </pluginItem>
       <pluginItem  blockID="p330">
             <match name="description" exp="^Shockwave Flash (([1-9]\.[0-9]+)|(10\.([0-2]|(3 r(([0-9][0-9]?)|1(([0-7][0-9])|8[0-2]))))))( |$)" />      <match name="filename" exp="libflashplayer\.so" />                                    <versionRange  severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="19.0a1" maxVersion="*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
+      <pluginItem  blockID="p330">
+            <match name="description" exp="^Shockwave Flash (([1-9]\.[0-9]+)|(10\.([0-2]|(3 r(([0-9][0-9]?)|1(([0-7][0-9])|8[0-2]))))))( |$)" />      <match name="filename" exp="libflashplayer\.so" />                                    <versionRange  severity="0" vulnerabilitystatus="1">
+                                <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="17.0.4" maxVersion="17.0.*" />
+                          </targetApplication>
+                  </versionRange>
+                  </pluginItem>
+      <pluginItem  blockID="p332">
+            <match name="description" exp="^Shockwave Flash 11.(0|1) r[0-9]{1,3}$" />      <match name="filename" exp="libflashplayer\.so" />                                    <versionRange  severity="0" vulnerabilitystatus="1">
+                                <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+                              <versionRange  minVersion="19.0a1" maxVersion="*" />
+                          </targetApplication>
+                  </versionRange>
+                  </pluginItem>
       <pluginItem  blockID="p332">
             <match name="description" exp="^Shockwave Flash 11.(0|1) r[0-9]{1,3}$" />      <match name="filename" exp="libflashplayer\.so" />                                    <versionRange  severity="0" vulnerabilitystatus="1">
                                 <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
-                              <versionRange  minVersion="19.0a1" maxVersion="*" />
+                              <versionRange  minVersion="17.0.4" maxVersion="17.0.*" />
                           </targetApplication>
                   </versionRange>
                   </pluginItem>
     </pluginItems>
 
   <gfxItems>
     <gfxBlacklistEntry  blockID="g35">      <os>WINNT 6.1</os>      <vendor>0x10de</vendor>              <devices>
                       <device>0x0a6c</device>
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -103,36 +103,34 @@ static bool IsArg(const char* arg, const
     return !strcasecmp(++arg, s);
 #endif
 
   return false;
 }
 
 #ifdef XP_WIN
 /*
- * AttachToTestsConsole - Windows helper for when we are running
+ * AttachToTestHarness - Windows helper for when we are running
  * in the immersive environment. Firefox is launched by Windows in
  * response to a request by metrotestharness, which is launched by
  * runtests.py. As such stdout in fx doesn't point to the right
  * stream. This helper touches up stdout such that test output gets
- * routed to the console the tests are run in.
+ * routed to a named pipe metrotestharness creates and dumps to its
+ * stdout.
  */
-static void AttachToTestsConsole(DWORD aProcessId)
+static void AttachToTestHarness()
 {
-  if (!AttachConsole(aProcessId)) {
-    OutputDebugStringW(L"Could not attach to console.\n");
-    return;
-  }
-
-  HANDLE winOut = CreateFileA("CONOUT$",
-                              GENERIC_READ | GENERIC_WRITE,
+  // attach to the metrotestharness named logging pipe
+  HANDLE winOut = CreateFileA("\\\\.\\pipe\\metrotestharness",
+                              GENERIC_WRITE,
                               FILE_SHARE_WRITE, 0,
                               OPEN_EXISTING, 0, 0);
+  
   if (winOut == INVALID_HANDLE_VALUE) {
-    OutputDebugStringW(L"Could not attach to console.\n");
+    OutputDebugStringW(L"Could not create named logging pipe.\n");
     return;
   }
 
   // Set the c runtime handle
   int stdOut = _open_osfhandle((intptr_t)winOut, _O_APPEND);
   if (stdOut == -1) {
     OutputDebugStringW(L"Could not open c-runtime handle.\n");
     return;
@@ -350,33 +348,28 @@ static int do_main(int argc, char* argv[
       char* ptr = buffer;
       newArgv[0] = ptr;
       while (*ptr != NULL &&
              (ptr - buffer) < sizeof(buffer) &&
              newArgc < ARRAYSIZE(newArgv)) {
         if (isspace(*ptr)) {
           *ptr = '\0';
           ptr++;
-          // Check for the console id the metrotestharness passes in, we need
-          // to connect up to this so test output goes to the right place.
-          if (ptr && !strncmp(ptr, kMetroConsoleIdParam, strlen(kMetroConsoleIdParam))) {
-            DWORD processId = strtol(ptr + strlen(kMetroConsoleIdParam), nullptr, 10);
-            if (processId > 0) {
-              AttachToTestsConsole(processId);
-            }
-            continue;
-          }
           newArgv[newArgc] = ptr;
           newArgc++;
           continue;
         }
         ptr++;
       }
       if (ptr == newArgv[newArgc-1])
         newArgc--;
+
+      // attach browser stdout to metrotestharness stdout
+      AttachToTestHarness();
+
       int result = XRE_main(newArgc, newArgv, appData, mainFlags);
       XRE_FreeAppData(appData);
       return result;
     }
   }
 #endif
 
   int result = XRE_main(argc, argv, appData, mainFlags);
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1009,18 +1009,18 @@ pref("services.sync.prefs.sync.privacy.c
 pref("services.sync.prefs.sync.privacy.clearOnShutdown.siteSettings", true);
 pref("services.sync.prefs.sync.privacy.donottrackheader.enabled", true);
 pref("services.sync.prefs.sync.privacy.donottrackheader.value", true);
 pref("services.sync.prefs.sync.privacy.sanitize.sanitizeOnShutdown", true);
 pref("services.sync.prefs.sync.security.OCSP.disable_button.managecrl", true);
 pref("services.sync.prefs.sync.security.OCSP.enabled", true);
 pref("services.sync.prefs.sync.security.OCSP.require", true);
 pref("services.sync.prefs.sync.security.default_personal_cert", true);
-pref("services.sync.prefs.sync.security.security.tls.version.min", true);
-pref("services.sync.prefs.sync.security.security.tls.version.max", true);
+pref("services.sync.prefs.sync.security.tls.version.min", true);
+pref("services.sync.prefs.sync.security.tls.version.max", true);
 pref("services.sync.prefs.sync.signon.rememberSignons", true);
 pref("services.sync.prefs.sync.spellchecker.dictionary", true);
 pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
 #endif
 
 // Disable the error console
 pref("devtools.errorconsole.enabled", false);
 
--- a/browser/base/content/browser-gestureSupport.js
+++ b/browser/base/content/browser-gestureSupport.js
@@ -186,17 +186,29 @@ let gGestureSupport = {
 
     if (canGoBack)
       aEvent.allowedDirections |= isLTR ? aEvent.DIRECTION_LEFT :
                                           aEvent.DIRECTION_RIGHT;
     if (canGoForward)
       aEvent.allowedDirections |= isLTR ? aEvent.DIRECTION_RIGHT :
                                           aEvent.DIRECTION_LEFT;
 
-    gHistorySwipeAnimation.startAnimation();
+    let isVerticalSwipe = false;
+    if (aEvent.direction == aEvent.DIRECTION_UP) {
+      isVerticalSwipe = true;
+      // Force a synchronous scroll to the top of the page.
+      content.scrollTo(content.scrollX, 0);
+    }
+    else if (aEvent.direction == aEvent.DIRECTION_DOWN) {
+      isVerticalSwipe = true;
+      // Force a synchronous scroll to the bottom of the page.
+      content.scrollTo(content.scrollX, content.scrollMaxY);
+    }
+
+    gHistorySwipeAnimation.startAnimation(isVerticalSwipe);
 
     this._doUpdate = function GS__doUpdate(aEvent) {
       gHistorySwipeAnimation.updateAnimation(aEvent.delta);
     };
 
     this._doEnd = function GS__doEnd(aEvent) {
       gHistorySwipeAnimation.swipeEndEventReceived();
 
@@ -536,59 +548,79 @@ let gHistorySwipeAnimation = {
   init: function HSA_init() {
     if (!this._isSupported())
       return;
 
     this.active = false;
     this.isLTR = document.documentElement.mozMatchesSelector(
                                             ":-moz-locale-dir(ltr)");
     this._trackedSnapshots = [];
+    this._startingIndex = -1;
     this._historyIndex = -1;
     this._boxWidth = -1;
+    this._boxHeight = -1;
     this._maxSnapshots = this._getMaxSnapshots();
     this._lastSwipeDir = "";
+    this._isVerticalSwipe = false;
 
     // We only want to activate history swipe animations if we store snapshots.
     // If we don't store any, we handle horizontal swipes without animations.
     if (this._maxSnapshots > 0) {
       this.active = true;
       gBrowser.addEventListener("pagehide", this, false);
       gBrowser.addEventListener("pageshow", this, false);
       gBrowser.addEventListener("popstate", this, false);
+      gBrowser.addEventListener("DOMModalDialogClosed", this, false);
       gBrowser.tabContainer.addEventListener("TabClose", this, false);
     }
   },
 
   /**
    * Uninitializes the support for history swipe animations.
    */
   uninit: function HSA_uninit() {
     gBrowser.removeEventListener("pagehide", this, false);
     gBrowser.removeEventListener("pageshow", this, false);
     gBrowser.removeEventListener("popstate", this, false);
+    gBrowser.removeEventListener("DOMModalDialogClosed", this, false);
     gBrowser.tabContainer.removeEventListener("TabClose", this, false);
 
     this.active = false;
     this.isLTR = false;
   },
 
   /**
    * Starts the swipe animation and handles fast swiping (i.e. a swipe animation
    * is already in progress when a new one is initiated).
+   *
+   * @param aIsVerticalSwipe
+   *        Whether we're dealing with a vertical swipe or not.
    */
-  startAnimation: function HSA_startAnimation() {
+  startAnimation: function HSA_startAnimation(aIsVerticalSwipe) {
+    this._isVerticalSwipe = aIsVerticalSwipe;
+
     if (this.isAnimationRunning()) {
-      gBrowser.stop();
-      this._lastSwipeDir = "RELOAD"; // just ensure that != ""
-      this._canGoBack = this.canGoBack();
-      this._canGoForward = this.canGoForward();
-      this._handleFastSwiping();
+      // If this is a horizontal scroll, or if this is a vertical scroll that
+      // was started while a horizontal scroll was still running, handle it as
+      // as a fast swipe. In the case of the latter scenario, this allows us to
+      // start the vertical animation without first loading the final page, or
+      // taking another snapshot. If vertical scrolls are initiated repeatedly
+      // without prior horizontal scroll we skip this and restart the animation
+      // from 0.
+      if (!this._isVerticalSwipe || this._lastSwipeDir != "") {
+        gBrowser.stop();
+        this._lastSwipeDir = "RELOAD"; // just ensure that != ""
+        this._canGoBack = this.canGoBack();
+        this._canGoForward = this.canGoForward();
+        this._handleFastSwiping();
+      }
     }
     else {
-      this._historyIndex = gBrowser.webNavigation.sessionHistory.index;
+      this._startingIndex = gBrowser.webNavigation.sessionHistory.index;
+      this._historyIndex = this._startingIndex;
       this._canGoBack = this.canGoBack();
       this._canGoForward = this.canGoForward();
       if (this.active) {
         this._takeSnapshot();
         this._installPrevAndNextSnapshots();
         this._addBoxes();
         this._lastSwipeDir = "";
       }
@@ -609,75 +641,86 @@ let gHistorySwipeAnimation = {
    * @param aVal
    *        A floating point value that represents the progress of the
    *        swipe gesture.
    */
   updateAnimation: function HSA_updateAnimation(aVal) {
     if (!this.isAnimationRunning())
       return;
 
-    if ((aVal >= 0 && this.isLTR) ||
-        (aVal <= 0 && !this.isLTR)) {
-      if (aVal > 1)
-        aVal = 1; // Cap value to avoid sliding the page further than allowed.
-
+    // We use the following value to decrease the bounce effect when scrolling
+    // to the top or bottom of the page, or when swiping back/forward past the
+    // browsing history. This value was determined experimentally.
+    let dampValue = 4;
+    if (this._isVerticalSwipe) {
+      this._prevBox.collapsed = true;
+      this._nextBox.collapsed = true;
+      this._positionBox(this._curBox, -1 * aVal / dampValue);
+    }
+    else if ((aVal >= 0 && this.isLTR) ||
+             (aVal <= 0 && !this.isLTR)) {
+      let tempDampValue = 1;
       if (this._canGoBack)
         this._prevBox.collapsed = false;
-      else
+      else {
+        tempDampValue = dampValue;
         this._prevBox.collapsed = true;
+      }
 
       // The current page is pushed to the right (LTR) or left (RTL),
       // the intention is to go back.
       // If there is a page to go back to, it should show in the background.
-      this._positionBox(this._curBox, aVal);
+      this._positionBox(this._curBox, aVal / tempDampValue);
 
       // The forward page should be pushed offscreen all the way to the right.
       this._positionBox(this._nextBox, 1);
     }
     else {
       if (aVal < -1)
         aVal = -1; // Cap value to avoid sliding the page further than allowed.
 
       // The intention is to go forward. If there is a page to go forward to,
       // it should slide in from the right (LTR) or left (RTL).
       // Otherwise, the current page should slide to the left (LTR) or
       // right (RTL) and the backdrop should appear in the background.
       // For the backdrop to be visible in that case, the previous page needs
       // to be hidden (if it exists).
       if (this._canGoForward) {
+        this._nextBox.collapsed = false;
         let offset = this.isLTR ? 1 : -1;
         this._positionBox(this._curBox, 0);
-        this._positionBox(this._nextBox, offset + aVal); // aVal is negative
+        this._positionBox(this._nextBox, offset + aVal);
       }
       else {
         this._prevBox.collapsed = true;
-        this._positionBox(this._curBox, aVal);
+        this._positionBox(this._curBox, aVal / dampValue);
       }
     }
   },
 
   /**
    * Event handler for events relevant to the history swipe animation.
    *
    * @param aEvent
    *        An event to process.
    */
   handleEvent: function HSA_handleEvent(aEvent) {
     switch (aEvent.type) {
       case "TabClose":
         let browser = gBrowser.getBrowserForTab(aEvent.target);
         this._removeTrackedSnapshot(-1, browser);
         break;
+      case "DOMModalDialogClosed":
+        this.stopAnimation();
+        break;
       case "pageshow":
       case "popstate":
-        if (this.isAnimationRunning()) {
-          if (aEvent.target != gBrowser.selectedBrowser.contentDocument)
-            break;
-          this.stopAnimation();
-        }
+        if (aEvent.target != gBrowser.selectedBrowser.contentDocument)
+          break;
+        this.stopAnimation();
         this._historyIndex = gBrowser.webNavigation.sessionHistory.index;
         break;
       case "pagehide":
         if (aEvent.target == gBrowser.selectedBrowser.contentDocument) {
           // Take a snapshot of a page whenever it's about to be navigated away
           // from.
           this._takeSnapshot();
         }
@@ -735,17 +778,17 @@ let gHistorySwipeAnimation = {
   },
 
   /**
    * Used to notify the history swipe animation that the OS sent a swipe end
    * event and that we should navigate to the page that the user swiped to, if
    * any. This will also result in the animation overlay to be torn down.
    */
   swipeEndEventReceived: function HSA_swipeEndEventReceived() {
-    if (this._lastSwipeDir != "")
+    if (this._lastSwipeDir != "" && this._historyIndex != this._startingIndex)
       this._navigateToHistoryIndex();
     else
       this.stopAnimation();
   },
 
   /**
    * Checks whether a particular index exists in the browser history or not.
    *
@@ -763,19 +806,20 @@ let gHistorySwipeAnimation = {
     return true;
   },
 
   /**
    * Navigates to the index in history that is currently being tracked by
    * |this|.
    */
   _navigateToHistoryIndex: function HSA__navigateToHistoryIndex() {
-    if (this._doesIndexExistInHistory(this._historyIndex)) {
+    if (this._doesIndexExistInHistory(this._historyIndex))
       gBrowser.webNavigation.gotoIndex(this._historyIndex);
-    }
+    else
+      this.stopAnimation();
   },
 
   /**
    * Checks to see if history swipe animations are supported by this
    * platform/configuration.
    *
    * return true if supported, false otherwise.
    */
@@ -811,30 +855,33 @@ let gHistorySwipeAnimation = {
     this._curBox = this._createElement("historySwipeAnimationCurrentPage",
                                        "box");
     this._container.appendChild(this._curBox);
 
     this._nextBox = this._createElement("historySwipeAnimationNextPage",
                                         "box");
     this._container.appendChild(this._nextBox);
 
-    this._boxWidth = this._curBox.getBoundingClientRect().width; // cache width
+    // Cache width and height.
+    this._boxWidth = this._curBox.getBoundingClientRect().width;
+    this._boxHeight = this._curBox.getBoundingClientRect().height;
   },
 
   /**
    * Removes the boxes.
    */
   _removeBoxes: function HSA__removeBoxes() {
     this._curBox = null;
     this._prevBox = null;
     this._nextBox = null;
     if (this._container)
       this._container.parentNode.removeChild(this._container);
     this._container = null;
     this._boxWidth = -1;
+    this._boxHeight = -1;
   },
 
   /**
    * Creates an element with a given identifier and tag name.
    *
    * @param aID
    *        An identifier to create the element with.
    * @param aTagName
@@ -852,17 +899,24 @@ let gHistorySwipeAnimation = {
    * Moves a given box to a given X coordinate position.
    *
    * @param aBox
    *        The box element to position.
    * @param aPosition
    *        The position (in X coordinates) to move the box element to.
    */
   _positionBox: function HSA__positionBox(aBox, aPosition) {
-    aBox.style.transform = "translateX(" + this._boxWidth * aPosition + "px)";
+    let transform = "";
+
+    if (this._isVerticalSwipe)
+      transform = "translateY(" + this._boxHeight * aPosition + "px)";
+    else
+      transform = "translateX(" + this._boxWidth * aPosition + "px)";
+
+    aBox.style.transform = transform;
   },
 
   /**
    * Takes a snapshot of the page the browser is currently on.
    */
   _takeSnapshot: function HSA__takeSnapshot() {
     if ((this._maxSnapshots < 1) ||
         (gBrowser.webNavigation.sessionHistory.index < 0))
@@ -991,22 +1045,27 @@ let gHistorySwipeAnimation = {
     if (!aBlob)
       return null;
 
     // Return aBlob if it's still a canvas and not a compressed blob yet.
     if (aBlob instanceof HTMLCanvasElement)
       return aBlob;
 
     let img = new Image();
-    let url = URL.createObjectURL(aBlob);
-    img.onload = function() {
-      URL.revokeObjectURL(url);
-    };
-    img.src = url;
-    return img;
+    let url = "";
+    try {
+      url = URL.createObjectURL(aBlob);
+      img.onload = function() {
+        URL.revokeObjectURL(url);
+      };
+    }
+    finally {
+      img.src = url;
+      return img;
+    }
   },
 
   /**
    * Sets the snapshot of the current page to the snapshot passed as parameter,
    * or to the one previously stored for the current index in history if the
    * parameter is null.
    *
    * @param aCanvas
--- a/browser/base/content/test/browser_aboutHome.js
+++ b/browser/base/content/test/browser_aboutHome.js
@@ -268,17 +268,18 @@ function test()
       let snippetsMap = yield promiseSetupSnippetsMap(tab, test.setup);
       // Ensure browser has set attributes already, or wait for them.
       yield promise;
       info("Running test");
       yield test.run(snippetsMap);
       info("Cleanup");
       gBrowser.removeCurrentTab();
     }
-
+  }).then(finish, ex => {
+    ok(false, "Unexpected Exception: " + ex);
     finish();
   });
 }
 
 /**
  * Creates a new tab and waits for a load event.
  *
  * @param aUrl
@@ -288,16 +289,21 @@ function test()
  * @return {Promise} resolved when the event is handled.  Gets the new tab.
  */
 function promiseNewTabLoadEvent(aUrl, aEventType="load")
 {
   let deferred = Promise.defer();
   let tab = gBrowser.selectedTab = gBrowser.addTab(aUrl);
   info("Wait tab event: " + aEventType);
   tab.linkedBrowser.addEventListener(aEventType, function load(event) {
+    if (event.originalTarget != tab.linkedBrowser.contentDocument ||
+        event.target.location.href == "about:blank") {
+      info("skipping spurious load event");
+      return;
+    }
     tab.linkedBrowser.removeEventListener(aEventType, load, true);
     info("Tab event received: " + aEventType);
     deferred.resolve(tab);
   }, true);
   return deferred.promise;
 }
 
 /**
--- a/browser/base/content/test/browser_keywordSearch.js
+++ b/browser/base/content/test/browser_keywordSearch.js
@@ -63,32 +63,24 @@ function test() {
     gBrowser.removeTab(tab);
   });
 
   nextTest();
 }
 
 var gCurrTest;
 function nextTest() {
-  // Clear the pref before every test (and after the last)
-  try {
-    Services.prefs.clearUserPref("keyword.URL");
-  } catch(ex) {}
-
   if (gTests.length) {
     gCurrTest = gTests.shift();
     doTest();
   } else {
     finish();
   }
 }
 
 function doTest() {
   info("Running test: " + gCurrTest.name);
 
-  if (gCurrTest.keywordURLPref)
-    Services.prefs.setCharPref("keyword.URL", gCurrTest.keywordURLPref);
-
   // Simulate a user entering search terms
   gURLBar.value = gCurrTest.testText;
   gURLBar.focus();
   EventUtils.synthesizeKey("VK_RETURN", {});
 }
--- a/browser/components/preferences/content.js
+++ b/browser/components/preferences/content.js
@@ -27,38 +27,16 @@ var gContentPane = {
   updateButtons: function (aButtonID, aPreferenceID)
   {
     var button = document.getElementById(aButtonID);
     var preference = document.getElementById(aPreferenceID);
     button.disabled = preference.value != true;
     return undefined;
   },
 
-  /**
-   * The exceptions types which may be passed to this._showExceptions().
-   */
-  _exceptionsParams: {
-    popup:   { blockVisible: false, sessionVisible: false, allowVisible: true, prefilledHost: "", permissionType: "popup"   }
-  },
-
-  /**
-   * Displays the exceptions dialog of the given type, where types map onto the
-   * the fields in this._exceptionsParams.
-   */  
-  _showExceptions: function (aPermissionType)
-  {
-    var bundlePreferences = document.getElementById("bundlePreferences");
-    var params = this._exceptionsParams[aPermissionType];
-    params.windowTitle = bundlePreferences.getString(aPermissionType + "permissionstitle");
-    params.introText = bundlePreferences.getString(aPermissionType + "permissionstext");
-    document.documentElement.openWindow("Browser:Permissions",
-                                        "chrome://browser/content/preferences/permissions.xul",
-                                        "", params);
-  },
-
   // BEGIN UI CODE
 
   /*
    * Preferences:
    *
    * dom.disable_open_during_load
    * - true if popups are blocked by default, false otherwise
    */
@@ -66,17 +44,23 @@ var gContentPane = {
   // POP-UPS
 
   /**
    * Displays the popup exceptions dialog where specific site popup preferences
    * can be set.
    */
   showPopupExceptions: function ()
   {
-    this._showExceptions("popup");
+    var bundlePreferences = document.getElementById("bundlePreferences");
+    var params = { blockVisible: false, sessionVisible: false, allowVisible: true, prefilledHost: "", permissionType: "popup" };
+    params.windowTitle = bundlePreferences.getString("popuppermissionstitle");
+    params.introText = bundlePreferences.getString("popuppermissionstext");
+    document.documentElement.openWindow("Browser:Permissions",
+                                        "chrome://browser/content/preferences/permissions.xul",
+                                        "", params);
   },
 
 
   // FONTS
 
   /**
    * Populates the default font list in UI.
    */
--- a/browser/components/preferences/fonts.xul
+++ b/browser/components/preferences/fonts.xul
@@ -222,16 +222,28 @@
               <menuitem value="14" label="14"/>
               <menuitem value="15" label="15"/>
               <menuitem value="16" label="16"/>
               <menuitem value="17" label="17"/>
               <menuitem value="18" label="18"/>
               <menuitem value="20" label="20"/>
               <menuitem value="22" label="22"/>
               <menuitem value="24" label="24"/>
+              <menuitem value="26" label="26"/>
+              <menuitem value="28" label="28"/>
+              <menuitem value="30" label="30"/>
+              <menuitem value="32" label="32"/>
+              <menuitem value="34" label="34"/>
+              <menuitem value="36" label="36"/>
+              <menuitem value="40" label="40"/>
+              <menuitem value="44" label="44"/>
+              <menuitem value="48" label="48"/>
+              <menuitem value="56" label="56"/>
+              <menuitem value="64" label="64"/>
+              <menuitem value="72" label="72"/>
             </menupopup>
           </menulist>
         </hbox>
       </hbox>
       <separator/>
       <separator class="groove"/>
       <hbox>
         <checkbox id="useDocumentFonts" 
--- a/browser/components/preferences/in-content/content.js
+++ b/browser/components/preferences/in-content/content.js
@@ -26,39 +26,16 @@ var gContentPane = {
   updateButtons: function (aButtonID, aPreferenceID)
   {
     var button = document.getElementById(aButtonID);
     var preference = document.getElementById(aPreferenceID);
     button.disabled = preference.value != true;
     return undefined;
   },
 
-  /**
-   * The exceptions types which may be passed to this._showExceptions().
-   */
-  _exceptionsParams: {
-    popup:   { blockVisible: false, sessionVisible: false, allowVisible: true, 
-               prefilledHost: "", permissionType: "popup" }
-  },
-
-  /**
-   * Displays the exceptions dialog of the given type, where types map onto the
-   * the fields in this._exceptionsParams.
-   */  
-  _showExceptions: function (aPermissionType)
-  {
-    var bundlePreferences = document.getElementById("bundlePreferences");
-    var params = this._exceptionsParams[aPermissionType];
-    params.windowTitle = bundlePreferences.getString(aPermissionType + "permissionstitle");
-    params.introText = bundlePreferences.getString(aPermissionType + "permissionstext");
-
-    openDialog("chrome://browser/content/preferences/permissions.xul", 
-               "Browser:Permissions", "resizable=yes", params);
-  },
-
   // BEGIN UI CODE
 
   /*
    * Preferences:
    *
    * dom.disable_open_during_load
    * - true if popups are blocked by default, false otherwise
    */
@@ -66,17 +43,24 @@ var gContentPane = {
   // POP-UPS
 
   /**
    * Displays the popup exceptions dialog where specific site popup preferences
    * can be set.
    */
   showPopupExceptions: function ()
   {
-    this._showExceptions("popup");
+    var bundlePreferences = document.getElementById("bundlePreferences");
+    var params = { blockVisible: false, sessionVisible: false, allowVisible: true,
+                   prefilledHost: "", permissionType: "popup" }
+    params.windowTitle = bundlePreferences.getString("popuppermissionstitle");
+    params.introText = bundlePreferences.getString("popuppermissionstext");
+
+    openDialog("chrome://browser/content/preferences/permissions.xul", 
+               "Browser:Permissions", "resizable=yes", params);
   },
 
   // FONTS
 
   /**
    * Populates the default font list in UI.
    */
   _rebuildFonts: function ()
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -1855,17 +1855,17 @@ let SessionStoreInternal = {
    * Collect data related to a single tab
    * @param aTab
    *        tabbrowser tab
    * @param aFullData
    *        always return privacy sensitive data (use with care)
    * @returns object
    */
   _collectTabData: function ssi_collectTabData(aTab, aFullData) {
-    var tabData = { entries: [] };
+    var tabData = { entries: [], lastAccessed: aTab.lastAccessed };
     var browser = aTab.linkedBrowser;
 
     if (!browser || !browser.currentURI)
       // can happen when calling this function right after .addTab()
       return tabData;
     else if (browser.__SS_data && browser.__SS_tabStillLoading) {
       // use the data to be restored when the tab hasn't been completely loaded
       tabData = browser.__SS_data;
@@ -3655,23 +3655,25 @@ let SessionStoreInternal = {
       if (oState.windows[i].isPrivate) {
         oState.windows.splice(i, 1);
         if (oState.selectedWindow >= i) {
           oState.selectedWindow--;
         }
       }
     }
 
+#ifndef XP_MACOSX
     // Don't save invalid states.
     // Looks like we currently have private windows, only.
     if (oState.windows.length == 0) {
       TelemetryStopwatch.cancel("FX_SESSION_RESTORE_COLLECT_DATA_MS");
       TelemetryStopwatch.cancel("FX_SESSION_RESTORE_COLLECT_DATA_LONGEST_OP_MS");
       return;
     }
+#endif
 
     for (let i = oState._closedWindows.length - 1; i >= 0; i--) {
       if (oState._closedWindows[i].isPrivate) {
         oState._closedWindows.splice(i, 1);
       }
     }
 
 #ifndef XP_MACOSX
--- a/browser/devtools/moz.build
+++ b/browser/devtools/moz.build
@@ -17,9 +17,11 @@ DIRS += [
     'debugger',
     'netmonitor',
     'layoutview',
     'shared',
     'responsivedesign',
     'framework',
     'profiler',
     'fontinspector',
+
 ]
+
--- a/browser/devtools/netmonitor/netmonitor-controller.js
+++ b/browser/devtools/netmonitor/netmonitor-controller.js
@@ -461,16 +461,53 @@ NetworkEventsHandler.prototype = {
    * @param object aResponse
    *        The message received from the server.
    */
   _onEventTimings: function NEH__onEventTimings(aResponse) {
     NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
       eventTimings: aResponse
     });
     window.emit("NetMonitor:NetworkEventUpdated:EventTimings");
+  },
+
+  /**
+   * Fetches the full text of a LongString.
+   *
+   * @param object | string aStringGrip
+   *        The long string grip containing the corresponding actor.
+   *        If you pass in a plain string (by accident or because you're lazy),
+   *        then a promise of the same string is simply returned.
+   * @return object Promise
+   *         A promise that is resolved when the full string contents
+   *         are available, or rejected if something goes wrong.
+   */
+  getString: function NEH_getString(aStringGrip) {
+    // Make sure this is a long string.
+    if (typeof aStringGrip != "object" || aStringGrip.type != "longString") {
+      return Promise.resolve(aStringGrip); // Go home string, you're drunk.
+    }
+    // Fetch the long string only once.
+    if (aStringGrip._fullText) {
+      return aStringGrip._fullText.promise;
+    }
+
+    let deferred = aStringGrip._fullText = Promise.defer();
+    let { actor, initial, length } = aStringGrip;
+    let longStringClient = this.webConsoleClient.longString(aStringGrip);
+
+    longStringClient.substring(initial.length, length, (aResponse) => {
+      if (aResponse.error) {
+        Cu.reportError(aResponse.error + ": " + aResponse.message);
+        deferred.reject(aResponse);
+        return;
+      }
+      deferred.resolve(initial + aResponse.substring);
+    });
+
+    return deferred.promise;
   }
 };
 
 /**
  * Localization convenience methods.
  */
 let L10N = new ViewHelpers.L10N(NET_STRINGS_URI);
 
@@ -492,17 +529,20 @@ EventEmitter.decorate(this);
 NetMonitorController.TargetEventsHandler = new TargetEventsHandler();
 NetMonitorController.NetworkEventsHandler = new NetworkEventsHandler();
 
 /**
  * Export some properties to the global scope for easier access.
  */
 Object.defineProperties(window, {
   "create": {
-    get: function() ViewHelpers.create,
+    get: function() ViewHelpers.create
+  },
+  "gNetwork": {
+    get: function() NetMonitorController.NetworkEventsHandler
   }
 });
 
 /**
  * Helper method for debugging.
  * @param string
  */
 function dumpn(str) {
--- a/browser/devtools/netmonitor/netmonitor-view.js
+++ b/browser/devtools/netmonitor/netmonitor-view.js
@@ -255,16 +255,17 @@ function RequestsMenuView() {
 create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
   /**
    * Initialization function, called when the network monitor is started.
    */
   initialize: function NVRM_initialize() {
     dumpn("Initializing the RequestsMenuView");
 
     this.node = new SideMenuWidget($("#requests-menu-contents"), false);
+    this.node.maintainSelectionVisible = false;
 
     this.node.addEventListener("mousedown", this._onMouseDown, false);
     this.node.addEventListener("select", this._onSelect, false);
     window.addEventListener("resize", this._onResize, false);
   },
 
   /**
    * Destruction function, called when the network monitor is closed.
@@ -880,17 +881,17 @@ create({ constructor: NetworkDetailsView
   _addHeaders: function NVND__addHeaders(aName, aResponse) {
     let kb = (aResponse.headersSize / 1024).toFixed(HEADERS_SIZE_DECIMALS);
     let size = L10N.getFormatStr("networkMenu.sizeKB", kb);
     let headersScope = this._headers.addScope(aName + " (" + size + ")");
     headersScope.expanded = true;
 
     for (let header of aResponse.headers) {
       let headerVar = headersScope.addVar(header.name, { null: true }, true);
-      headerVar.setGrip(header.value);
+      gNetwork.getString(header.value).then((aString) => headerVar.setGrip(aString));
     }
   },
 
   /**
    * Sets the network request cookies shown in this view.
    *
    * @param object aResponse
    *        The message received from the server.
@@ -923,17 +924,17 @@ create({ constructor: NetworkDetailsView
    *        The message received from the server.
    */
   _addCookies: function NVND__addCookies(aName, aResponse) {
     let cookiesScope = this._cookies.addScope(aName);
     cookiesScope.expanded = true;
 
     for (let cookie of aResponse.cookies) {
       let cookieVar = cookiesScope.addVar(cookie.name, { null: true }, true);
-      cookieVar.setGrip(cookie.value);
+      gNetwork.getString(cookie.value).then((aString) => cookieVar.setGrip(aString));
 
       // By default the cookie name and value are shown. If this is the only
       // information available, then nothing else is to be displayed.
       let cookieProps = Object.keys(cookie);
       if (cookieProps.length == 2) {
         continue;
       }
 
@@ -955,22 +956,17 @@ create({ constructor: NetworkDetailsView
    *
    * @param string aUrl
    *        The request's url.
    */
   _setRequestGetParams: function NVND__setRequestGetParams(aUrl) {
     let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
     let query = uri.query;
     if (query) {
-      this._addParams(this._paramsQueryString, query.split("&").map((e) =>
-        let (param = e.split("=")) {
-          name: param[0],
-          value: NetworkHelper.convertToUnicode(unescape(param[1]))
-        }
-      ));
+      this._addParams(this._paramsQueryString, query);
     }
   },
 
   /**
    * Sets the network request post params shown in this view.
    *
    * @param object aHeadersResponse
    *        The "requestHeaders" message received from the server.
@@ -979,52 +975,60 @@ create({ constructor: NetworkDetailsView
    */
   _setRequestPostParams: function NVND__setRequestPostParams(aHeadersResponse, aPostResponse) {
     if (!aHeadersResponse || !aPostResponse) {
       return;
     }
     let contentType = aHeadersResponse.headers.filter(({ name }) => name == "Content-Type")[0];
     let text = aPostResponse.postData.text;
 
-    if (contentType.value.contains("x-www-form-urlencoded")) {
-      this._addParams(this._paramsFormData, text.replace(/^[?&]/, "").split("&").map((e) =>
-        let (param = e.split("=")) {
-          name: param[0],
-          value: NetworkHelper.convertToUnicode(unescape(param[1]))
-        }
-      ));
-    } else {
-      // This is really awkward, but hey, it works. Let's show an empty
-      // scope in the params view and place the source editor containing
-      // the raw post data directly underneath.
-      $("#request-params-box").removeAttribute("flex");
-      let paramsScope = this._params.addScope(this._paramsPostPayload);
-      paramsScope.expanded = true;
-      paramsScope.locked = true;
+    gNetwork.getString(text).then((aString) => {
+      // Handle query strings (poor man's forms, e.g. "?foo=bar&baz=42").
+      if (contentType.value.contains("x-www-form-urlencoded")) {
+        this._addParams(this._paramsFormData, aString);
+      }
+      // Handle actual forms ("multipart/form-data" content type).
+      else {
+        // This is really awkward, but hey, it works. Let's show an empty
+        // scope in the params view and place the source editor containing
+        // the raw post data directly underneath.
+        $("#request-params-box").removeAttribute("flex");
+        let paramsScope = this._params.addScope(this._paramsPostPayload);
+        paramsScope.expanded = true;
+        paramsScope.locked = true;
 
-      $("#request-post-data-textarea-box").hidden = false;
-      NetMonitorView.editor("#request-post-data-textarea").then((aEditor) => {
-        aEditor.setText(text);
-      });
-    }
+        $("#request-post-data-textarea-box").hidden = false;
+        NetMonitorView.editor("#request-post-data-textarea").then((aEditor) => {
+          aEditor.setText(aString);
+        });
+      }
+      window.emit("NetMonitor:ResponsePostParamsAvailable");
+    });
   },
 
   /**
    * Populates the params container in this view with the specified data.
    *
    * @param string aName
    *        The type of params to populate (get or post).
-   * @param object aParams
-   *        An array containing { name: value } param information tuples.
+   * @param string aParams
+   *        A query string of params (e.g. "?foo=bar&baz=42").
    */
   _addParams: function NVND__addParams(aName, aParams) {
+    // Turn the params string into an array containing { name: value } tuples.
+    let paramsArray = aParams.replace(/^[?&]/, "").split("&").map((e) =>
+      let (param = e.split("=")) {
+        name: NetworkHelper.convertToUnicode(unescape(param[0])),
+        value: NetworkHelper.convertToUnicode(unescape(param[1]))
+      });
+
     let paramsScope = this._params.addScope(aName);
     paramsScope.expanded = true;
 
-    for (let param of aParams) {
+    for (let param of paramsArray) {
       let headerVar = paramsScope.addVar(param.name, { null: true }, true);
       headerVar.setGrip(param.value);
     }
   },
 
   /**
    * Sets the network response body shown in this view.
    *
@@ -1033,61 +1037,76 @@ create({ constructor: NetworkDetailsView
    * @param object aResponse
    *        The message received from the server.
    */
   _setResponseBody: function NVND__setresponseBody(aUrl, aResponse) {
     if (!aResponse) {
       return;
     }
     let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
-    let { mimeType: mime, text, encoding } = aResponse.content;
+    let { mimeType, text, encoding } = aResponse.content;
+
+    gNetwork.getString(text).then((aString) => {
+      // Handle json.
+      if (mimeType.contains("/json")) {
+        $("#response-content-json-box").hidden = false;
+        let jsonpRegex = /^[a-zA-Z0-9_$]+\(|\)$/g; // JSONP with callback.
+        let sanitizedJSON = aString.replace(jsonpRegex, "");
+        let callbackPadding = aString.match(jsonpRegex);
 
-    if (mime.contains("/json")) {
-      $("#response-content-json-box").hidden = false;
-      let jsonScope = this._json.addScope("JSON");
-      jsonScope.addVar().populate(JSON.parse(text), { expanded: true });
-      jsonScope.expanded = true;
-    }
-    else if (mime.contains("image/")) {
-      $("#response-content-image-box").setAttribute("align", "center");
-      $("#response-content-image-box").setAttribute("pack", "center");
-      $("#response-content-image-box").hidden = false;
-      $("#response-content-image").src = "data:" + mime + ";" + encoding + "," + text;
+        let jsonScopeName = callbackPadding
+          ? L10N.getFormatStr("jsonpScopeName", callbackPadding[0].slice(0, -1))
+          : L10N.getStr("jsonScopeName");
 
-      // Immediately display additional information about the image:
-      // file name, mime type and encoding.
-      $("#response-content-image-name-value").setAttribute("value", uri.fileName);
-      $("#response-content-image-mime-value").setAttribute("value", mime);
-      $("#response-content-image-encoding-value").setAttribute("value", encoding);
+        let jsonScope = this._json.addScope(jsonScopeName);
+        jsonScope.addVar().populate(JSON.parse(sanitizedJSON), { expanded: true });
+        jsonScope.expanded = true;
+      }
+      // Handle images.
+      else if (mimeType.contains("image/")) {
+        $("#response-content-image-box").setAttribute("align", "center");
+        $("#response-content-image-box").setAttribute("pack", "center");
+        $("#response-content-image-box").hidden = false;
+        $("#response-content-image").src =
+          "data:" + mimeType + ";" + encoding + "," + aString;
 
-      // Wait for the image to load in order to display the width and height.
-      $("#response-content-image").onload = (e) => {
-        // XUL images are majestic so they don't bother storing their dimensions
-        // in width and height attributes like the rest of the folk. Hack around
-        // this by getting the bounding client rect and subtracting the margins.
-        let { width, height } = e.target.getBoundingClientRect();
-        let dimensions = (width - 2) + " x " + (height - 2);
-        $("#response-content-image-dimensions-value").setAttribute("value", dimensions);
-      };
-    }
-    else {
-      $("#response-content-textarea-box").hidden = false;
-      NetMonitorView.editor("#response-content-textarea").then((aEditor) => {
-        aEditor.setMode(SourceEditor.MODES.TEXT);
-        aEditor.setText(typeof text == "string" ? text : text.initial);
+        // Immediately display additional information about the image:
+        // file name, mime type and encoding.
+        $("#response-content-image-name-value").setAttribute("value", uri.fileName);
+        $("#response-content-image-mime-value").setAttribute("value", mimeType);
+        $("#response-content-image-encoding-value").setAttribute("value", encoding);
 
-        // Maybe set a more appropriate mode in the Source Editor if possible.
-        for (let key in CONTENT_MIME_TYPE_MAPPINGS) {
-          if (mime.contains(key)) {
-            aEditor.setMode(CONTENT_MIME_TYPE_MAPPINGS[key]);
-            break;
+        // Wait for the image to load in order to display the width and height.
+        $("#response-content-image").onload = (e) => {
+          // XUL images are majestic so they don't bother storing their dimensions
+          // in width and height attributes like the rest of the folk. Hack around
+          // this by getting the bounding client rect and subtracting the margins.
+          let { width, height } = e.target.getBoundingClientRect();
+          let dimensions = (width - 2) + " x " + (height - 2);
+          $("#response-content-image-dimensions-value").setAttribute("value", dimensions);
+        };
+      }
+      // Handle anything else.
+      else {
+        $("#response-content-textarea-box").hidden = false;
+        NetMonitorView.editor("#response-content-textarea").then((aEditor) => {
+          aEditor.setMode(SourceEditor.MODES.TEXT);
+          aEditor.setText(aString);
+
+          // Maybe set a more appropriate mode in the Source Editor if possible.
+          for (let key in CONTENT_MIME_TYPE_MAPPINGS) {
+            if (mimeType.contains(key)) {
+              aEditor.setMode(CONTENT_MIME_TYPE_MAPPINGS[key]);
+              break;
+            }
           }
-        }
-      });
-    }
+        });
+      }
+      window.emit("NetMonitor:ResponseBodyAvailable");
+    });
   },
 
   /**
    * Sets the timings information shown in this view.
    *
    * @param object aResponse
    *        The message received from the server.
    */
--- a/browser/devtools/netmonitor/test/Makefile.in
+++ b/browser/devtools/netmonitor/test/Makefile.in
@@ -18,26 +18,30 @@ MOCHITEST_BROWSER_TESTS = \
 	browser_net_prefs-reload.js \
 	browser_net_pane-collapse.js \
 	browser_net_simple-request.js \
 	browser_net_simple-request-data.js \
 	browser_net_simple-request-details.js \
 	browser_net_content-type.js \
 	browser_net_status-codes.js \
 	browser_net_post-data.js \
+	browser_net_jsonp.js \
+	browser_net_json-long.js \
 	head.js \
 	$(NULL)
 
 MOCHITEST_BROWSER_PAGES = \
 	test-image.png \
 	html_simple-test-page.html \
 	html_navigate-test-page.html \
 	html_content-type-test-page.html \
 	html_status-codes-test-page.html \
 	html_post-data-test-page.html \
+	html_jsonp-test-page.html \
+	html_json-long-test-page.html \
 	sjs_simple-test-server.sjs \
 	sjs_content-type-test-server.sjs \
 	sjs_status-codes-test-server.sjs \
 	$(NULL)
 
 MOCHITEST_BROWSER_FILES_PARTS = MOCHITEST_BROWSER_TESTS MOCHITEST_BROWSER_PAGES
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/devtools/netmonitor/test/browser_net_content-type.js
+++ b/browser/devtools/netmonitor/test/browser_net_content-type.js
@@ -161,17 +161,18 @@ function test() {
             is(tabpanel.querySelectorAll(".variables-view-property").length, 2,
               "There should be 2 json properties displayed in this tabpanel.");
             is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0,
               "The empty notice should not be displayed in this tabpanel.");
 
             let jsonScope = tabpanel.querySelectorAll(".variables-view-scope")[0];
 
             is(jsonScope.querySelector(".name").getAttribute("value"),
-              "JSON", "The json scope doesn't have the correct title.");
+              L10N.getStr("jsonScopeName"),
+              "The json scope doesn't have the correct title.");
 
             is(jsonScope.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"),
               "greeting", "The first json property name was incorrect.");
             is(jsonScope.querySelectorAll(".variables-view-property .value")[0].getAttribute("value"),
               "\"Hello JSON!\"", "The first json property value was incorrect.");
 
             is(jsonScope.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"),
               "__proto__", "The second json property name was incorrect.");
new file mode 100644
--- /dev/null
+++ b/browser/devtools/netmonitor/test/browser_net_json-long.js
@@ -0,0 +1,95 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests if very long JSON responses are handled correctly.
+ */
+
+function test() {
+  initNetMonitor(JSON_LONG_URL).then(([aTab, aDebuggee, aMonitor]) => {
+    info("Starting test... ");
+
+    // This is receiving over 80 KB of json and will populate over 6000 items
+    // in a variables view instance. Debug builds are slow.
+    requestLongerTimeout(2);
+
+    let { document, L10N, SourceEditor, NetMonitorView } = aMonitor.panelWin;
+    let { RequestsMenu } = NetMonitorView;
+
+    RequestsMenu.lazyUpdate = false;
+
+    waitForNetworkEvents(aMonitor, 1).then(() => {
+      verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
+        "GET", CONTENT_TYPE_SJS + "?fmt=json-long", {
+          status: 200,
+          statusText: "OK",
+          type: "json",
+          fullMimeType: "text/json; charset=utf-8",
+          size: L10N.getFormatStr("networkMenu.sizeKB", 83.96),
+          time: true
+        });
+
+      EventUtils.sendMouseEvent({ type: "mousedown" },
+        document.getElementById("details-pane-toggle"));
+      EventUtils.sendMouseEvent({ type: "mousedown" },
+        document.querySelectorAll("#details-pane tab")[3]);
+
+      aMonitor.panelWin.once("NetMonitor:ResponseBodyAvailable", () => {
+        testResponseTab();
+        teardown(aMonitor).then(finish);
+      });
+
+      function testResponseTab() {
+        let tab = document.querySelectorAll("#details-pane tab")[3];
+        let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3];
+
+        is(tab.getAttribute("selected"), "true",
+          "The response tab in the network details pane should be selected.");
+
+        is(tabpanel.querySelector("#response-content-json-box")
+          .hasAttribute("hidden"), false,
+          "The response content json box doesn't have the intended visibility.");
+
+        is(tabpanel.querySelector("#response-content-textarea-box")
+          .hasAttribute("hidden"), true,
+          "The response content textarea box doesn't have the intended visibility.");
+
+        is(tabpanel.querySelector("#response-content-image-box")
+          .hasAttribute("hidden"), true,
+          "The response content image box doesn't have the intended visibility.");
+
+        is(tabpanel.querySelectorAll(".variables-view-scope").length, 1,
+          "There should be 1 json scope displayed in this tabpanel.");
+        is(tabpanel.querySelectorAll(".variables-view-property").length, 6057,
+          "There should be 6057 json properties displayed in this tabpanel.");
+        is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0,
+          "The empty notice should not be displayed in this tabpanel.");
+
+        let jsonScope = tabpanel.querySelectorAll(".variables-view-scope")[0];
+        let names = ".variables-view-property .name";
+        let values = ".variables-view-property .value";
+
+        is(jsonScope.querySelector(".name").getAttribute("value"),
+          L10N.getStr("jsonScopeName"),
+          "The json scope doesn't have the correct title.");
+
+        is(jsonScope.querySelectorAll(names)[0].getAttribute("value"),
+          "0", "The first json property name was incorrect.");
+        is(jsonScope.querySelectorAll(values)[0].getAttribute("value"),
+          "[object Object]", "The first json property value was incorrect.");
+
+        is(jsonScope.querySelectorAll(names)[1].getAttribute("value"),
+          "greeting", "The second json property name was incorrect.");
+        is(jsonScope.querySelectorAll(values)[1].getAttribute("value"),
+          "\"Hello long string JSON!\"", "The second json property value was incorrect.");
+
+        is(Array.slice(jsonScope.querySelectorAll(names), -1).shift().getAttribute("value"),
+          "__proto__", "The last json property name was incorrect.");
+        is(Array.slice(jsonScope.querySelectorAll(values), -1).shift().getAttribute("value"),
+          "[object Object]", "The last json property value was incorrect.");
+      }
+    });
+
+    aDebuggee.performRequests();
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/netmonitor/test/browser_net_jsonp.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests if JSONP responses are handled correctly.
+ */
+
+function test() {
+  initNetMonitor(JSONP_URL).then(([aTab, aDebuggee, aMonitor]) => {
+    info("Starting test... ");
+
+    let { document, L10N, SourceEditor, NetMonitorView } = aMonitor.panelWin;
+    let { RequestsMenu } = NetMonitorView;
+
+    RequestsMenu.lazyUpdate = false;
+
+    waitForNetworkEvents(aMonitor, 1).then(() => {
+      verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
+        "GET", CONTENT_TYPE_SJS + "?fmt=jsonp&jsonp=$_0123Fun", {
+          status: 200,
+          statusText: "OK",
+          type: "json",
+          fullMimeType: "text/json; charset=utf-8",
+          size: L10N.getFormatStr("networkMenu.sizeKB", 0.04),
+          time: true
+        });
+
+      EventUtils.sendMouseEvent({ type: "mousedown" },
+        document.getElementById("details-pane-toggle"));
+      EventUtils.sendMouseEvent({ type: "mousedown" },
+        document.querySelectorAll("#details-pane tab")[3]);
+
+      testResponseTab();
+      teardown(aMonitor).then(finish);
+
+      function testResponseTab() {
+        let tab = document.querySelectorAll("#details-pane tab")[3];
+        let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3];
+
+        is(tab.getAttribute("selected"), "true",
+          "The response tab in the network details pane should be selected.");
+
+        is(tabpanel.querySelector("#response-content-json-box")
+          .hasAttribute("hidden"), false,
+          "The response content json box doesn't have the intended visibility.");
+
+        is(tabpanel.querySelector("#response-content-textarea-box")
+          .hasAttribute("hidden"), true,
+          "The response content textarea box doesn't have the intended visibility.");
+
+        is(tabpanel.querySelector("#response-content-image-box")
+          .hasAttribute("hidden"), true,
+          "The response content image box doesn't have the intended visibility.");
+
+        is(tabpanel.querySelectorAll(".variables-view-scope").length, 1,
+          "There should be 1 json scope displayed in this tabpanel.");
+        is(tabpanel.querySelectorAll(".variables-view-property").length, 2,
+          "There should be 2 json properties displayed in this tabpanel.");
+        is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0,
+          "The empty notice should not be displayed in this tabpanel.");
+
+        let jsonScope = tabpanel.querySelectorAll(".variables-view-scope")[0];
+
+        is(jsonScope.querySelector(".name").getAttribute("value"),
+          L10N.getFormatStr("jsonpScopeName", "$_0123Fun"),
+          "The json scope doesn't have the correct title.");
+
+        is(jsonScope.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"),
+          "greeting", "The first json property name was incorrect.");
+        is(jsonScope.querySelectorAll(".variables-view-property .value")[0].getAttribute("value"),
+          "\"Hello JSONP!\"", "The first json property value was incorrect.");
+
+        is(jsonScope.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"),
+          "__proto__", "The second json property name was incorrect.");
+        is(jsonScope.querySelectorAll(".variables-view-property .value")[1].getAttribute("value"),
+          "[object Object]", "The second json property value was incorrect.");
+      }
+    });
+
+    aDebuggee.performRequests();
+  });
+}
--- a/browser/devtools/netmonitor/test/head.js
+++ b/browser/devtools/netmonitor/test/head.js
@@ -11,16 +11,18 @@ let { gDevTools } = Cu.import("resource:
 
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/netmonitor/test/";
 
 const SIMPLE_URL = EXAMPLE_URL + "html_simple-test-page.html";
 const NAVIGATE_URL = EXAMPLE_URL + "html_navigate-test-page.html";
 const CONTENT_TYPE_URL = EXAMPLE_URL + "html_content-type-test-page.html";
 const STATUS_CODES_URL = EXAMPLE_URL + "html_status-codes-test-page.html";
 const POST_DATA_URL = EXAMPLE_URL + "html_post-data-test-page.html";
+const JSONP_URL = EXAMPLE_URL + "html_jsonp-test-page.html";
+const JSON_LONG_URL = EXAMPLE_URL + "html_json-long-test-page.html";
 
 const SIMPLE_SJS = EXAMPLE_URL + "sjs_simple-test-server.sjs";
 const CONTENT_TYPE_SJS = EXAMPLE_URL + "sjs_content-type-test-server.sjs";
 const STATUS_CODES_SJS = EXAMPLE_URL + "sjs_status-codes-test-server.sjs";
 
 const TEST_IMAGE = EXAMPLE_URL + "test-image.png";
 
 // All tests are asynchronous.
new file mode 100644
--- /dev/null
+++ b/browser/devtools/netmonitor/test/html_json-long-test-page.html
@@ -0,0 +1,33 @@
+<!doctype html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Network Monitor test page</title>
+  </head>
+
+  <body>
+    <p>JSON long string test</p>
+
+    <script type="text/javascript">
+      function get(aAddress, aCallback) {
+        var xhr = new XMLHttpRequest();
+        xhr.open("GET", aAddress, true);
+
+        xhr.onreadystatechange = function() {
+          if (this.readyState == this.DONE) {
+            aCallback();
+          }
+        };
+        xhr.send(null);
+      }
+
+      function performRequests() {
+        get("sjs_content-type-test-server.sjs?fmt=json-long", function() {
+          // Done.
+        });
+      }
+    </script>
+  </body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/netmonitor/test/html_jsonp-test-page.html
@@ -0,0 +1,33 @@
+<!doctype html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Network Monitor test page</title>
+  </head>
+
+  <body>
+    <p>JSONP test</p>
+
+    <script type="text/javascript">
+      function get(aAddress, aCallback) {
+        var xhr = new XMLHttpRequest();
+        xhr.open("GET", aAddress, true);
+
+        xhr.onreadystatechange = function() {
+          if (this.readyState == this.DONE) {
+            aCallback();
+          }
+        };
+        xhr.send(null);
+      }
+
+      function performRequests() {
+        get("sjs_content-type-test-server.sjs?fmt=jsonp&jsonp=$_0123Fun", function() {
+          // Done.
+        });
+      }
+    </script>
+  </body>
+
+</html>
--- a/browser/devtools/netmonitor/test/sjs_content-type-test-server.sjs
+++ b/browser/devtools/netmonitor/test/sjs_content-type-test-server.sjs
@@ -6,41 +6,62 @@ const { classes: Cc, interfaces: Ci } = 
 function handleRequest(request, response) {
   response.processAsync();
 
   let params = request.queryString.split("&");
   let format = params.filter((s) => s.contains("fmt="))[0].split("=")[1];
 
   Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer).initWithCallback(() => {
     switch (format) {
-      case "xml":
+      case "xml": {
         response.setStatusLine(request.httpVersion, 200, "OK");
         response.setHeader("Content-Type", "text/xml; charset=utf-8", false);
         response.write("<label value='greeting'>Hello XML!</label>");
         response.finish();
         break;
-      case "css":
+      }
+      case "css": {
         response.setStatusLine(request.httpVersion, 200, "OK");
         response.setHeader("Content-Type", "text/css; charset=utf-8", false);
         response.write("body:pre { content: 'Hello CSS!' }");
         response.finish();
         break;
-      case "js":
+      }
+      case "js": {
         response.setStatusLine(request.httpVersion, 200, "OK");
         response.setHeader("Content-Type", "application/javascript; charset=utf-8", false);
         response.write("function() { return 'Hello JS!'; }");
         response.finish();
         break;
-      case "json":
+      }
+      case "json": {
         response.setStatusLine(request.httpVersion, 200, "OK");
         response.setHeader("Content-Type", "application/json; charset=utf-8", false);
         response.write("{ \"greeting\": \"Hello JSON!\" }");
         response.finish();
         break;
-      default:
+      }
+      case "jsonp": {
+        let fun = params.filter((s) => s.contains("jsonp="))[0].split("=")[1];
+        response.setStatusLine(request.httpVersion, 200, "OK");
+        response.setHeader("Content-Type", "text/json; charset=utf-8", false);
+        response.write(fun + "({ \"greeting\": \"Hello JSONP!\" })");
+        response.finish();
+        break;
+      }
+      case "json-long": {
+        let str = "{ \"greeting\": \"Hello long string JSON!\" },";
+        response.setStatusLine(request.httpVersion, 200, "OK");
+        response.setHeader("Content-Type", "text/json; charset=utf-8", false);
+        response.write("[" + new Array(2048).join(str).slice(0, -1) + "]");
+        response.finish();
+        break;
+      }
+      default: {
         response.setStatusLine(request.httpVersion, 404, "Not Found");
         response.setHeader("Content-Type", "text/html; charset=utf-8", false);
         response.write("<blink>Not Found</blink>");
         response.finish();
         break;
+      }
     }
   }, 10, Ci.nsITimer.TYPE_ONE_SHOT); // Make sure this request takes a few ms.
 }
--- a/browser/devtools/shared/widgets/SideMenuWidget.jsm
+++ b/browser/devtools/shared/widgets/SideMenuWidget.jsm
@@ -59,16 +59,22 @@ this.SideMenuWidget = function SideMenuW
   ViewHelpers.delegateWidgetEventMethods(this, aNode);
 };
 
 SideMenuWidget.prototype = {
   get document() this._parent.ownerDocument,
   get window() this.document.defaultView,
 
   /**
+   * Specifies if this container should try to keep the selected item visible.
+   * (For example, when new items are added the selection is brought into view).
+   */
+  maintainSelectionVisible: true,
+
+  /**
    * Specifies if groups in this container should be sorted alphabetically.
    */
   sortedGroups: true,
 
   /**
    * Inserts an item in this container at the specified index, optionally
    * grouping by name.
    *
@@ -79,17 +85,19 @@ SideMenuWidget.prototype = {
    * @param string aTooltip [optional]
    *        A tooltip attribute for the displayed item.
    * @param string aGroup [optional]
    *        The group to place the displayed item into.
    * @return nsIDOMNode
    *         The element associated with the displayed item.
    */
   insertItemAt: function SMW_insertItemAt(aIndex, aContents, aTooltip = "", aGroup = "") {
-    this.ensureSelectionIsVisible(true, true); // Don't worry, it's delayed.
+    if (this.maintainSelectionVisible) {
+      this.ensureSelectionIsVisible(true, true); // Don't worry, it's delayed.
+    }
 
     let group = this._getGroupForName(aGroup);
     return group.insertItemAt(aIndex, aContents, aTooltip, this._showArrows);
   },
 
   /**
    * Returns the child node in this container situated at the specified index.
    *
--- a/browser/devtools/webconsole/test/Makefile.in
+++ b/browser/devtools/webconsole/test/Makefile.in
@@ -115,16 +115,17 @@ MOCHITEST_BROWSER_FILES = \
 	browser_output_longstring_expand.js \
 	browser_netpanel_longstring_expand.js \
 	browser_repeated_messages_accuracy.js \
 	browser_webconsole_bug_821877_csp_errors.js \
 	browser_eval_in_debugger_stackframe.js \
 	browser_console_variables_view.js \
 	browser_console_variables_view_while_debugging.js \
 	browser_console.js \
+	browser_longstring_hang.js \
 	head.js \
 	$(NULL)
 
 ifeq ($(OS_ARCH), Darwin)
 MOCHITEST_BROWSER_FILES += \
         browser_webconsole_bug_804845_ctrl_key_nav.js \
         $(NULL)
 endif
@@ -216,11 +217,12 @@ MOCHITEST_BROWSER_FILES += \
 	test-bug-737873-mixedcontent.html \
 	test-repeated-messages.html \
 	test-bug-766001-console-log.js \
 	test-bug-766001-js-console-links.html \
 	test-bug-766001-js-errors.js \
 	test-bug-821877-csperrors.html \
 	test-bug-821877-csperrors.html^headers^ \
 	test-eval-in-stackframe.html \
+	test-bug-859170-longstring-hang.html \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_longstring_hang.js
@@ -0,0 +1,88 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that very long strings do not hang the browser.
+
+function test()
+{
+  waitForExplicitFinish();
+
+  let DebuggerServer = Cu.import("resource://gre/modules/devtools/dbg-server.jsm",
+                                 {}).DebuggerServer;
+
+  addTab("http://example.com/browser/browser/devtools/webconsole/test/test-bug-859170-longstring-hang.html");
+
+  let hud = null;
+
+  gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+    openConsole(null, performTest);
+  }, true);
+
+  function performTest(aHud)
+  {
+    hud = aHud;
+
+    info("wait for the initial long string");
+
+    waitForMessages({
+      webconsole: hud,
+      messages: [
+        {
+          name: "find 'foobar', no 'foobaz', in long string output",
+          text: "foobar",
+          noText: "foobaz",
+          category: CATEGORY_WEBDEV,
+          longString: true,
+        },
+      ],
+    }).then(onInitialString);
+  }
+
+  function onInitialString(aResults)
+  {
+    let msg = [...aResults[0].matched][0];
+    ok(msg, "console.log result message element");
+
+    let clickable = msg.querySelector(".longStringEllipsis");
+    ok(clickable, "long string ellipsis is shown");
+
+    scrollToVisible(clickable);
+
+    executeSoon(() => {
+      EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow);
+
+      info("wait for long string expansion");
+
+      waitForMessages({
+        webconsole: hud,
+        messages: [
+          {
+            name: "find 'foobaz' after expand, but no 'boom!' at the end",
+            text: "foobaz",
+            noText: "boom!",
+            category: CATEGORY_WEBDEV,
+            longString: false,
+          },
+          {
+            text: "too long to be displayed",
+            longString: false,
+          },
+        ],
+      }).then(finishTest);
+    });
+  }
+
+  function scrollToVisible(aNode)
+  {
+    let richListBoxNode = aNode.parentNode;
+    while (richListBoxNode.tagName != "richlistbox") {
+      richListBoxNode = richListBoxNode.parentNode;
+    }
+
+    let boxObject = richListBoxNode.scrollBoxObject;
+    let nsIScrollBoxObject = boxObject.QueryInterface(Ci.nsIScrollBoxObject);
+    nsIScrollBoxObject.ensureElementIsVisible(aNode);
+  }
+}
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_598357_jsterm_output.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_598357_jsterm_output.js
@@ -231,18 +231,16 @@ function testGen() {
 
     HUD.jsterm.off("variablesview-open", propertyPanelShown);
 
     eventHandlers[eventHandlerID] = null;
 
     ok(showsPropertyPanel,
       "the property panel shown for inputValues[" + cpos + "]");
 
-    HUD.jsterm._splitter.state = "collapsed";
-
     popupShown[cpos] = true;
 
     if (showsPropertyPanel) {
       executeSoon(subtestNext);
     }
   };
 
   HUD.jsterm.on("variablesview-open", propertyPanelShown);
--- a/browser/devtools/webconsole/test/head.js
+++ b/browser/devtools/webconsole/test/head.js
@@ -871,30 +871,38 @@ function waitForMessages(aOptions)
 {
   gPendingOutputTest++;
   let webconsole = aOptions.webconsole;
   let rules = WebConsoleUtils.cloneObject(aOptions.messages, true);
   let rulesMatched = 0;
   let listenerAdded = false;
   let deferred = Promise.defer();
 
+  function checkText(aRule, aText)
+  {
+    let result;
+    if (typeof aRule == "string") {
+      result = aText.indexOf(aRule) > -1;
+    }
+    else if (aRule instanceof RegExp) {
+      result = aRule.test(aText);
+    }
+    return result;
+  }
+
   function checkMessage(aRule, aElement)
   {
-    if (aRule.text) {
-      let elemText = getMessageElementText(aElement);
-      let matched = false;
-      if (typeof aRule.text == "string") {
-        matched = elemText.indexOf(aRule.text) > -1;
-      }
-      else if (aRule.text instanceof RegExp) {
-        matched = aRule.text.test(elemText);
-      }
-      if (!matched) {
-        return false;
-      }
+    let elemText = getMessageElementText(aElement);
+
+    if (aRule.text && !checkText(aRule.text, elemText)) {
+      return false;
+    }
+
+    if (aRule.noText && checkText(aRule.noText, elemText)) {
+      return false;
     }
 
     if (aRule.category) {
       if (aElement.category != aRule.category) {
         return false;
       }
     }
 
@@ -906,16 +914,21 @@ function waitForMessages(aOptions)
 
     if (aRule.repeats) {
       let repeats = aElement.querySelector(".webconsole-msg-repeat");
       if (!repeats || repeats.getAttribute("value") != aRule.repeats) {
         return false;
       }
     }
 
+    let longString = !!aElement.querySelector(".longStringEllipsis");
+    if ("longString" in aRule && aRule.longString != longString) {
+      return false;
+    }
+
     let count = aRule.count || 1;
     if (!aRule.matched) {
       aRule.matched = new Set();
     }
     aRule.matched.add(aElement);
 
     return aRule.matched.size == count;
   }
@@ -941,16 +954,17 @@ function waitForMessages(aOptions)
     }
   }
 
   function maybeDone()
   {
     if (rulesMatched == rules.length) {
       if (listenerAdded) {
         webconsole.ui.off("messages-added", onMessagesAdded);
+        webconsole.ui.off("messages-updated", onMessagesAdded);
       }
       gPendingOutputTest--;
       deferred.resolve(rules);
       return true;
     }
     return false;
   }
 
@@ -960,29 +974,30 @@ function waitForMessages(aOptions)
     }
 
     if (webconsole.ui) {
       webconsole.ui.off("messages-added", onMessagesAdded);
     }
 
     for (let rule of rules) {
       if (!rule._ruleMatched) {
-        console.log("failed to match rule: " + displayRule(rule));
+        ok(false, "failed to match rule: " + displayRule(rule));
       }
     }
   }
 
   function displayRule(aRule)
   {
     return aRule.name || aRule.text;
   }
 
   executeSoon(() => {
     onMessagesAdded("messages-added", webconsole.outputNode.childNodes);
     if (rulesMatched != rules.length) {
       listenerAdded = true;
       registerCleanupFunction(testCleanup);
       webconsole.ui.on("messages-added", onMessagesAdded);
+      webconsole.ui.on("messages-updated", onMessagesAdded);
     }
   });
 
   return deferred.promise;
 }
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/test-bug-859170-longstring-hang.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head><meta charset="utf-8">
+    <title>Web Console test for bug 859170 - very long strings hang the browser</title>
+    <!-- Any copyright is dedicated to the Public Domain.
+         http://creativecommons.org/publicdomain/zero/1.0/ -->
+<script type="application/javascript">
+(function() {
+var longString = "abbababazomglolztest";
+for (var i = 0; i < 10; i++) {
+  longString += longString + longString;
+}
+
+longString = "foobar" + (new Array(20000)).join("a") + "foobaz" +
+             longString + "boom!";
+console.log(longString);
+})();
+</script>
+  </head>
+  <body>
+    <p>Web Console test for bug 859170 - very long strings hang the browser.</p>
+  </body>
+</html>
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -166,16 +166,19 @@ const OUTPUT_INTERVAL = 50; // milliseco
 const THROTTLE_UPDATES = 1000; // milliseconds
 
 // The preference prefix for all of the Web Console filters.
 const FILTER_PREFS_PREFIX = "devtools.webconsole.filter.";
 
 // The minimum font size.
 const MIN_FONT_SIZE = 10;
 
+// The maximum length of strings to be displayed by the Web Console.
+const MAX_LONG_STRING_LENGTH = 200000;
+
 const PREF_CONNECTION_TIMEOUT = "devtools.debugger.remote-timeout";
 
 /**
  * A WebConsoleFrame instance is an interactive console initialized *per target*
  * that displays console log data as well as provides an interactive terminal to
  * manipulate the target's document content.
  *
  * The WebConsoleFrame is responsible for the actual Web Console UI
@@ -1314,27 +1317,37 @@ WebConsoleFrame.prototype = {
    *        The file URI that was requested.
    */
   handleFileActivity: function WCF_handleFileActivity(aFileURI)
   {
     this.outputMessage(CATEGORY_NETWORK, this.logFileActivity, [aFileURI]);
   },
 
   /**
-   * Inform user that the Web Console API has been replaced by a script
+   * Inform user that the window.console API has been replaced by a script
    * in a content page.
    */
   logWarningAboutReplacedAPI: function WCF_logWarningAboutReplacedAPI()
   {
     let node = this.createMessageNode(CATEGORY_JS, SEVERITY_WARNING,
                                       l10n.getStr("ConsoleAPIDisabled"));
     this.outputMessage(CATEGORY_JS, node);
   },
 
   /**
+   * Inform user that the string he tries to view is too long.
+   */
+  logWarningAboutStringTooLong: function WCF_logWarningAboutStringTooLong()
+  {
+    let node = this.createMessageNode(CATEGORY_JS, SEVERITY_WARNING,
+                                      l10n.getStr("longStringTooLong"));
+    this.outputMessage(CATEGORY_JS, node);
+  },
+
+  /**
    * Handle the network events coming from the remote Web Console.
    *
    * @param object aActor
    *        The NetworkEventActor grip.
    */
   handleNetworkEvent: function WCF_handleNetworkEvent(aActor)
   {
     let networkInfo = {
@@ -1690,21 +1703,27 @@ WebConsoleFrame.prototype = {
     let outputNode = this.outputNode;
     let lastVisibleNode = null;
     let scrolledToBottom = Utils.isOutputScrolledToBottom(outputNode);
     let scrollBox = outputNode.scrollBoxObject.element;
 
     let hudIdSupportsString = WebConsoleUtils.supportsString(this.hudId);
 
     // Output the current batch of messages.
-    let newOrUpdatedNodes = new Set();
+    let newMessages = new Set();
+    let updatedMessages = new Set();
     for (let item of batch) {
       let result = this._outputMessageFromQueue(hudIdSupportsString, item);
       if (result) {
-        newOrUpdatedNodes.add(result.isRepeated || result.node);
+        if (result.isRepeated) {
+          updatedMessages.add(result.isRepeated);
+        }
+        else {
+          newMessages.add(result.node);
+        }
         if (result.visible && result.node == this.outputNode.lastChild) {
           lastVisibleNode = result.node;
         }
       }
     }
 
     let oldScrollHeight = 0;
 
@@ -1738,17 +1757,22 @@ WebConsoleFrame.prototype = {
     }
     else if (!scrolledToBottom && removedNodes > 0 &&
              oldScrollHeight != scrollBox.scrollHeight) {
       // If there were pruned messages and if scroll is not at the bottom, then
       // we need to adjust the scroll location.
       scrollBox.scrollTop -= oldScrollHeight - scrollBox.scrollHeight;
     }
 
-    this.emit("messages-added", newOrUpdatedNodes);
+    if (newMessages.size) {
+      this.emit("messages-added", newMessages);
+    }
+    if (updatedMessages.size) {
+      this.emit("messages-updated", updatedMessages);
+    }
 
     // If the queue is not empty, schedule another flush.
     if (this._outputQueue.length > 0) {
       this._initOutputTimer();
     }
     else {
       this._outputTimerInitialized = false;
       this._flushCallback && this._flushCallback();
@@ -2250,33 +2274,40 @@ WebConsoleFrame.prototype = {
   {
     aEvent.preventDefault();
 
     if (!aFormatter) {
       aFormatter = function(s) s;
     }
 
     let longString = this.webConsoleClient.longString(aActor);
-    longString.substring(longString.initial.length, longString.length,
+    let toIndex = Math.min(longString.length, MAX_LONG_STRING_LENGTH);
+    longString.substring(longString.initial.length, toIndex,
       function WCF__onSubstring(aResponse) {
         if (aResponse.error) {
           Cu.reportError("WCF__longStringClick substring failure: " +
                          aResponse.error);
           return;
         }
 
         let node = aEllipsis.previousSibling;
         node.textContent = aFormatter(longString.initial + aResponse.substring);
         aEllipsis.parentNode.removeChild(aEllipsis);
 
         if (aMessage.category == CATEGORY_WEBDEV ||
             aMessage.category == CATEGORY_OUTPUT) {
           aMessage.clipboardText = aMessage.textContent;
         }
-      });
+
+        this.emit("messages-updated", new Set([aMessage]));
+
+        if (toIndex != longString.length) {
+          this.logWarningAboutStringTooLong();
+        }
+      }.bind(this));
   },
 
   /**
    * Creates the XUL label that displays the textual location of an incoming
    * message.
    *
    * @param string aSourceURL
    *        The URL of the source file responsible for the error.
@@ -2626,23 +2657,16 @@ JSTerm.prototype = {
   /**
    * The Web Console sidebar.
    * @see this._createSidebar()
    * @see Sidebar.jsm
    */
   sidebar: null,
 
   /**
-   * The Web Console splitter between output and the sidebar.
-   * @private
-   * @type nsIDOMElement
-   */
-  _splitter: null,
-
-  /**
    * The Variables View instance shown in the sidebar.
    * @private
    * @type object
    */
   _variablesView: null,
 
   /**
    * Tells if you want the variables view UI updates to be lazy or not. Tests
@@ -2716,18 +2740,16 @@ JSTerm.prototype = {
 
     let doc = this.hud.document;
     this.completeNode = doc.querySelector(".jsterm-complete-node");
     this.inputNode = doc.querySelector(".jsterm-input-node");
     this.inputNode.addEventListener("keypress", this._keyPress, false);
     this.inputNode.addEventListener("input", this._inputEventHandler, false);
     this.inputNode.addEventListener("keyup", this._inputEventHandler, false);
 
-    this._splitter = doc.querySelector(".devtools-side-splitter");
-
     this.lastInputValue && this.setInputValue(this.lastInputValue);
   },
 
   /**
    * The JavaScript evaluation response handler.
    *
    * @private
    * @param nsIDOMElement [aAfterNode]
@@ -3034,17 +3056,16 @@ JSTerm.prototype = {
    */
   _createSidebar: function JST__createSidebar()
   {
     if (!this.sidebar) {
       let tabbox = this.hud.document.querySelector("#webconsole-sidebar");
       this.sidebar = new ToolSidebar(tabbox, this);
     }
     this.sidebar.show();
-    this._splitter.setAttribute("state", "open");
   },
 
   /**
    * Add the variables view tab to the sidebar.
    *
    * @private
    * @return object
    *         A Promise object for the adding of the new tab.
@@ -3472,27 +3493,32 @@ JSTerm.prototype = {
     aVar._fetched = true;
 
     let grip = aVar.value;
     if (!grip) {
       throw new Error("No long string actor grip was given for the variable.");
     }
 
     let client = this.webConsoleClient.longString(grip);
-    client.substring(grip.initial.length, grip.length, (aResponse) => {
+    let toIndex = Math.min(grip.length, MAX_LONG_STRING_LENGTH);
+    client.substring(grip.initial.length, toIndex, (aResponse) => {
       if (aResponse.error) {
         Cu.reportError("JST__fetchVarLongString substring failure: " +
                        aResponse.error + ": " + aResponse.message);
         return;
       }
 
       aVar.onexpand = null;
       aVar.setGrip(grip.initial + aResponse.substring);
       aVar.hideArrow();
       aVar._retrieved = true;
+
+      if (toIndex != grip.length) {
+        this.hud.logWarningAboutStringTooLong();
+      }
     });
   },
 
   /**
    * Writes a JS object to the JSTerm outputNode.
    *
    * @param string aOutputMessage
    *        The message to display.
--- a/browser/devtools/webconsole/webconsole.xul
+++ b/browser/devtools/webconsole/webconsole.xul
@@ -134,16 +134,16 @@
         <stack class="jsterm-stack-node" flex="1">
           <textbox class="jsterm-complete-node" multiline="true" rows="1"
                    tabindex="-1"/>
           <textbox class="jsterm-input-node" multiline="true" rows="1"/>
         </stack>
       </hbox>
     </vbox>
 
-    <splitter class="devtools-side-splitter" collapse="after" state="collapsed" />
+    <splitter class="devtools-side-splitter"/>
 
     <tabbox id="webconsole-sidebar" class="devtools-sidebar-tabs" hidden="true" width="300">
       <tabs/>
       <tabpanels flex="1"/>
     </tabbox>
   </hbox>
 </window>
--- a/browser/fuel/test/browser_ApplicationPrefs.js
+++ b/browser/fuel/test/browser_ApplicationPrefs.js
@@ -1,15 +1,15 @@
 // The various properties that we'll be testing
 var testdata = {
   missing: "fuel.fuel-test-missing",
   dummy: "fuel.fuel-test",
   string: "browser.active_color",
   integer: "permissions.default.image",
-  boolean: "browser.blink_allowed"
+  boolean: "browser.underline_anchors"
 };
 
 function test() {
   // test getting nonexistent values
   var itemValue = Application.prefs.getValue(testdata.missing, "default");
   is(itemValue, "default", "Check 'Application.prefs.getValue' for nonexistent item");
 
   is(Application.prefs.get(testdata.missing), null, "Check 'Application.prefs.get' for nonexistent item");
@@ -86,17 +86,17 @@ function test() {
   var type = Application.prefs.get(testdata.integer).type;
   is(type, "Number", "Check 'Application.prefs.get().type' for integer pref");
 
   // test resetting an existing integer property
   Application.prefs.get(testdata.integer).reset();
   var val = Application.prefs.getValue(testdata.integer, 0);
   is(val, 1, "Reset existing integer property");
 
-  // PREF: boolean browser.blink_allowed == true
+  // PREF: boolean browser.underline_anchors == true
 
   // test to see if an existing boolean property exists
   ok(Application.prefs.has(testdata.boolean), "Check existing boolean property for existence");
 
   // test accessing a non-existant boolean property
   var val = Application.prefs.getValue(testdata.dummy, true);
   ok(val, "Check non-existant boolean property for expected value");
 
--- a/browser/locales/Makefile.in
+++ b/browser/locales/Makefile.in
@@ -146,16 +146,19 @@ install:: $(call MERGE_FILES,$(addprefix
 # metro build calls back here for search engine plugins
 searchplugins: $(addprefix $(FINAL_TARGET)/searchplugins/,$(SEARCHPLUGINS))
 .PHONY: searchplugins
 
 libs-%:
 	$(NSINSTALL) -D $(DIST)/install
 	@$(MAKE) -C ../../toolkit/locales libs-$*
 	@$(MAKE) -C ../../services/sync/locales AB_CD=$* XPI_NAME=locale-$*
+ifdef MOZ_WEBAPP_RUNTIME
+	@$(MAKE) -C ../../webapprt/locales AB_CD=$* XPI_NAME=locale-$*
+endif
 	@$(MAKE) -C ../../extensions/spellcheck/locales AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) -C ../../intl/locales AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) libs AB_CD=$* XPI_NAME=locale-$* PREF_DIR=$(PREF_DIR)
 ifdef MOZ_METRO
 	@$(MAKE) -C ../metro/locales AB_CD=$* XPI_NAME=locale-$*
 endif
 	@$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales AB_CD=$* XPI_NAME=locale-$*
 
--- a/browser/locales/en-US/chrome/browser/devtools/netmonitor.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/netmonitor.properties
@@ -84,15 +84,23 @@ requestCookies=Request cookies
 # LOCALIZATION NOTE (responseCookies): This is the label displayed
 # in the network details params tab identifying the response cookies.
 responseCookies=Response cookies
 
 # LOCALIZATION NOTE (jsonFilterText): This is the text displayed
 # in the response tab of the network details pane for the JSON filtering input.
 jsonFilterText=Filter properties
 
+# LOCALIZATION NOTE (jsonScopeName): This is the text displayed
+# in the response tab of the network details pane for a JSON scope.
+jsonScopeName=JSON
+
+# LOCALIZATION NOTE (jsonpScopeName): This is the text displayed
+# in the response tab of the network details pane for a JSONP scope.
+jsonpScopeName=JSONP → callback %S()
+
 # LOCALIZATION NOTE (networkMenu.size): This is the label displayed
 # in the network menu specifying the size of a request (in kilobytes).
 networkMenu.sizeKB=%S KB
 
 # LOCALIZATION NOTE (networkMenu.total): This is the label displayed
 # in the network menu specifying the time for a request to finish (in milliseconds).
 networkMenu.totalMS=→ %S ms
--- a/browser/locales/en-US/chrome/browser/devtools/webconsole.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/webconsole.properties
@@ -175,16 +175,20 @@ ToolboxWebconsole.label=Web Console
 # displayed inside the developer tools window.
 ToolboxWebconsole.tooltip=Web Console
 
 # LOCALIZATION NOTE (longStringEllipsis): The string displayed after a long
 # string. This string is clickable such that the rest of the string is retrieved
 # from the server.
 longStringEllipsis=[…]
 
+# LOCALIZATION NOTE (longStringTooLong): The string displayed after the user
+# tries to expand a long string.
+longStringTooLong=The string you are trying to view is too long to be displayed by the Web Console.
+
 # LOCALIZATION NOTE (executeEmptyInput): This is displayed when the user tries
 # to execute code, but the input is empty.
 executeEmptyInput=No value to execute.
 
 # LOCALIZATION NOTE (NetworkPanel.fetchRemainingResponseContentLink): This is
 # displayed in the network panel when the response body is only partially
 # available.
 NetworkPanel.fetchRemainingResponseContentLink=Fetch the remaining %1$S bytes
--- a/browser/locales/en-US/profile/chrome/userContent-example.css
+++ b/browser/locales/en-US/profile/chrome/userContent-example.css
@@ -9,23 +9,16 @@
 
 /*
  * This file can be used to apply a style to all web pages you view
  * Rules without !important are overruled by author rules if the
  * author sets any.  Rules with !important overrule author rules.
  */
 
 /*
- * example: turn off "blink" element blinking
- *
- * blink { text-decoration: none ! important; }
- *
- */
-
-/*
  * example: give all tables a 2px border
  *
  * table { border: 2px solid; }
  */
 
 /*
  * example: turn off "marquee" element
  *
--- a/browser/locales/filter.py
+++ b/browser/locales/filter.py
@@ -1,18 +1,18 @@
 # 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/.
 
 def test(mod, path, entity = None):
   import re
   # ignore anything but Firefox
   if mod not in ("netwerk", "dom", "toolkit", "security/manager",
-                 "browser", "browser/metro", "extensions/reporter",
-                 "extensions/spellcheck",
+                 "browser", "browser/metro", "webapprt",
+                 "extensions/reporter", "extensions/spellcheck",
                  "other-licenses/branding/firefox",
                  "browser/branding/official",
                  "services/sync"):
     return False
   if mod != "browser" and mod != "extensions/spellcheck":
     # we only have exceptions for browser and extensions/spellcheck
     return True
   if not entity:
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -127,14 +127,8 @@
 % override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties
 % override chrome://mozapps/locale/downloads/settingsChange.dtd chrome://browser/locale/downloads/settingsChange.dtd
 % locale testpilot @AB_CD@ %locale/feedback/
     locale/feedback/main.dtd                       (%feedback/main.dtd)
     locale/feedback/main.properties                (%feedback/main.properties)
 % locale pdf.js @AB_CD@ %locale/pdfviewer/
     locale/pdfviewer/viewer.properties             (%pdfviewer/viewer.properties)
     locale/pdfviewer/chrome.properties             (%pdfviewer/chrome.properties)
-#ifdef MOZ_WEBAPP_RUNTIME
-../../webapprt/chrome/@AB_CD@.jar:
-% locale webapprt @AB_CD@ %locale/webapprt/
-    locale/webapprt/webapp.dtd                     (%webapprt/webapp.dtd)
-    locale/webapprt/webapp.properties              (%webapprt/webapp.properties)
-#endif
--- a/browser/locales/l10n.ini
+++ b/browser/locales/l10n.ini
@@ -12,11 +12,12 @@ dirs = browser
      other-licenses/branding/firefox
      browser/branding/official
 
 [includes]
 # non-central apps might want to use %(topsrcdir)s here, or other vars
 # RFE: that needs to be supported by compare-locales, too, though
 toolkit = toolkit/locales/l10n.ini
 services_sync = services/sync/locales/l10n.ini
+webapprt = webapprt/locales/l10n.ini
 
 [extras]
 dirs = extensions/spellcheck
--- a/browser/metro/base/content/helperui/MenuUI.js
+++ b/browser/metro/base/content/helperui/MenuUI.js
@@ -183,18 +183,23 @@ var ContextMenuUI = {
       }
     }
 
     if (!optionsAvailable) {
       this._popupState = null;
       return false;
     }
 
-    let coords =
-      aMessage.target.msgBrowserToClient(aMessage, true);
+    let coords = { x: aMessage.json.xPos, y: aMessage.json.yPos };
+
+    // chrome calls don't need to be translated and as such
+    // don't provide target.
+    if (aMessage.target) {
+      coords = aMessage.target.msgBrowserToClient(aMessage, true);
+    }
     this._menuPopup.show(Util.extend({}, this._defaultPositionOptions, {
       xPos: coords.x,
       yPos: coords.y,
       source: aMessage.json.source
     }));
     return true;
   },
 
--- a/browser/metro/shell/testing/metrotestharness.cpp
+++ b/browser/metro/shell/testing/metrotestharness.cpp
@@ -23,16 +23,22 @@
 #include <strsafe.h>
 #include <io.h>
 #include <shellapi.h>
 
 static const WCHAR* kFirefoxExe = L"firefox.exe";
 static const WCHAR* kDefaultMetroBrowserIDPathKey = L"FirefoxURL";
 static const WCHAR* kDemoMetroBrowserIDPathKey = L"Mozilla.Firefox.URL";
 
+// Logging pipe handle
+HANDLE gTestOutputPipe = INVALID_HANDLE_VALUE;
+// Logging pipe read buffer
+#define PIPE_BUFFER_SIZE 4096
+char buffer[PIPE_BUFFER_SIZE + 1];
+
 CString sAppParams;
 CString sFirefoxPath;
 
 // The tests file we write out for firefox.exe which contains test
 // startup command line paramters.
 #define kMetroTestFile "tests.ini"
 
 static void Log(const wchar_t *fmt, ...)
@@ -151,22 +157,46 @@ public:
   ~DeleteTestFileHelper() {
     if (mTestFile.GetLength()) {
       Log(L"Deleting %s", CStringW(mTestFile));
       DeleteFileA(mTestFile);
     }
   }
 };
 
-static void AddConsoleIdToParams()
+static bool SetupTestOutputPipe()
 {
-  DWORD dwId = GetCurrentProcessId();
-  CString tmp;
-  tmp.Format(L" testconsoleid=%d", dwId);
-  sAppParams += tmp;
+  SECURITY_ATTRIBUTES saAttr;
+  saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+  saAttr.bInheritHandle = TRUE;
+  saAttr.lpSecurityDescriptor = NULL;
+
+  gTestOutputPipe =
+    CreateNamedPipeW(L"\\\\.\\pipe\\metrotestharness",
+                     PIPE_ACCESS_INBOUND,
+                     PIPE_TYPE_BYTE|PIPE_WAIT,
+                     1,
+                     PIPE_BUFFER_SIZE,
+                     PIPE_BUFFER_SIZE, 0, NULL);
+
+  if (gTestOutputPipe == INVALID_HANDLE_VALUE) {
+    Log(L"Failed to create named logging pipe.");
+    return false;
+  }
+  return true;
+}
+
+static void ReadPipe()
+{
+  DWORD numBytesRead;
+  while (ReadFile(gTestOutputPipe, buffer, PIPE_BUFFER_SIZE, &numBytesRead, NULL) &&
+         numBytesRead) {
+    buffer[numBytesRead] = '\0';
+    printf("%s", buffer);
+  }
 }
 
 static bool Launch()
 {
   Log(L"Launching browser...");
 
   DWORD processID;
 
@@ -244,16 +274,22 @@ static bool Launch()
   if (!WriteFile(hTestFile, asciiParams, asciiParams.GetLength(), NULL, 0)) {
     CloseHandle(hTestFile);
     Fail(L"WriteFile errorno=%d", GetLastError());
     return false;
   }
   FlushFileBuffers(hTestFile);
   CloseHandle(hTestFile);
 
+  // Create a named stdout pipe for the browser
+  if (!SetupTestOutputPipe()) {
+    Fail(L"SetupTestOutputPipe failed (errno=%d)", GetLastError());
+    return false;
+  }
+
   // Launch firefox
   hr = activateMgr->ActivateApplication(appModelID, L"", AO_NOERRORUI, &processID);
   if (FAILED(hr)) {
     Fail(L"ActivateApplication result %X", hr);
     return false;
   }
 
   Log(L"Activation succeeded. processid=%d", processID);
@@ -263,23 +299,34 @@ static bool Launch()
     Fail(L"Couldn't find child process. (%d)", GetLastError());
     return false;
   }
 
   Log(L"Waiting on child process...");
 
   MSG msg;
   DWORD waitResult = WAIT_TIMEOUT;
-  while ((waitResult = WaitForSingleObject(child, 10)) != WAIT_OBJECT_0) {
-    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+  HANDLE handles[2] = { child, gTestOutputPipe };
+  while ((waitResult = MsgWaitForMultipleObjects(2, handles, FALSE, INFINITE, QS_ALLINPUT)) != WAIT_OBJECT_0) {
+    if (waitResult == WAIT_FAILED) {
+      Log(L"Wait failed (errno=%d)", GetLastError());
+      break;
+    } else if (waitResult == WAIT_OBJECT_0 + 1) {
+      ReadPipe();
+    } else if (waitResult == WAIT_OBJECT_0 + 2 &&
+               PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
       TranslateMessage(&msg);
       DispatchMessage(&msg);
     }
   }
 
+  ReadPipe();
+  CloseHandle(gTestOutputPipe);
+  CloseHandle(child);
+
   Log(L"Exiting.");
   return true;
 }
 
 int wmain(int argc, WCHAR* argv[])
 {
   CoInitialize(NULL);
 
@@ -302,15 +349,14 @@ int wmain(int argc, WCHAR* argv[])
 
     sAppParams.Append(argv[idx]);
     sAppParams.Append(L" ");
   }
   sAppParams.Trim();
   if (sFirefoxPath.GetLength()) {
     Log(L"firefoxpath: '%s'", sFirefoxPath);
   }
-  AddConsoleIdToParams();
   Log(L"args: '%s'", sAppParams);
   Launch();
 
   CoUninitialize();
   return 0;
 }
--- a/browser/themes/linux/devtools/webconsole.css
+++ b/browser/themes/linux/devtools/webconsole.css
@@ -1,22 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-.hud-outer-wrapper {
-  width: 100%;
-  height: 100%;
-}
-
-.hud-console-wrapper {
-  width: 100%;
-  overflow: auto;
-}
-
 /* General output styles */
 
 .webconsole-timestamp {
   color: GrayText;
   margin-top: 0;
   margin-bottom: 0;
   font-family: "DejaVu Sans Mono", monospace;
 }
@@ -98,30 +88,28 @@
 .jsterm-input-node,
 .jsterm-complete-node {
   font: 0.9em "DejaVu Sans Mono", monospace;
 }
 
 .hud-output-node {
   -moz-appearance: none;
   border-bottom: 1px solid ThreeDShadow;
-  border-top: 1px solid ThreeDShadow;
   margin: 0;
   font-size: 0.9em;
 }
 
 .hud-filtered-by-type,
 .hud-filtered-by-string {
   display: none;
 }
 
 /* WebConsole colored drops */
 
-.webconsole-filter-button,
-.webconsole-filter-button[checked=true] {
+.webconsole-filter-button[checked] {
   color: white !important;
 }
 
 .webconsole-filter-button > .toolbarbutton-menubutton-button:before {
   content: "";
   display: inline-block;
   height: 8px;
   width: 8px;
@@ -238,19 +226,17 @@
 
 .jsterm-complete-node > .textbox-input-box > .textbox-textarea {
   color: GrayText;
 }
 
 .webconsole-msg-inspector iframe {
   height: 7em;
   margin-bottom: 15px;
-}
-
-.devtools-side-splitter {
-  background: #666;
-  width: 2px;
+  -moz-margin-end: 15px;
+  border-radius: 4px;
+  box-shadow: 0 0 12px #dfdfdf;
 }
 
 #webconsole-sidebar > tabs {
   height: 0;
-  overflow: hidden;
+  border: none;
 }
--- a/browser/themes/osx/devtools/webconsole.css
+++ b/browser/themes/osx/devtools/webconsole.css
@@ -1,24 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 %include ../shared.inc
 
-.hud-outer-wrapper {
-  width: 100%;
-  height: 100%;
-}
-
-.hud-console-wrapper {
-  width: 100%;
-  overflow: auto;
-}
-
 /* General output styles */
 
 .webconsole-timestamp {
   color: GrayText;
   margin-top: 0;
   margin-bottom: 0;
   font-family: Menlo, Monaco, monospace;
 }
@@ -91,29 +81,27 @@
 .jsterm-input-node,
 .jsterm-complete-node {
   font: 1em Menlo, Monaco, monospace;
 }
 
 .hud-output-node {
   -moz-appearance: none;
   border-bottom: 1px solid ThreeDShadow;
-  border-top: 1px solid ThreeDShadow;
   margin: 0;
 }
 
 .hud-filtered-by-type,
 .hud-filtered-by-string {
   display: none;
 }
 
 /* WebConsole colored drops */
 
-.webconsole-filter-button,
-.webconsole-filter-button[checked=true] {
+.webconsole-filter-button[checked] {
   color: white !important;
 }
 
 .webconsole-filter-button > .toolbarbutton-menubutton-button:before {
   content: "";
   display: inline-block;
   height: 8px;
   width: 8px;
@@ -242,19 +230,17 @@
 
 .jsterm-complete-node > .textbox-input-box > .textbox-textarea {
   color: GrayText;
 }
 
 .webconsole-msg-inspector iframe {
   height: 7em;
   margin-bottom: 15px;
-}
-
-.devtools-side-splitter {
-  background: #666;
-  width: 2px;
+  -moz-margin-end: 15px;
+  border-radius: 4px;
+  box-shadow: 0 0 12px #dfdfdf;
 }
 
 #webconsole-sidebar > tabs {
   height: 0;
-  overflow: hidden;
+  border: none;
 }
--- a/browser/themes/windows/devtools/webconsole.css
+++ b/browser/themes/windows/devtools/webconsole.css
@@ -1,22 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-.hud-outer-wrapper {
-  width: 100%;
-  height: 100%;
-}
-
-.hud-console-wrapper {
-  width: 100%;
-  overflow: auto;
-}
-
 /* General output styles */
 
 .webconsole-timestamp {
   color: GrayText;
   margin-top: 0;
   margin-bottom: 0;
   font-family: Consolas, Lucida Console, monospace;
 }
@@ -89,28 +79,27 @@
 .jsterm-input-node,
 .jsterm-complete-node {
   font-family: Consolas, Lucida Console, monospace;
 }
 
 .hud-output-node {
   -moz-appearance: none;
   border-bottom: 1px solid ThreeDShadow;
-  border-top: 1px solid ThreeDShadow;
   margin: 0;
 }
 
 .hud-filtered-by-type,
 .hud-filtered-by-string {
   display: none;
 }
 
 /* WebConsole colored drops */
 
-.webconsole-filter-button[checked=true] {
+.webconsole-filter-button[checked] {
   color: white !important;
 }
 
 .webconsole-filter-button > .toolbarbutton-menubutton-button:before {
   content: "";
   display: inline-block;
   height: 8px;
   width: 8px;
@@ -246,19 +235,17 @@
 
 .hud-filter-box {
   width: 200px;
 }
 
 .webconsole-msg-inspector iframe {
   height: 7em;
   margin-bottom: 15px;
-}
-
-.devtools-side-splitter {
-  background: #666;
-  width: 2px;
+  -moz-margin-end: 15px;
+  border-radius: 4px;
+  box-shadow: 0 0 12px #dfdfdf;
 }
 
 #webconsole-sidebar > tabs {
   height: 0;
-  overflow: hidden;
+  border: none;
 }
--- a/build/autoconf/android.m4
+++ b/build/autoconf/android.m4
@@ -21,23 +21,28 @@ MOZ_ARG_WITH_STRING(android-gnu-compiler
     android_gnu_compiler_version=$withval)
 
 MOZ_ARG_ENABLE_BOOL(android-libstdcxx,
 [  --enable-android-libstdcxx
                           use GNU libstdc++ instead of STLPort],
     MOZ_ANDROID_LIBSTDCXX=1,
     MOZ_ANDROID_LIBSTDCXX= )
 
-android_version=9
+define([MIN_ANDROID_VERSION], [9])
+android_version=MIN_ANDROID_VERSION
 
 MOZ_ARG_WITH_STRING(android-version,
 [  --with-android-version=VER
-                          android platform version, default 9],
+                          android platform version, default] MIN_ANDROID_VERSION,
     android_version=$withval)
 
+if test $android_version -lt MIN_ANDROID_VERSION ; then
+    AC_MSG_ERROR([--with-android-version must be at least MIN_ANDROID_VERSION.])
+fi
+
 MOZ_ARG_WITH_STRING(android-platform,
 [  --with-android-platform=DIR
                            location of platform dir],
     android_platform=$withval)
 
 case "$target" in
 arm-linux*-android*|*-linuxandroid*)
     android_tool_prefix="arm-linux-androideabi"
@@ -104,16 +109,18 @@ case "$target" in
         if test -z "$android_gnu_compiler_version" ; then
             AC_MSG_ERROR([not found. You have to specify --with-android-toolchain=/path/to/ndk/toolchain.])
         else
             AC_MSG_RESULT([$android_toolchain])
         fi
         NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-android-toolchain=$android_toolchain"
     fi
 
+    NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-android-version=$android_version"
+
     if test -z "$android_platform" ; then
         AC_MSG_CHECKING([for android platform directory])
 
         case "$target_cpu" in
         arm)
             target_name=arm
             ;;
         i?86)
@@ -179,21 +186,18 @@ case "$target" in
     fi
     if test -z "$HOST_LDFLAGS" ; then
         HOST_LDFLAGS=" "
     fi
 
     ANDROID_NDK="${android_ndk}"
     ANDROID_TOOLCHAIN="${android_toolchain}"
     ANDROID_PLATFORM="${android_platform}"
-    ANDROID_VERSION="${android_version}"
 
     AC_DEFINE(ANDROID)
-    AC_DEFINE_UNQUOTED(ANDROID_VERSION, $android_version)
-    AC_SUBST(ANDROID_VERSION)
     CROSS_COMPILE=1
     AC_SUBST(ANDROID_NDK)
     AC_SUBST(ANDROID_TOOLCHAIN)
     AC_SUBST(ANDROID_PLATFORM)
 
     ;;
 esac
 
--- a/build/autoconf/arch.m4
+++ b/build/autoconf/arch.m4
@@ -197,36 +197,48 @@ if test "$CPU_ARCH" = "arm"; then
                  [asm("uqadd8 r1, r1, r2");],
                  result="yes", result="no")
   AC_MSG_RESULT("$result")
   if test "$result" = "yes"; then
       AC_DEFINE(HAVE_ARM_SIMD)
       HAVE_ARM_SIMD=1
   fi
 
+  AC_MSG_CHECKING(ARM version support in compiler)
+  dnl Determine the target ARM architecture (5 for ARMv5, v5T, v5E, etc.; 6 for ARMv6, v6K, etc.)
+  ARM_ARCH=`${CC-cc} ${CFLAGS} -dM -E - < /dev/null | sed -n 's/.*__ARM_ARCH_\([[0-9]]*\).*/\1/p'`
+  AC_MSG_RESULT("$ARM_ARCH")
+
   AC_MSG_CHECKING(for ARM NEON support in compiler)
   # We try to link so that this also fails when
   # building with LTO.
   AC_TRY_LINK([],
                  [asm(".fpu neon\n vadd.i8 d0, d0, d0");],
                  result="yes", result="no")
   AC_MSG_RESULT("$result")
   if test "$result" = "yes"; then
       AC_DEFINE(HAVE_ARM_NEON)
       HAVE_ARM_NEON=1
+
+      dnl We don't need to build NEON support if we're targetting a non-NEON device.
+      dnl This matches media/webrtc/trunk/webrtc/build/common.gypi.
+      if test -n "$ARM_ARCH"; then
+          if test "$ARM_ARCH" -lt 7; then
+              BUILD_ARM_NEON=0
+          else
+              BUILD_ARM_NEON=1
+          fi
+      fi
   fi
 
-  AC_MSG_CHECKING(ARM version support in compiler)
-  dnl Determine the target ARM architecture (5 for ARMv5, v5T, v5E, etc.; 6 for ARMv6, v6K, etc.)
-  ARM_ARCH=`${CC-cc} ${CFLAGS} -dM -E - < /dev/null | sed -n 's/.*__ARM_ARCH_\([[0-9]]*\).*/\1/p'`
-  AC_MSG_RESULT("$ARM_ARCH")
 fi # CPU_ARCH = arm
 
 AC_SUBST(HAVE_ARM_SIMD)
 AC_SUBST(HAVE_ARM_NEON)
+AC_SUBST(BUILD_ARM_NEON)
 
 if test -n "$MOZ_ARCH"; then
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-arch=$MOZ_ARCH"
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-thumb=$MOZ_THUMB"
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-thumb-interwork=$MOZ_THUMB_INTERWORK"
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-fpu=$MOZ_FPU"
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-float-abi=$MOZ_FLOAT_ABI"
   NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-soft-float=$MOZ_SOFT_FLOAT"
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -1157,18 +1157,18 @@ user_pref("camino.use_system_proxy_setti
     for processPID in processList:
       self.log.info("INFO | zombiecheck | Checking for orphan process with PID: %d", processPID)
       if self.isPidAlive(processPID):
         foundZombie = True
         self.log.info("TEST-UNEXPECTED-FAIL | zombiecheck | child process %d still alive after shutdown", processPID)
         self.killAndGetStack(processPID, utilityPath, debuggerInfo)
     return foundZombie
 
-  def checkForCrashes(self, profileDir, symbolsPath):
-    return mozcrash.check_for_crashes(os.path.join(profileDir, "minidumps"), symbolsPath, test_name=self.lastTestSeen)
+  def checkForCrashes(self, minidumpDir, symbolsPath):
+    return mozcrash.check_for_crashes(minidumpDir, symbolsPath, test_name=self.lastTestSeen)
 
   def runApp(self, testURL, env, app, profileDir, extraArgs,
              runSSLTunnel = False, utilityPath = None,
              xrePath = None, certPath = None,
              debuggerInfo = None, symbolsPath = None,
              timeout = -1, maxTime = None, onLaunch = None):
     """
     Run the app, log the duration it took to execute, return the status code.
@@ -1232,17 +1232,17 @@ user_pref("camino.use_system_proxy_setti
       onLaunch()
 
     status = self.waitForFinish(proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath)
     self.log.info("INFO | automation.py | Application ran for: %s", str(datetime.now() - startTime))
 
     # Do a final check for zombie child processes.
     zombieProcesses = self.checkForZombies(processLog, utilityPath, debuggerInfo)
 
-    crashed = self.checkForCrashes(profileDir, symbolsPath)
+    crashed = self.checkForCrashes(os.path.join(profileDir, "minidumps"), symbolsPath)
 
     if crashed or zombieProcesses:
       status = 1
 
     if os.path.exists(processLog):
       os.unlink(processLog)
 
     if self.IS_TEST_BUILD and runSSLTunnel:
--- a/build/clang-plugin/Makefile.in
+++ b/build/clang-plugin/Makefile.in
@@ -11,30 +11,31 @@ VPATH		 := @srcdir@
 NULL :=
 
 CPPSRCS := \
 	clang-plugin.cpp \
 	$(NULL)
 
 TESTSRCS := \
 	TestMustOverride.cpp \
+	TestStackClass.cpp \
 	$(NULL)
 
 OBJS := $(patsubst %.cpp,%.o,$(CPPSRCS))
 TESTS := $(patsubst %.cpp,test-%,$(TESTSRCS))
 
 PLUGIN := libclang-plugin.so
 
 all: $(PLUGIN) $(TESTS)
 
 $(OBJS): %.o: %.cpp Makefile
 	$(CXX) -o $@ -c $(CXXFLAGS) $<
 
 $(PLUGIN): $(OBJS)
 	rm -f $@
-	$(CXX) -shared -o $@ $(CXXFLAGS) $(LDFLAGS) $(OBJS)
+	$(CXX) -shared -o $@ $(CXXFLAGS) $(LDFLAGS) $(OBJS) -lclangASTMatchers
 
 TESTFLAGS := -fsyntax-only -Xclang -verify \
 	-Xclang -load -Xclang $(CURDIR)/$(PLUGIN) \
 	-Xclang -add-plugin -Xclang moz-check
 
 $(TESTS): test-%: tests/%.cpp $(PLUGIN)
 	$(CXX) $(TESTFLAGS) $<
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -1,30 +1,59 @@
 /* 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 "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Basic/Version.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendPluginRegistry.h"
+#include "clang/Frontend/MultiplexConsumer.h"
 #include "clang/Sema/Sema.h"
 
 #define CLANG_VERSION_FULL (CLANG_VERSION_MAJOR * 100 + CLANG_VERSION_MINOR)
 
 using namespace llvm;
 using namespace clang;
 
 namespace {
+
+using namespace clang::ast_matchers;
+class DiagnosticsMatcher {
+public:
+  DiagnosticsMatcher();
+
+  ASTConsumer *makeASTConsumer() {
+    return astMatcher.newASTConsumer();
+  }
+
+private:
+  class StackClassChecker : public MatchFinder::MatchCallback {
+  public:
+    virtual void run(const MatchFinder::MatchResult &Result);
+  };
+
+  StackClassChecker stackClassChecker;
+  MatchFinder astMatcher;
+};
+
 class MozChecker : public ASTConsumer, public RecursiveASTVisitor<MozChecker> {
   DiagnosticsEngine &Diag;
   const CompilerInstance &CI;
+  DiagnosticsMatcher matcher;
 public:
   MozChecker(const CompilerInstance &CI) : Diag(CI.getDiagnostics()), CI(CI) {}
+
+  ASTConsumer *getOtherConsumer() {
+    return matcher.makeASTConsumer();
+  }
+
   virtual void HandleTranslationUnit(ASTContext &ctx) {
     TraverseDecl(ctx.getTranslationUnitDecl());
   }
 
   static bool hasCustomAnnotation(const Decl *d, const char *spelling) {
     AnnotateAttr *attr = d->getAttr<AnnotateAttr>();
     if (!attr)
       return false;
@@ -53,16 +82,26 @@ public:
         continue;
       }
       parent = parent->getDefinition();
       for (CXXRecordDecl::method_iterator M = parent->method_begin();
           M != parent->method_end(); ++M) {
         if (hasCustomAnnotation(*M, "moz_must_override"))
           must_overrides.push_back(*M);
       }
+
+      // While we are looping over parent classes, we'll also check to make sure
+      // that the subclass has the annotation if the superclass does.
+      if (hasCustomAnnotation(parent, "moz_stack_class") &&
+          !hasCustomAnnotation(d, "moz_stack_class")) {
+        unsigned badInheritID = Diag.getDiagnosticIDs()->getCustomDiagID(
+            DiagnosticIDs::Error, "%0 inherits from a stack class %1");
+        Diag.Report(d->getLocation(), badInheritID)
+          << d->getDeclName() << parent->getDeclName();
+      }
     }
 
     for (OverridesVector::iterator it = must_overrides.begin();
         it != must_overrides.end(); ++it) {
       bool overridden = false;
       for (CXXRecordDecl::method_iterator M = d->method_begin();
           !overridden && M != d->method_end(); ++M) {
         // The way that Clang checks if a method M overrides its parent method
@@ -79,21 +118,88 @@ public:
         Diag.Report(d->getLocation(), overrideID) << d->getDeclName() <<
           (*it)->getDeclName();
         Diag.Report((*it)->getLocation(), overrideNote);
       }
     }
     return true;
   }
 };
+}
+
+namespace clang {
+namespace ast_matchers {
+
+/// This matcher will match any class with the stack class assertion or an
+/// array of such classes.
+AST_MATCHER(QualType, stackClassAggregate) {
+  QualType t = Node;
+  while (const ArrayType *arrTy = t->getAsArrayTypeUnsafe())
+    t = arrTy->getElementType();
+  CXXRecordDecl *clazz = t->getAsCXXRecordDecl();
+  return clazz && MozChecker::hasCustomAnnotation(clazz, "moz_stack_class");
+}
+}
+}
+
+namespace {
+
+DiagnosticsMatcher::DiagnosticsMatcher() {
+  // Stack class assertion: non-local variables of a stack class are forbidden
+  // (non-localness checked in the callback)
+  astMatcher.addMatcher(varDecl(hasType(stackClassAggregate())).bind("node"),
+    &stackClassChecker);
+  // Stack class assertion: new stack class is forbidden (unless placement new)
+  astMatcher.addMatcher(newExpr(hasType(pointerType(
+      pointee(stackClassAggregate())
+    ))).bind("node"), &stackClassChecker);
+  // Stack class assertion: a stack-class field is permitted only if it's a
+  // member of a class with the annotation
+  astMatcher.addMatcher(fieldDecl(hasType(stackClassAggregate())).bind("field"),
+    &stackClassChecker);
+}
+
+void DiagnosticsMatcher::StackClassChecker::run(
+    const MatchFinder::MatchResult &Result) {
+  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
+  unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
+    DiagnosticIDs::Error, "variable of type %0 only valid on the stack");
+  if (const VarDecl *d = Result.Nodes.getNodeAs<VarDecl>("node")) {
+    // Ignore the match if it's a local variable.
+    if (d->hasLocalStorage())
+      return;
+    Diag.Report(d->getLocation(), stackID) << d->getType();
+  } else if (const CXXNewExpr *expr =
+      Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
+    // If it's placement new, then this match doesn't count.
+    if (expr->getNumPlacementArgs() > 0)
+      return;
+    Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType();
+  } else if (const FieldDecl *field =
+      Result.Nodes.getNodeAs<FieldDecl>("field")) {
+    // AST matchers don't let me get the class that contains a field...
+    const RecordDecl *parent = field->getParent();
+    if (!MozChecker::hasCustomAnnotation(parent, "moz_stack_class")) {
+      // We use a more verbose error message here.
+      unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
+        DiagnosticIDs::Error,
+        "member of type %0 in class %1 that is not a stack class");
+      Diag.Report(field->getLocation(), stackID) << field->getType() <<
+        parent->getDeclName();
+    }
+  }
+}
 
 class MozCheckAction : public PluginASTAction {
 public:
   ASTConsumer *CreateASTConsumer(CompilerInstance &CI, StringRef fileName) {
-    return new MozChecker(CI);
+    MozChecker *checker = new MozChecker(CI);
+
+    ASTConsumer *consumers[] = { checker, checker->getOtherConsumer() };
+    return new MultiplexConsumer(consumers);
   }
 
   bool ParseArgs(const CompilerInstance &CI,
                  const std::vector<std::string> &args) {
     return true;
   }
 };
 }
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/tests/TestStackClass.cpp
@@ -0,0 +1,47 @@
+#define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class")))
+#include <stddef.h>
+
+struct MOZ_STACK_CLASS Stack {
+  int i;
+  void *operator new(size_t x) { return 0; }
+  void *operator new(size_t blah, char *buffer) { return buffer; }
+};
+
+template <class T>
+struct MOZ_STACK_CLASS TemplateClass {
+  T i;
+};
+
+void gobble(void *) { }
+
+void misuseStackClass(int len) {
+  Stack valid;
+  Stack alsoValid[2];
+  static Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}}
+  static Stack alsoNotValid[2]; // expected-error {{variable of type 'Stack [2]' only valid on the stack}}
+
+  gobble(&valid);
+  gobble(&notValid);
+  gobble(&alsoValid[0]);
+
+  gobble(new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}}
+  gobble(new Stack[10]); // expected-error {{variable of type 'Stack' only valid on the stack}}
+  gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' only valid on the stack}}
+  gobble(len <= 5 ? &valid : new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}}
+
+  char buffer[sizeof(Stack)];
+  gobble(new (buffer) Stack);
+}
+
+Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}}
+struct RandomClass {
+  Stack nonstaticMember; // expected-error {{member of type 'Stack' in class 'RandomClass' that is not a stack class}}
+  static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
+};
+struct MOZ_STACK_CLASS RandomStackClass {
+  Stack nonstaticMember;
+  static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
+};
+
+struct BadInherit : Stack {}; // expected-error {{'BadInherit' inherits from a stack class 'Stack'}}
+struct MOZ_STACK_CLASS GoodInherit : Stack {};
--- a/build/mobile/sutagent/android/SUTAgentAndroid.java
+++ b/build/mobile/sutagent/android/SUTAgentAndroid.java
@@ -33,16 +33,17 @@ import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.net.Uri;
 import android.net.wifi.SupplicantState;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.WifiLock;
 import android.os.BatteryManager;
+import android.os.Build;
 import android.os.Build.VERSION;
 import android.os.Bundle;
 import android.os.Handler;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
@@ -69,16 +70,17 @@ public class SUTAgentAndroid extends Act
     String lineSep = System.getProperty("line.separator");
     public PrintWriter dataOut = null;
 
     private static boolean bNetworkingStarted = false;
     private static String RegSvrIPAddr = "";
     private static String RegSvrIPPort = "";
     private static String HardwareID = "";
     private static String Pool = "";
+    private static String Abi = "";
     private static String sRegString = "";
     private static boolean LogCommands = false;
 
     private WifiLock wl = null;
 
     private BroadcastReceiver battReceiver = null;
 
     private TextView  tv = null;
@@ -167,16 +169,17 @@ public class SUTAgentAndroid extends Act
         String lc = dc.GetIniData("General", "LogCommands", sIniFile);
         if (lc != "" && Integer.parseInt(lc) == 1) {
             SUTAgentAndroid.LogCommands = true;
         }
         SUTAgentAndroid.RegSvrIPAddr = dc.GetIniData("Registration Server", "IPAddr", sIniFile);
         SUTAgentAndroid.RegSvrIPPort = dc.GetIniData("Registration Server", "PORT", sIniFile);
         SUTAgentAndroid.HardwareID = dc.GetIniData("Registration Server", "HARDWARE", sIniFile);
         SUTAgentAndroid.Pool = dc.GetIniData("Registration Server", "POOL", sIniFile);
+        SUTAgentAndroid.Abi = android.os.Build.CPU_ABI;
         log(dc, "onCreate");
 
         tv = (TextView) this.findViewById(R.id.Textview01);
 
         if (getLocalIpAddress() == null)
             setUpNetwork(sIniFile);
 
         String macAddress = "Unknown";
@@ -272,16 +275,17 @@ public class SUTAgentAndroid extends Act
 
         String hwid = getHWID(this);
 
         sLocalIPAddr = getLocalIpAddress();
         Toast.makeText(getApplication().getApplicationContext(), "SUTAgent [" + sLocalIPAddr + "] ...", Toast.LENGTH_LONG).show();
 
         String sConfig = "Unique ID: " + sUniqueID + lineSep;
         sConfig += "HWID: " + hwid + lineSep;
+        sConfig += "ABI: " + Abi + lineSep;
         sConfig += "OS Info" + lineSep;
         sConfig += "\t" + dc.GetOSInfo() + lineSep;
         sConfig += "Screen Info" + lineSep;
         int [] xy = dc.GetScreenXY();
         sConfig += "\t Width: " + xy[0] + lineSep;
         sConfig += "\t Height: " + xy[1] + lineSep;
         sConfig += "Memory Info" + lineSep;
         sConfig += "\t" + dc.GetMemoryInfo() + lineSep;
@@ -297,16 +301,17 @@ public class SUTAgentAndroid extends Act
         sRegString += "&DATAPORT=" + 20700;
         sRegString += "&OS=Android-" + dc.GetOSInfo();
         sRegString += "&SCRNWIDTH=" + xy[0];
         sRegString += "&SCRNHEIGHT=" + xy[1];
         sRegString += "&BPP=8";
         sRegString += "&MEMORY=" + dc.GetMemoryConfig();
         sRegString += "&HARDWARE=" + HardwareID;
         sRegString += "&POOL=" + Pool;
+        sRegString += "&ABI=" + Abi;
 
         String sTemp = Uri.encode(sRegString,"=&");
         sRegString = "register " + sTemp;
 
         pruneCommandLog(dc.GetSystemTime(), dc.GetTestRoot());
 
         if (!bNetworkingStarted)
             {
new file mode 100644
--- /dev/null
+++ b/build/unix/add_phony_targets.py
@@ -0,0 +1,33 @@
+import pymake.data
+import pymake.parser
+import pymake.parserdata
+import sys
+
+'''
+Modifies the output of Sun Studio's -xM to look more like the output
+of gcc's -MD -MP, adding phony targets for dependencies.
+'''
+
+
+def add_phony_targets(path):
+    print path
+    deps = set()
+    targets = set()
+    for stmt in pymake.parser.parsefile(path):
+        if isinstance(stmt, pymake.parserdata.Rule):
+            assert isinstance(stmt.depexp, pymake.data.StringExpansion)
+            assert isinstance(stmt.targetexp, pymake.data.StringExpansion)
+            for d in stmt.depexp.s.split():
+                deps.add(d)
+            for t in stmt.targetexp.s.split():
+                targets.add(t)
+    phony_targets = deps - targets
+    if not phony_targets:
+        return
+    with open(path, 'a') as f:
+        f.writelines('%s:\n' % d for d in phony_targets)
+
+
+if __name__ == '__main__':
+    for f in sys.argv[1:]:
+        add_phony_targets(f)
--- a/build/unix/build-clang/build-clang.py
+++ b/build/unix/build-clang/build-clang.py
@@ -11,16 +11,17 @@ moz_version = "moz0"
 import os
 import os.path
 import shutil
 import tarfile
 import subprocess
 import platform
 import sys
 import json
+import collections
 
 def check_run(args):
     r = subprocess.call(args)
     assert r == 0
 
 def run_in(path, args):
     d = os.getcwd()
     os.chdir(path)
@@ -69,36 +70,26 @@ clang_source_dir = source_dir + "/clang"
 compiler_rt_source_dir = source_dir + "/compiler-rt"
 
 def build_one_stage(env, stage_dir, is_stage_one):
     def f():
         build_one_stage_aux(stage_dir, is_stage_one)
     with_env(env, f)
 
 def build_tooltool_manifest():
-    def key_sort(item):
-        item = item[0]
-        if item == 'size':
-            return 0
-        if item == 'digest':
-            return 1
-        if item == 'algorithm':
-            return 3
-        return 4
-
     basedir = os.path.split(os.path.realpath(sys.argv[0]))[0]
     tooltool = basedir + '/tooltool.py'
     setup = basedir + '/setup.sh'
     manifest = 'clang.manifest'
     check_run(['python', tooltool, '-m', manifest, 'add',
                setup, 'clang.tar.bz2'])
-    data = json.load(file(manifest))
+    data = json.load(file(manifest), object_pairs_hook=collections.OrderedDict)
     data = [{'clang_version' : 'r%s' % llvm_revision }] + data
     out = file(manifest,'w')
-    json.dump(data, out, indent=0, item_sort_key=key_sort)
+    json.dump(data, out, indent=0)
     out.write('\n')
 
     assert data[2]['filename'] == 'clang.tar.bz2'
     os.rename('clang.tar.bz2', data[2]['digest'])
 
 isDarwin = platform.system() == "Darwin"
 
 def build_one_stage_aux(stage_dir, is_stage_one):
deleted file mode 100755
--- a/build/unix/mddepend.pl
+++ /dev/null
@@ -1,139 +0,0 @@
-#!/usr/bin/env perl
-
-# 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/.
-
-# mddepend.pl - Reads in dependencies generated my -MD flag. Prints list
-#   of objects that need to be rebuilt. These can then be added to the
-#   PHONY target. Using this script copes with the problem of header
-#   files that have been removed from the build.
-#    
-# Usage:
-#   mddepend.pl <output_file> <dependency_files...>
-#
-# Send comments, improvements, bugs to Steve Lamm (slamm@netscape.com).
-
-use strict;
-
-use constant DEBUG => 0;
-
-my $outfile = shift @ARGV;
-my $silent = $ENV{MAKEFLAGS} =~ /^\w*s|\s-s/;
-
-my $line = '';
-my %alldeps;
-# Parse dependency files
-while (<>) {
-  s/\r?\n$//; # Handle both unix and DOS line endings
-  $line .= $_;
-  if ($line =~ /\\$/) {
-    chop $line;
-    next;
-  }
-
-  $line =~ s|\\|/|g;
-
-  my ($obj,$rest) = split /\s*:\s+/, $line, 2;
-  $line = '';
-  next if !$obj || !$rest;
-
-  my @deps = split /\s+/, $rest;
-  push @{$alldeps{$obj}}, @deps;
-  if (DEBUG >= 2) {
-    foreach my $dep (@deps) { print STDERR "add $obj $dep\n"; }
-  }
-}
-
-# Test dependencies
-my %modtimes; # cache
-my @objs;     # force rebuild on these
-OBJ_LOOP: foreach my $obj (keys %alldeps) {
-  my $mtime = (stat $obj)[9] or next;
-
-  my %not_in_cache;
-  my $deps = $alldeps{$obj};
-  foreach my $dep_file (@{$deps}) {
-    my $dep_mtime = $modtimes{$dep_file};
-    if (not defined $dep_mtime) {
-      print STDERR "Skipping $dep_file for $obj, will stat() later\n" if DEBUG >= 2;
-      $not_in_cache{$dep_file} = 1;
-      next;
-    }
-
-    print STDERR "Found $dep_file in cache\n" if DEBUG >= 2;
-
-    if ($dep_mtime > $mtime) {
-      print STDERR "$dep_file($dep_mtime) newer than $obj($mtime)\n" if DEBUG;
-    }
-    elsif ($dep_mtime == -1) {
-      print STDERR "Couldn't stat $dep_file for $obj\n" if DEBUG;
-    }
-    else {
-      print STDERR "$dep_file($dep_mtime) older than $obj($mtime)\n" if DEBUG >= 2;
-      next;
-    }
-
-    push @objs, $obj; # dependency is missing or newer
-    next OBJ_LOOP; # skip checking the rest of the dependencies
-  }
-
-  foreach my $dep_file (keys %not_in_cache) {
-    print STDERR "STAT $dep_file for $obj\n" if DEBUG >= 2;
-    my $dep_mtime = $modtimes{$dep_file} = (stat $dep_file)[9] || -1;
-
-    if ($dep_mtime > $mtime) {
-      print STDERR "$dep_file($dep_mtime) newer than $obj($mtime)\n" if DEBUG;
-    }
-    elsif ($dep_mtime == -1) {
-      print STDERR "Couldn't stat $dep_file for $obj\n" if DEBUG;
-    }
-    else {
-      print STDERR "$dep_file($dep_mtime) older than $obj($mtime)\n" if DEBUG >= 2;
-      next;
-    }
-
-    push @objs, $obj; # dependency is missing or newer
-    next OBJ_LOOP; # skip checking the rest of the dependencies
-  }
-
-  # If we get here it means nothing needs to be done for $obj
-}
-
-# Output objects to rebuild (if needed).
-if ($outfile eq '-') {
-    if (@objs) {
-	print "@objs: FORCE\n";
-    }
-} elsif (@objs) {
-  my $old_output;
-  my $new_output = "@objs: FORCE\n";
-
-  # Read in the current dependencies file.
-  open(OLD, "<$outfile")
-    and $old_output = <OLD>;
-  close(OLD);
-
-  # Only write out the dependencies if they are different.
-  if ($new_output ne $old_output) {
-    open(OUT, ">$outfile") and print OUT "$new_output";
-    print "Updating dependencies file, $outfile\n" unless $silent;
-    if (DEBUG) {
-      print "new: $new_output\n";
-      print "was: $old_output\n" if $old_output ne '';
-    }
-  }
-} elsif (-s $outfile) {
-  # Remove the old dependencies because all objects are up to date.
-  print "Removing old dependencies file, $outfile\n" unless $silent;
-
-  if (DEBUG) {
-    my $old_output;
-    open(OLD, "<$outfile")
-      and $old_output = <OLD>;
-    close(OLD);
-    print "was: $old_output\n";
-  }
-
-  unlink $outfile;
-}
--- a/configure.in
+++ b/configure.in
@@ -3959,17 +3959,17 @@ dnl = If NSS was not detected in the sys
 dnl = use the one in the source tree (mozilla/security/nss)
 dnl ========================================================
 
 MOZ_ARG_WITH_BOOL(system-nss,
 [  --with-system-nss       Use system installed NSS],
     _USE_SYSTEM_NSS=1 )
 
 if test -n "$_USE_SYSTEM_NSS"; then
-    AM_PATH_NSS(3.14.3, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
+    AM_PATH_NSS(3.15, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
 fi
 
 if test -n "$MOZ_NATIVE_NSS"; then
    NSS_LIBS="$NSS_LIBS -lcrmf"
 else
    NSS_CFLAGS='-I$(LIBXUL_DIST)/include/nss'
 
    if test -z "$GNU_CC" -a "$OS_ARCH" = "WINNT" -o "$OS_ARCH" = "OS2"; then
@@ -4214,17 +4214,16 @@ MOZ_WEBRTC_IN_LIBXUL=
 MOZ_WEBRTC_ASSERT_ALWAYS=1
 MOZ_SCTP=
 MOZ_MEDIA_PLUGINS=
 MOZ_MEDIA_NAVIGATOR=
 MOZ_OMX_PLUGIN=
 MOZ_VP8=
 MOZ_VP8_ERROR_CONCEALMENT=
 MOZ_VP8_ENCODER=
-MOZ_WEBVTT=1
 MOZ_WEBSPEECH=1
 VPX_AS=
 VPX_ASFLAGS=
 VPX_AS_DASH_C_FLAG=
 VPX_AS_CONVERSION=
 VPX_ASM_SUFFIX=
 VPX_X86_ASM=
 VPX_ARM_ASM=
@@ -5696,20 +5695,16 @@ fi
 if test -n "$MOZ_TREMOR"; then
     AC_DEFINE(MOZ_TREMOR)
 fi
 
 if test -n "$MOZ_OPUS"; then
     AC_DEFINE(MOZ_OPUS)
 fi
 
-if test -n "$MOZ_WEBVTT"; then
-    AC_DEFINE(MOZ_WEBVTT)
-fi
-
 dnl ========================================================
 dnl = Check alsa availability on Linux if using sydneyaudio
 dnl ========================================================
 
 dnl If using sydneyaudio with Linux, ensure that the alsa library is available
 if test -n "$MOZ_CUBEB" -a "$OS_TARGET" = "Linux"; then
     MOZ_ALSA=1
 fi
@@ -8904,17 +8899,16 @@ AC_SUBST(MOZ_APP_EXTRA_LIBS)
 AC_SUBST(MOZ_SPEEX_RESAMPLER)
 AC_SUBST(MOZ_SOUNDTOUCH)
 AC_SUBST(MOZ_CUBEB)
 AC_SUBST(MOZ_WAVE)
 AC_SUBST(MOZ_VORBIS)
 AC_SUBST(MOZ_TREMOR)
 AC_SUBST(MOZ_OPUS)
 AC_SUBST(MOZ_WEBM)
-AC_SUBST(MOZ_WEBVTT)
 AC_SUBST(MOZ_DASH)
 AC_SUBST(MOZ_WMF)
 AC_SUBST(MOZ_MEDIA_PLUGINS)
 AC_SUBST(MOZ_OMX_PLUGIN)
 AC_SUBST(MOZ_VP8_ERROR_CONCEALMENT)
 AC_SUBST(MOZ_VP8_ENCODER)
 AC_SUBST(MOZ_VP8)
 AC_SUBST(MOZ_OGG)
@@ -9330,17 +9324,17 @@ if test "$COMPILE_ENVIRONMENT" -a -z "$L
 export WRAP_LDFLAGS
 
 if test -n "$_WRAP_MALLOC"; then
     # Avoid doubling wrap malloc arguments
     _SUBDIR_CONFIG_ARGS="`echo $_SUBDIR_CONFIG_ARGS | sed -e 's/--enable-wrap-malloc *//'`"
 fi
 
 if test -z "$MOZ_NATIVE_NSPR"; then
-    ac_configure_args="$_SUBDIR_CONFIG_ARGS --with-dist-prefix=$MOZ_BUILD_ROOT/dist --with-mozilla --with-android-version=$ANDROID_VERSION"
+    ac_configure_args="$_SUBDIR_CONFIG_ARGS --with-dist-prefix=$MOZ_BUILD_ROOT/dist --with-mozilla"
     if test -z "$MOZ_DEBUG"; then
         ac_configure_args="$ac_configure_args --disable-debug"
     else
         ac_configure_args="$ac_configure_args --enable-debug"
     fi
     if test "$MOZ_OPTIMIZE" = "1"; then
         ac_configure_args="$ac_configure_args --enable-optimize"
     elif test -z "$MOZ_OPTIMIZE"; then
--- a/content/base/public/Element.h
+++ b/content/base/public/Element.h
@@ -1484,16 +1484,21 @@ NS_IMETHOD GetNextElementSibling(nsIDOME
   }                                                                           \
   return CallQueryInterface(element, aNextElementSibling);                    \
 }                                                                             \
 NS_IMETHOD GetChildElementCount(uint32_t* aChildElementCount) MOZ_FINAL       \
 {                                                                             \
   *aChildElementCount = Element::ChildElementCount();                         \
   return NS_OK;                                                               \
 }                                                                             \
+NS_IMETHOD MozRemove() MOZ_FINAL                                              \
+{                                                                             \
+  nsINode::Remove();                                                          \
+  return NS_OK;                                                               \
+}                                                                             \
 NS_IMETHOD GetOnmouseenter(JSContext* cx, JS::Value* aOnmouseenter) MOZ_FINAL \
 {                                                                             \
   return Element::GetOnmouseenter(cx, aOnmouseenter);                         \
 }                                                                             \
 NS_IMETHOD SetOnmouseenter(JSContext* cx,                                     \
                            const JS::Value& aOnmouseenter) MOZ_FINAL          \
 {                                                                             \
   return Element::SetOnmouseenter(cx, aOnmouseenter);                         \
--- a/content/base/public/nsContentCID.h
+++ b/content/base/public/nsContentCID.h
@@ -48,21 +48,16 @@
  0xd6008c40, 0x4dad, 0x11d2,                      \
  {0xb3, 0x28, 0x00, 0x80, 0x5f, 0x8a, 0x38, 0x59}}
 
 #define NS_HTMLOPTIONELEMENT_CID                  \
 { /* a6cf90f5-15b3-11d2-932e-00805f8add32 */      \
  0xa6cf90f5, 0x15b3, 0x11d2,                      \
  {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}}
 
-#define NS_HTMLAUDIOELEMENT_CID                   \
-{ /* 1d40026b-4c44-4f6f-b158-26bb5e9c65e9 */      \
- 0x1d40026b, 0x4c44, 0x4f6f,                      \
- {0xb1, 0x58, 0x26, 0xbb, 0x5e, 0x9c, 0x65, 0xe9}}
-
 #define NS_NAMESPACEMANAGER_CID                   \
 { /* d9783472-8fe9-11d2-9d3c-0060088f9ff7 */      \
  0xd9783472, 0x8fe9, 0x11d2,                      \
  {0x9d, 0x3c, 0x00, 0x60, 0x08, 0x8f, 0x9f, 0xf7}}
 
 #define NS_CONTENTITERATOR_CID \
 {/* {a6cf90e3-15b3-11d2-932e-00805f8add32}*/ \
  0xa6cf90e3, 0x15b3, 0x11d2, {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32 } }
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -2263,17 +2263,17 @@ typedef nsCharSeparatedTokenizerTemplate
 #define NS_HOLD_JS_OBJECTS(obj, clazz)                                         \
   nsContentUtils::HoldJSObjects(NS_CYCLE_COLLECTION_UPCAST(obj, clazz),        \
                                 NS_CYCLE_COLLECTION_PARTICIPANT(clazz))
 
 #define NS_DROP_JS_OBJECTS(obj, clazz)                                         \
   nsContentUtils::DropJSObjects(NS_CYCLE_COLLECTION_UPCAST(obj, clazz))
 
 
-class NS_STACK_CLASS nsCxPusher
+class MOZ_STACK_CLASS nsCxPusher
 {
 public:
   nsCxPusher();
   ~nsCxPusher(); // Calls Pop();
 
   // Returns false if something erroneous happened.
   bool Push(mozilla::dom::EventTarget *aCurrentTarget);
   // If nothing has been pushed to stack, this works like Push.
@@ -2297,45 +2297,45 @@ private:
   bool mScriptIsRunning;
   bool mPushedSomething;
 #ifdef DEBUG
   JSContext* mPushedContext;
   unsigned mCompartmentDepthOnEntry;
 #endif
 };
 
-class NS_STACK_CLASS nsAutoScriptBlocker {
+class MOZ_STACK_CLASS nsAutoScriptBlocker {
 public:
   nsAutoScriptBlocker(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     nsContentUtils::AddScriptBlocker();
   }
   ~nsAutoScriptBlocker() {
     nsContentUtils::RemoveScriptBlocker();
   }
 private:
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-class NS_STACK_CLASS nsAutoScriptBlockerSuppressNodeRemoved :
+class MOZ_STACK_CLASS nsAutoScriptBlockerSuppressNodeRemoved :
                           public nsAutoScriptBlocker {
 public:
   nsAutoScriptBlockerSuppressNodeRemoved() {
 #ifdef DEBUG
     ++nsContentUtils::sDOMNodeRemovedSuppressCount;
 #endif
   }
   ~nsAutoScriptBlockerSuppressNodeRemoved() {
 #ifdef DEBUG
     --nsContentUtils::sDOMNodeRemovedSuppressCount;
 #endif
   }
 };
 
-class NS_STACK_CLASS nsAutoMicroTask
+class MOZ_STACK_CLASS nsAutoMicroTask
 {
 public:
   nsAutoMicroTask()
   {
     nsContentUtils::EnterMicroTask();
   }
   ~nsAutoMicroTask()
   {
@@ -2345,17 +2345,17 @@ public:
 
 namespace mozilla {
 
 /**
  * Use AutoJSContext when you need a JS context on the stack but don't have one
  * passed as a parameter. AutoJSContext will take care of finding the most
  * appropriate JS context and release it when leaving the stack.
  */
-class NS_STACK_CLASS AutoJSContext {
+class MOZ_STACK_CLASS AutoJSContext {
 public:
   AutoJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
   operator JSContext*();
 
 protected:
   AutoJSContext(bool aSafe MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
 
 private:
@@ -2368,17 +2368,17 @@ private:
   nsCxPusher mPusher;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /**
  * SafeAutoJSContext is similar to AutoJSContext but will only return the safe
  * JS context. That means it will never call ::GetCurrentJSContext().
  */
-class NS_STACK_CLASS SafeAutoJSContext : public AutoJSContext {
+class MOZ_STACK_CLASS SafeAutoJSContext : public AutoJSContext {
 public:
   SafeAutoJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
 };
 
 /**
  * Use AutoPushJSContext when you want to use a specific JSContext that may or
  * may not be already on the stack. This differs from nsCxPusher in that it only
  * pushes in the case that the given cx is not the active cx on the JSContext
@@ -2387,17 +2387,17 @@ public:
  * Most consumers of this should probably just use AutoJSContext. But the goal
  * here is to preserve the existing behavior while ensure proper cx-stack
  * semantics in edge cases where the context being used doesn't match the active
  * context.
  *
  * NB: This will not push a null cx even if aCx is null. Make sure you know what
  * you're doing.
  */
-class NS_STACK_CLASS AutoPushJSContext {
+class MOZ_STACK_CLASS AutoPushJSContext {
   nsCxPusher mPusher;
   JSContext* mCx;
 
 public:
     AutoPushJSContext(JSContext* aCx) : mCx(aCx) {
       if (mCx && mCx != nsContentUtils::GetCurrentJSContext()) {
         mPusher.Push(mCx);
       }
--- a/content/base/public/nsDOMFile.h
+++ b/content/base/public/nsDOMFile.h
@@ -485,17 +485,17 @@ public:
     return mFiles.Count();
   }
 
 private:
   nsCOMArray<nsIDOMFile> mFiles;
   nsISupports *mParent;
 };
 
-class NS_STACK_CLASS nsDOMFileInternalUrlHolder {
+class MOZ_STACK_CLASS nsDOMFileInternalUrlHolder {
 public:
   nsDOMFileInternalUrlHolder(nsIDOMBlob* aFile, nsIPrincipal* aPrincipal
                              MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
   ~nsDOMFileInternalUrlHolder();
   nsAutoString mUrl;
 private:
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -2425,17 +2425,17 @@ protected:
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
 
 /**
  * mozAutoSubtreeModified batches DOM mutations so that a DOMSubtreeModified
  * event is dispatched, if necessary, when the outermost mozAutoSubtreeModified
  * object is deleted.
  */
-class NS_STACK_CLASS mozAutoSubtreeModified
+class MOZ_STACK_CLASS mozAutoSubtreeModified
 {
 public:
   /**
    * @param aSubTreeOwner The document in which a subtree will be modified.
    * @param aTarget       The target of the possible DOMSubtreeModified event.
    *                      Can be nullptr, in which case mozAutoSubtreeModified
    *                      is just used to batch DOM mutations.
    */
@@ -2462,17 +2462,17 @@ public:
     }
   }
 
 private:
   nsCOMPtr<nsINode>     mTarget;
   nsCOMPtr<nsIDocument> mSubtreeOwner;
 };
 
-class NS_STACK_CLASS nsAutoSyncOperation
+class MOZ_STACK_CLASS nsAutoSyncOperation
 {
 public:
   nsAutoSyncOperation(nsIDocument* aDocument);
   ~nsAutoSyncOperation();
 private:
   nsCOMArray<nsIDocument> mDocuments;
   uint32_t                mMicroTaskLevel;
 };
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -254,18 +254,18 @@ private:
 // Categories of node properties
 // 0 is global.
 #define DOM_USER_DATA         1
 #define DOM_USER_DATA_HANDLER 2
 #define SMIL_MAPPED_ATTR_ANIMVAL 3
 
 // IID for the nsINode interface
 #define NS_INODE_IID \
-{ 0xb3ee8053, 0x43b0, 0x44bc, \
-  { 0xa0, 0x97, 0x18, 0x24, 0xd2, 0xac, 0x65, 0xb6 } }
+{ 0x5daa9e95, 0xe49c, 0x4b41, \
+  { 0xb2, 0x02, 0xde, 0xa9, 0xd3, 0x06, 0x21, 0x17 } }
 
 /**
  * An internal interface that abstracts some DOMNode-related parts that both
  * nsIContent and nsIDocument share.  An instance of this interface has a list
  * of nsIContent children and provides access to them.
  */
 class nsINode : public mozilla::dom::EventTarget
 {
@@ -1587,16 +1587,21 @@ public:
   nsresult RemoveFromParent()
   {
     nsINode* parent = GetParentNode();
     mozilla::ErrorResult rv;
     parent->RemoveChild(*this, rv);
     return rv.ErrorCode();
   }
 
+  /**
+   * Remove this node from its parent, if any.
+   */
+  void Remove();
+
 protected:
 
   // Override this function to create a custom slots class.
   // Must not return null.
   virtual nsINode::nsSlots* CreateSlots();
 
   bool HasSlots() const
   {
--- a/content/base/public/nsTreeSanitizer.h
+++ b/content/base/public/nsTreeSanitizer.h
@@ -10,17 +10,17 @@
 #include "mozilla/dom/Element.h"
 
 class nsIContent;
 
 /**
  * See the documentation of nsIParserUtils::sanitize for documentation
  * about the default behavior and the configuration options of this sanitizer.
  */
-class NS_STACK_CLASS nsTreeSanitizer {
+class MOZ_STACK_CLASS nsTreeSanitizer {
 
   public:
 
     /**
      * The constructor.
      *
      * @param aFlags Flags from nsIParserUtils
      */
--- a/content/base/public/nsViewportInfo.h
+++ b/content/base/public/nsViewportInfo.h
@@ -18,17 +18,17 @@ static const uint32_t kViewportMaxWidth 
 static const uint32_t kViewportMinHeight = 223;
 static const uint32_t kViewportMaxHeight = 10000;
 static const int32_t  kViewportDefaultScreenWidth = 980;
 
 /**
  * Information retrieved from the <meta name="viewport"> tag. See
  * nsContentUtils::GetViewportInfo for more information on this functionality.
  */
-class NS_STACK_CLASS nsViewportInfo
+class MOZ_STACK_CLASS nsViewportInfo
 {
   public:
     nsViewportInfo(uint32_t aDisplayWidth, uint32_t aDisplayHeight) :
       mDefaultZoom(1.0),
       mMinZoom(kViewportMinScale),
       mMaxZoom(kViewportMaxScale),
       mWidth(aDisplayWidth),
       mHeight(aDisplayHeight),
--- a/content/base/src/DocumentType.cpp
+++ b/content/base/src/DocumentType.cpp
@@ -127,16 +127,23 @@ DocumentType::GetSystemId(nsAString& aSy
 
 NS_IMETHODIMP
 DocumentType::GetInternalSubset(nsAString& aInternalSubset)
 {
   aInternalSubset = mInternalSubset;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+DocumentType::MozRemove()
+{
+  Remove();
+  return NS_OK;
+}
+
 nsGenericDOMDataNode*
 DocumentType::CloneDataNode(nsINodeInfo *aNodeInfo, bool aCloneText) const
 {
   nsCOMPtr<nsINodeInfo> ni = aNodeInfo;
   return new DocumentType(ni.forget(), mPublicId, mSystemId,
                           mInternalSubset);
 }
 
--- a/content/base/src/FileIOObject.cpp
+++ b/content/base/src/FileIOObject.cpp
@@ -212,49 +212,34 @@ FileIOObject::OnStopRequest(nsIRequest* 
 
   // Dispatch event to signify end of a successful operation
   DispatchProgressEvent(successEvent);
   DispatchProgressEvent(termEvent);
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-FileIOObject::Abort()
+void
+FileIOObject::Abort(ErrorResult& aRv)
 {
   if (mReadyState != 1) {
     // XXX The spec doesn't say this
-    return NS_ERROR_DOM_FILE_ABORT_ERR;
+    aRv.Throw(NS_ERROR_DOM_FILE_ABORT_ERR);
+    return;
   }
 
   ClearProgressEventTimer();
 
   mReadyState = 2; // There are DONE constants on multiple interfaces,
                    // but they all have value 2.
   // XXX The spec doesn't say this
   mError = DOMError::CreateWithName(NS_LITERAL_STRING("AbortError"));
 
   nsString finalEvent;
-  nsresult rv = DoAbort(finalEvent);
+  DoAbort(finalEvent);
 
   // Dispatch the events
   DispatchProgressEvent(NS_LITERAL_STRING(ABORT_STR));
   DispatchProgressEvent(finalEvent);
-
-  return rv;
-}
-
-NS_IMETHODIMP
-FileIOObject::GetReadyState(uint16_t *aReadyState)
-{
-  *aReadyState = mReadyState;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-FileIOObject::GetError(nsIDOMDOMError** aError)
-{
-  NS_IF_ADDREF(*aError = mError);
-  return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/base/src/FileIOObject.h
+++ b/content/base/src/FileIOObject.h
@@ -15,55 +15,68 @@
 #include "nsITimer.h"
 #include "nsCOMPtr.h"
 
 #include "mozilla/dom/DOMError.h"
 
 #define NS_PROGRESS_EVENT_INTERVAL 50
 
 namespace mozilla {
+
+class ErrorResult;
+
 namespace dom {
 
 extern const uint64_t kUnknownSize;
 
 // A common base class for FileReader and FileSaver
 
 class FileIOObject : public nsDOMEventTargetHelper,
                      public nsIStreamListener,
                      public nsITimerCallback
 {
 public:
   FileIOObject();
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // Common methods
-  NS_METHOD Abort();
-  NS_METHOD GetReadyState(uint16_t* aReadyState);
-  NS_METHOD GetError(nsIDOMDOMError** aError);
+  void Abort(ErrorResult& aRv);
+  uint16_t ReadyState() const
+  {
+    return mReadyState;
+  }
+  nsIDOMDOMError* GetError() const
+  {
+    return mError;
+  }
 
   NS_METHOD GetOnabort(JSContext* aCx, JS::Value* aValue);
   NS_METHOD SetOnabort(JSContext* aCx, const JS::Value& aValue);
   NS_METHOD GetOnerror(JSContext* aCx, JS::Value* aValue);
   NS_METHOD SetOnerror(JSContext* aCx, const JS::Value& aValue);
   NS_METHOD GetOnprogress(JSContext* aCx, JS::Value* aValue);
   NS_METHOD SetOnprogress(JSContext* aCx, const JS::Value& aValue);
 
+  IMPL_EVENT_HANDLER(abort)
+  IMPL_EVENT_HANDLER(error)
+  IMPL_EVENT_HANDLER(progress)
+
   NS_DECL_NSITIMERCALLBACK
 
   NS_DECL_NSISTREAMLISTENER
 
   NS_DECL_NSIREQUESTOBSERVER
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileIOObject,
                                            nsDOMEventTargetHelper)
 
 protected:
   // Implemented by the derived class to do whatever it needs to do for abort
-  NS_IMETHOD DoAbort(nsAString& aEvent) = 0;
+  virtual void DoAbort(nsAString& aEvent) = 0;
   // for onStartRequest (this has a default impl since FileReader doesn't need
   // special handling
   NS_IMETHOD DoOnStartRequest(nsIRequest *aRequest, nsISupports *aContext);
   // for onStopRequest
   NS_IMETHOD DoOnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
                              nsresult aStatus, nsAString& aSuccessEvent,
                              nsAString& aTerminationEvent) = 0;
   // and for onDataAvailable
--- a/content/base/src/NodeIterator.cpp
+++ b/content/base/src/NodeIterator.cpp
@@ -10,23 +10,20 @@
 
 #include "mozilla/dom/NodeIterator.h"
 
 #include "nsIDOMNode.h"
 #include "nsError.h"
 
 #include "nsIContent.h"
 #include "nsIDocument.h"
-#include "nsDOMClassInfoID.h"
 #include "nsContentUtils.h"
 #include "nsCOMPtr.h"
 #include "mozilla/dom/NodeIteratorBinding.h"
 
-DOMCI_DATA(NodeIterator, mozilla::dom::NodeIterator)
-
 namespace mozilla {
 namespace dom {
 
 /*
  * NodePointer implementation
  */
 NodeIterator::NodePointer::NodePointer(nsINode *aNode, bool aBeforeNode) :
     mNode(aNode),
@@ -170,17 +167,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilter)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 // QueryInterface implementation for NodeIterator
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NodeIterator)
     NS_INTERFACE_MAP_ENTRY(nsIDOMNodeIterator)
     NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMNodeIterator)
-    NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(NodeIterator)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(NodeIterator)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(NodeIterator)
 
 /* readonly attribute nsIDOMNode root; */
 NS_IMETHODIMP NodeIterator::GetRoot(nsIDOMNode * *aRoot)
 {
--- a/content/base/src/TreeWalker.cpp
+++ b/content/base/src/TreeWalker.cpp
@@ -9,22 +9,19 @@
  */
 
 #include "mozilla/dom/TreeWalker.h"
 
 #include "nsIContent.h"
 #include "nsIDOMNode.h"
 #include "nsError.h"
 #include "nsINode.h"
-#include "nsDOMClassInfoID.h"
 #include "nsContentUtils.h"
 #include "mozilla/dom/TreeWalkerBinding.h"
 
-DOMCI_DATA(TreeWalker, mozilla::dom::TreeWalker)
-
 namespace mozilla {
 namespace dom {
 
 /*
  * Factories, constructors and destructors
  */
 
 TreeWalker::TreeWalker(nsINode *aRoot,
@@ -45,17 +42,16 @@ TreeWalker::~TreeWalker()
  */
 
 NS_IMPL_CYCLE_COLLECTION_3(TreeWalker, mFilter, mCurrentNode, mRoot)
 
 // QueryInterface implementation for TreeWalker
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TreeWalker)
     NS_INTERFACE_MAP_ENTRY(nsIDOMTreeWalker)
     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMTreeWalker)
-    NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TreeWalker)
 NS_INTERFACE_MAP_END
 
 // Have to pass in dom::TreeWalker because a11y has an a11y::TreeWalker that
 // passes TreeWalker so refcount logging would get confused on the name
 // collision.
 NS_IMPL_CYCLE_COLLECTING_ADDREF(dom::TreeWalker)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(dom::TreeWalker)
 
--- a/content/base/src/mozAutoDocUpdate.h
+++ b/content/base/src/mozAutoDocUpdate.h
@@ -11,17 +11,17 @@
 
 /**
  * Helper class to automatically handle batching of document updates.  This
  * class will call BeginUpdate on construction and EndUpdate on destruction on
  * the given document with the given update type.  The document could be null,
  * in which case no updates will be called.  The constructor also takes a
  * boolean that can be set to false to prevent notifications.
  */
-class NS_STACK_CLASS mozAutoDocUpdate
+class MOZ_STACK_CLASS mozAutoDocUpdate
 {
 public:
   mozAutoDocUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType,
                    bool aNotify) :
     mDocument(aNotify ? aDocument : nullptr),
     mUpdateType(aUpdateType)
   {
     if (mDocument) {
@@ -58,17 +58,17 @@ private:
 /**
  * Creates an update batch only under certain conditions.
  * Use this rather than mozAutoDocUpdate when you expect inner updates
  * to notify but you don't always want to spec cycles creating a batch.
  * This is needed to avoid having this batch always create a blocker,
  * but then have inner mozAutoDocUpdate call the last EndUpdate before.
  * we remove that blocker. See bug 423269.
  */
-class NS_STACK_CLASS mozAutoDocConditionalContentUpdateBatch
+class MOZ_STACK_CLASS mozAutoDocConditionalContentUpdateBatch
 {
 public:
   mozAutoDocConditionalContentUpdateBatch(nsIDocument* aDocument,
                                           bool aNotify) :
     mDocument(aNotify ? aDocument : nullptr)
   {
     if (mDocument) {
       mDocument->BeginUpdate(UPDATE_CONTENT_MODEL);
--- a/content/base/src/nsAttrValueOrString.h
+++ b/content/base/src/nsAttrValueOrString.h
@@ -13,17 +13,17 @@
  */
 
 #ifndef nsAttrValueOrString_h___
 #define nsAttrValueOrString_h___
 
 #include "nsString.h"
 #include "nsAttrValue.h"
 
-class NS_STACK_CLASS nsAttrValueOrString
+class MOZ_STACK_CLASS nsAttrValueOrString
 {
 public:
   nsAttrValueOrString(const nsAString& aValue)
     : mAttrValue(nullptr)
     , mStringPtr(&aValue)
     , mCheapString(nullptr)
   { }
   nsAttrValueOrString(const nsAttrValue& aValue)
--- a/content/base/src/nsContentAreaDragDrop.cpp
+++ b/content/base/src/nsContentAreaDragDrop.cpp
@@ -48,17 +48,17 @@
 #include "nsEscape.h"
 #include "nsContentUtils.h"
 #include "nsIMIMEService.h"
 #include "imgIContainer.h"
 #include "imgIRequest.h"
 #include "nsDOMDataTransfer.h"
 #include "mozilla/dom/Element.h"
 
-class NS_STACK_CLASS DragDataProducer
+class MOZ_STACK_CLASS DragDataProducer
 {
 public:
   DragDataProducer(nsPIDOMWindow* aWindow,
                    nsIContent* aTarget,
                    nsIContent* aSelectionTargetNode,
                    bool aIsAltKeyPressed);
   nsresult Produce(nsDOMDataTransfer* aDataTransfer,
                    bool* aCanDrag,
--- a/content/base/src/nsContentList.h
+++ b/content/base/src/nsContentList.h
@@ -429,17 +429,17 @@ protected:
 #endif
 };
 
 /**
  * A class of cacheable content list; cached on the combination of aRootNode + aFunc + aDataString
  */
 class nsCacheableFuncStringContentList;
 
-class NS_STACK_CLASS nsFuncStringCacheKey {
+class MOZ_STACK_CLASS nsFuncStringCacheKey {
 public:
   nsFuncStringCacheKey(nsINode* aRootNode,
                        nsContentListMatchFunc aFunc,
                        const nsAString& aString) :
     mRootNode(aRootNode),
     mFunc(aFunc),
     mString(aString)
     {}
--- a/content/base/src/nsDOMBlobBuilder.cpp
+++ b/content/base/src/nsDOMBlobBuilder.cpp
@@ -184,24 +184,24 @@ nsDOMMultipartFile::InitBlob(JSContext* 
                              uint32_t aArgc,
                              JS::Value* aArgv,
                              UnwrapFuncPtr aUnwrapFunc)
 {
   bool nativeEOL = false;
   if (aArgc > 1) {
     if (NS_IsMainThread()) {
       BlobPropertyBag d;
-      if (!d.Init(aCx, nullptr, aArgv[1])) {
+      if (!d.Init(aCx, JS::NullPtr(), aArgv[1])) {
         return NS_ERROR_TYPE_ERR;
       }
       mContentType = d.mType;
       nativeEOL = d.mEndings == EndingTypesValues::Native;
     } else {
       BlobPropertyBagWorkers d;
-      if (!d.Init(aCx, nullptr, aArgv[1])) {
+      if (!d.Init(aCx, JS::NullPtr(), aArgv[1])) {
         return NS_ERROR_TYPE_ERR;
       }
       mContentType = d.mType;
       nativeEOL = d.mEndings == EndingTypesValues::Native;
     }
   }
 
   if (aArgc > 0) {
@@ -278,17 +278,17 @@ nsDOMMultipartFile::InitFile(JSContext* 
   if (!nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR; // Real short trip
   }
 
   NS_ENSURE_TRUE(aArgc > 0, NS_ERROR_UNEXPECTED);
 
   if (aArgc > 1) {
     FilePropertyBag d;
-    if (!d.Init(aCx, nullptr, aArgv[1])) {
+    if (!d.Init(aCx, JS::NullPtr(), aArgv[1])) {
       return NS_ERROR_TYPE_ERR;
     }
     mName = d.mName;
     mContentType = d.mType;
   }
 
 
   // We expect to get a path to represent as a File object or
--- a/content/base/src/nsDOMDataChannel.cpp
+++ b/content/base/src/nsDOMDataChannel.cpp
@@ -217,21 +217,22 @@ nsDOMDataChannel::GetOrdered(bool* aOrde
   *aOrdered = mDataChannel->GetOrdered();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMDataChannel::GetReadyState(nsAString& aReadyState)
 {
   uint16_t readyState = mDataChannel->GetReadyState();
+  // From the WebRTC spec
   const char * stateName[] = {
-    "Connecting",
-    "Open",
-    "Closing",
-    "Closed"
+    "connecting",
+    "open",
+    "closing",
+    "closed"
   };
   MOZ_ASSERT(/*readyState >= mozilla::DataChannel::CONNECTING && */ // Always true due to datatypes
              readyState <= mozilla::DataChannel::CLOSED);
   aReadyState.AssignASCII(stateName[readyState]);
 
   return NS_OK;
 }
 
--- a/content/base/src/nsDOMFileReader.cpp
+++ b/content/base/src/nsDOMFileReader.cpp
@@ -35,32 +35,31 @@
 #include "nsIStreamConverterService.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsLayoutStatics.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "mozilla/Base64.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/dom/FileReaderBinding.h"
 #include "xpcpublic.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsDOMJSUtils.h"
 #include "nsDOMEventTargetHelper.h"
 
 #include "jsfriendapi.h"
 
 using namespace mozilla;
+using namespace mozilla::dom;
 
 #define LOAD_STR "load"
 #define LOADSTART_STR "loadstart"
 #define LOADEND_STR "loadend"
 
-using mozilla::dom::EncodingUtils;
-using mozilla::dom::FileIOObject;
-
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMFileReader,
                                                   FileIOObject)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFile)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMFileReader,
                                                 FileIOObject)
@@ -70,24 +69,21 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsDOMFileReader,
                                                nsDOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
-DOMCI_DATA(FileReader, nsDOMFileReader)
-
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMFileReader)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIDOMFileReader)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(FileReader)
 NS_INTERFACE_MAP_END_INHERITING(FileIOObject)
 
 NS_IMPL_ADDREF_INHERITED(nsDOMFileReader, FileIOObject)
 NS_IMPL_RELEASE_INHERITED(nsDOMFileReader, FileIOObject)
 
 NS_IMPL_EVENT_HANDLER(nsDOMFileReader, load)
 NS_IMPL_EVENT_HANDLER(nsDOMFileReader, loadend)
 NS_IMPL_EVENT_HANDLER(nsDOMFileReader, loadstart)
@@ -107,16 +103,17 @@ nsDOMFileReader::RootResultArrayBuffer()
 
 nsDOMFileReader::nsDOMFileReader()
   : mFileData(nullptr),
     mDataLen(0), mDataFormat(FILE_AS_BINARY),
     mResultArrayBuffer(nullptr)     
 {
   nsLayoutStatics::AddRef();
   SetDOMStringToNull(mResult);
+  SetIsDOMBinding();
 }
 
 nsDOMFileReader::~nsDOMFileReader()
 {
   FreeFileData();
 
   nsLayoutStatics::Release();
 }
@@ -133,51 +130,64 @@ nsDOMFileReader::Init()
     NS_ENSURE_SUCCESS(rv, rv);
   }
   NS_ENSURE_STATE(subjectPrincipal);
   mPrincipal.swap(subjectPrincipal);
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsDOMFileReader::Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
-                            uint32_t argc, JS::Value *argv)
+/* static */ already_AddRefed<nsDOMFileReader>
+nsDOMFileReader::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
 {
-  nsCOMPtr<nsPIDOMWindow> owner = do_QueryInterface(aOwner);
+  nsRefPtr<nsDOMFileReader> fileReader = new nsDOMFileReader();
+
+  nsCOMPtr<nsPIDOMWindow> owner = do_QueryInterface(aGlobal.Get());
   if (!owner) {
     NS_WARNING("Unexpected nsIJSNativeInitializer owner");
-    return NS_OK;
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
   }
 
-  BindToOwner(owner);
+  fileReader->BindToOwner(owner);
 
   // This object is bound to a |window|,
   // so reset the principal.
-  nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal = do_QueryInterface(aOwner);
-  NS_ENSURE_STATE(scriptPrincipal);
-  mPrincipal = scriptPrincipal->GetPrincipal();
-
-  return NS_OK; 
+  nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal = do_QueryInterface(owner);
+  if (!scriptPrincipal) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+  fileReader->mPrincipal = scriptPrincipal->GetPrincipal();
+  return fileReader.forget();
 }
 
 // nsIInterfaceRequestor
 
 NS_IMETHODIMP
 nsDOMFileReader::GetInterface(const nsIID & aIID, void **aResult)
 {
   return QueryInterface(aIID, aResult);
 }
 
 // nsIDOMFileReader
 
 NS_IMETHODIMP
 nsDOMFileReader::GetReadyState(uint16_t *aReadyState)
 {
-  return FileIOObject::GetReadyState(aReadyState);
+  *aReadyState = ReadyState();
+  return NS_OK;
+}
+
+JS::Value
+nsDOMFileReader::GetResult(JSContext* aCx, ErrorResult& aRv)
+{
+  JS::Value result = JS::UndefinedValue();
+  aRv = GetResult(aCx, &result);
+  return result;
 }
 
 NS_IMETHODIMP
 nsDOMFileReader::GetResult(JSContext* aCx, JS::Value* aResult)
 {
   if (mDataFormat == FILE_AS_ARRAYBUFFER) {
     if (mReadyState == nsIDOMFileReader::DONE && mResultArrayBuffer) {
       JSObject* tmp = mResultArrayBuffer;
@@ -196,51 +206,66 @@ nsDOMFileReader::GetResult(JSContext* aC
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMFileReader::GetError(nsIDOMDOMError** aError)
 {
-  return FileIOObject::GetError(aError);
+  NS_IF_ADDREF(*aError = GetError());
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMFileReader::ReadAsArrayBuffer(nsIDOMBlob* aFile, JSContext* aCx)
 {
-  return ReadFileContent(aCx, aFile, EmptyString(), FILE_AS_ARRAYBUFFER);
+  NS_ENSURE_TRUE(aFile, NS_ERROR_NULL_POINTER);
+  ErrorResult rv;
+  ReadAsArrayBuffer(aCx, aFile, rv);
+  return rv.ErrorCode();
 }
 
 NS_IMETHODIMP
 nsDOMFileReader::ReadAsBinaryString(nsIDOMBlob* aFile)
 {
-  return ReadFileContent(nullptr, aFile, EmptyString(), FILE_AS_BINARY);
+  NS_ENSURE_TRUE(aFile, NS_ERROR_NULL_POINTER);
+  ErrorResult rv;
+  ReadAsBinaryString(aFile, rv);
+  return rv.ErrorCode();
 }
 
 NS_IMETHODIMP
 nsDOMFileReader::ReadAsText(nsIDOMBlob* aFile,
                             const nsAString &aCharset)
 {
-  return ReadFileContent(nullptr, aFile, aCharset, FILE_AS_TEXT);
+  NS_ENSURE_TRUE(aFile, NS_ERROR_NULL_POINTER);
+  ErrorResult rv;
+  ReadAsText(aFile, aCharset, rv);
+  return rv.ErrorCode();
 }
 
 NS_IMETHODIMP
 nsDOMFileReader::ReadAsDataURL(nsIDOMBlob* aFile)
 {
-  return ReadFileContent(nullptr, aFile, EmptyString(), FILE_AS_DATAURL);
+  NS_ENSURE_TRUE(aFile, NS_ERROR_NULL_POINTER);
+  ErrorResult rv;
+  ReadAsDataURL(aFile, rv);
+  return rv.ErrorCode();
 }
 
 NS_IMETHODIMP
 nsDOMFileReader::Abort()
 {
-  return FileIOObject::Abort();
+  ErrorResult rv;
+  FileIOObject::Abort(rv);
+  return rv.ErrorCode();
 }
 
-nsresult
+/* virtual */ void
 nsDOMFileReader::DoAbort(nsAString& aEvent)
 {
   // Revert status and result attributes
   SetDOMStringToNull(mResult);
   mResultArrayBuffer = nullptr;
     
   // Non-null channel indicates a read is currently active
   if (mChannel) {
@@ -250,17 +275,16 @@ nsDOMFileReader::DoAbort(nsAString& aEve
   }
   mFile = nullptr;
 
   //Clean up memory buffer
   FreeFileData();
 
   // Tell the base class which event to dispatch
   aEvent = NS_LITERAL_STRING(LOADEND_STR);
-  return NS_OK;
 }
 
 static
 NS_METHOD
 ReadFuncBinaryString(nsIInputStream* in,
                      void* closure,
                      const char* fromRawSegment,
                      uint32_t toOffset,
@@ -370,24 +394,24 @@ nsDOMFileReader::DoOnStopRequest(nsIRequ
 
   FreeFileData();
 
   return rv;
 }
 
 // Helper methods
 
-nsresult
+void
 nsDOMFileReader::ReadFileContent(JSContext* aCx,
                                  nsIDOMBlob* aFile,
                                  const nsAString &aCharset,
-                                 eDataFormat aDataFormat)
+                                 eDataFormat aDataFormat,
+                                 ErrorResult& aRv)
 {
-  nsresult rv;
-  NS_ENSURE_TRUE(aFile, NS_ERROR_NULL_POINTER);
+  MOZ_ASSERT(aFile);
 
   //Implicit abort to clear any other activity going on
   Abort();
   mError = nullptr;
   SetDOMStringToNull(mResult);
   mTransferred = 0;
   mTotal = 0;
   mReadyState = nsIDOMFileReader::EMPTY;
@@ -400,54 +424,55 @@ nsDOMFileReader::ReadFileContent(JSConte
   //Establish a channel with our file
   {
     // Hold the internal URL alive only as long as necessary
     // After the channel is created it will own whatever is backing
     // the DOMFile.
     nsDOMFileInternalUrlHolder urlHolder(mFile, mPrincipal);
 
     nsCOMPtr<nsIURI> uri;
-    rv = NS_NewURI(getter_AddRefs(uri), urlHolder.mUrl);
-    NS_ENSURE_SUCCESS(rv, rv);
+    aRv = NS_NewURI(getter_AddRefs(uri), urlHolder.mUrl);
+    NS_ENSURE_SUCCESS_VOID(aRv.ErrorCode());
 
     nsCOMPtr<nsILoadGroup> loadGroup;
     if (HasOrHasHadOwner()) {
-      NS_ENSURE_STATE(GetOwner());
+      if (!GetOwner()) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return;
+      }
       nsIDocument* doc = GetOwner()->GetExtantDoc();
       if (doc) {
         loadGroup = doc->GetDocumentLoadGroup();
       }
     }
 
-    rv = NS_NewChannel(getter_AddRefs(mChannel), uri, nullptr, loadGroup,
-                       nullptr, nsIRequest::LOAD_BACKGROUND);
-    NS_ENSURE_SUCCESS(rv, rv);
+    aRv = NS_NewChannel(getter_AddRefs(mChannel), uri, nullptr, loadGroup,
+                        nullptr, nsIRequest::LOAD_BACKGROUND);
+    NS_ENSURE_SUCCESS_VOID(aRv.ErrorCode());
   }
 
   //Obtain the total size of the file before reading
   mTotal = mozilla::dom::kUnknownSize;
   mFile->GetSize(&mTotal);
 
-  rv = mChannel->AsyncOpen(this, nullptr);
-  NS_ENSURE_SUCCESS(rv, rv);
+  aRv = mChannel->AsyncOpen(this, nullptr);
+  NS_ENSURE_SUCCESS_VOID(aRv.ErrorCode());
 
   //FileReader should be in loading state here
   mReadyState = nsIDOMFileReader::LOADING;
   DispatchProgressEvent(NS_LITERAL_STRING(LOADSTART_STR));
-  
+
   if (mDataFormat == FILE_AS_ARRAYBUFFER) {
     RootResultArrayBuffer();
     mResultArrayBuffer = JS_NewArrayBuffer(aCx, mTotal);
     if (!mResultArrayBuffer) {
       NS_WARNING("Failed to create JS array buffer");
-      return NS_ERROR_FAILURE;
+      aRv.Throw(NS_ERROR_FAILURE);
     }
   }
- 
-  return NS_OK;
 }
 
 nsresult
 nsDOMFileReader::GetAsText(const nsACString &aCharset,
                            const char *aFileData,
                            uint32_t aDataLen,
                            nsAString& aResult)
 {
@@ -520,8 +545,14 @@ nsDOMFileReader::ConvertStream(const cha
     return NS_ERROR_OUT_OF_MEMORY;
 
   int32_t srcLength = aDataLen;
   rv = unicodeDecoder->Convert(aFileData, &srcLength, aResult.BeginWriting(), &destLength);
   aResult.SetLength(destLength); //Trim down to the correct size
 
   return rv;
 }
+
+/* virtual */ JSObject*
+nsDOMFileReader::WrapObject(JSContext* aCx, JSObject* aScope)
+{
+  return FileReaderBinding::Wrap(aCx, aScope, this);
+}
--- a/content/base/src/nsDOMFileReader.h
+++ b/content/base/src/nsDOMFileReader.h
@@ -25,60 +25,108 @@
 #include "nsIStreamLoader.h"
 #include "nsIChannel.h"
 
 #include "FileIOObject.h"
 
 class nsDOMFileReader : public mozilla::dom::FileIOObject,
                         public nsIDOMFileReader,
                         public nsIInterfaceRequestor,
-                        public nsSupportsWeakReference,
-                        public nsIJSNativeInitializer
+                        public nsSupportsWeakReference
 {
+  typedef mozilla::ErrorResult ErrorResult;
 public:
   nsDOMFileReader();
   virtual ~nsDOMFileReader();
 
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_NSIDOMFILEREADER
 
   NS_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper::)
 
   // nsIInterfaceRequestor 
   NS_DECL_NSIINTERFACEREQUESTOR
 
-  // nsIJSNativeInitializer
-  NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
-                        uint32_t argc, JS::Value* argv);
-
   // FileIOObject overrides
-  NS_IMETHOD DoAbort(nsAString& aEvent);
+  virtual void DoAbort(nsAString& aEvent) MOZ_OVERRIDE;
   NS_IMETHOD DoOnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
                              nsresult aStatus, nsAString& aSuccessEvent,
                              nsAString& aTerminationEvent);
   NS_IMETHOD DoOnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
                                nsIInputStream* aInputStream, uint64_t aOffset,
                                uint32_t aCount);
 
+  nsPIDOMWindow* GetParentObject() const
+  {
+    return GetOwner();
+  }
+  virtual JSObject* WrapObject(JSContext* aCx, JSObject* aScope) MOZ_OVERRIDE;
+
+  // WebIDL
+  static already_AddRefed<nsDOMFileReader>
+  Constructor(const mozilla::dom::GlobalObject& aGlobal, ErrorResult& aRv);
+  void ReadAsArrayBuffer(JSContext* aCx, nsIDOMBlob* aBlob, ErrorResult& aRv)
+  {
+    MOZ_ASSERT(aBlob);
+    ReadFileContent(aCx, aBlob, EmptyString(), FILE_AS_ARRAYBUFFER, aRv);
+  }
+  void ReadAsText(nsIDOMBlob* aBlob, const nsAString& aLabel, ErrorResult& aRv)
+  {
+    MOZ_ASSERT(aBlob);
+    ReadFileContent(nullptr, aBlob, aLabel, FILE_AS_TEXT, aRv);
+  }
+  void ReadAsDataURL(nsIDOMBlob* aBlob, ErrorResult& aRv)
+  {
+    MOZ_ASSERT(aBlob);
+    ReadFileContent(nullptr, aBlob, EmptyString(), FILE_AS_DATAURL, aRv);
+  }
+
+  using FileIOObject::Abort;
+
+  // Inherited ReadyState().
+
+  JS::Value GetResult(JSContext* aCx, ErrorResult& aRv);
+
+  using FileIOObject::GetError;
+
+  IMPL_EVENT_HANDLER(loadstart)
+  using FileIOObject::GetOnprogress;
+  using FileIOObject::SetOnprogress;
+  IMPL_EVENT_HANDLER(load)
+  using FileIOObject::GetOnabort;
+  using FileIOObject::SetOnabort;
+  using FileIOObject::GetOnerror;
+  using FileIOObject::SetOnerror;
+  IMPL_EVENT_HANDLER(loadend)
+
+  void ReadAsBinaryString(nsIDOMBlob* aBlob, ErrorResult& aRv)
+  {
+    MOZ_ASSERT(aBlob);
+    ReadFileContent(nullptr, aBlob, EmptyString(), FILE_AS_BINARY, aRv);
+  }
+
+
   nsresult Init();
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsDOMFileReader,
                                                          FileIOObject)
   void RootResultArrayBuffer();
 
 protected:
   enum eDataFormat {
     FILE_AS_ARRAYBUFFER,
     FILE_AS_BINARY,
     FILE_AS_TEXT,
     FILE_AS_DATAURL
   };
 
-  nsresult ReadFileContent(JSContext* aCx, nsIDOMBlob *aFile, const nsAString &aCharset, eDataFormat aDataFormat); 
+  void ReadFileContent(JSContext* aCx, nsIDOMBlob* aBlob,
+                       const nsAString &aCharset, eDataFormat aDataFormat,
+                       ErrorResult& aRv);
   nsresult GetAsText(const nsACString &aCharset,
                      const char *aFileData, uint32_t aDataLen, nsAString &aResult);
   nsresult GetAsDataURL(nsIDOMBlob *aFile, const char *aFileData, uint32_t aDataLen, nsAString &aResult); 
   nsresult ConvertStream(const char *aFileData, uint32_t aDataLen, const char *aCharset, nsAString &aResult); 
 
   void FreeFileData() {
     moz_free(mFileData);
     mFileData = nullptr;
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1847,23 +1847,16 @@ nsDocument::Init()
     return NS_ERROR_ALREADY_INITIALIZED;
   }
 
   mIdentifierMap.Init();
   mStyledLinks.Init();
   mRadioGroups.Init();
   mCustomPrototypes.Init();
 
-  // If after creation the owner js global is not set for a document
-  // we use the default compartment for this document, instead of creating
-  // wrapper in some random compartment when the document is exposed to js
-  // via some events.
-  mScopeObject = do_GetWeakReference(xpc::GetNativeForGlobal(xpc::GetJunkScope()));
-  MOZ_ASSERT(mScopeObject);
-
   // Force initialization.
   nsINode::nsSlots* slots = Slots();
 
   // Prepend self as mutation-observer whether we need it or not (some
   // subclasses currently do, other don't). This is because the code in
   // nsNodeUtils always notifies the first observer first, expecting the
   // first observer to be the document.
   NS_ENSURE_TRUE(slots->mMutationObservers.PrependElementUnlessExists(static_cast<nsIMutationObserver*>(this)),
@@ -2420,27 +2413,24 @@ nsDocument::InitCSP(nsIChannel* aChannel
         NS_LITERAL_CSTRING("content-security-policy-report-only"),
         tCspROHeaderValue);
   }
   NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
   NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
   NS_ConvertASCIItoUTF16 cspOldHeaderValue(tCspOldHeaderValue);
   NS_ConvertASCIItoUTF16 cspOldROHeaderValue(tCspOldROHeaderValue);
 
-  // Until we want to turn on our CSP 1.0 spec compliant support
-  // only use the 1.0 spec compliant headers if a pref to do so
-  // is set (this lets us land CSP 1.0 support with tests without
-  // having to turn it on before it's ready). When we turn on
-  // CSP 1.0 in the release, we should remove this pref check.
-  // This pref will never be set by default, it should only
-  // be created/set by the CSP tests.
-  if (!cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty()) {
-    bool specCompliantEnabled =
-      Preferences::GetBool("security.csp.speccompliant");
-
+  // Only use the CSP 1.0 spec compliant headers if a pref to do so
+  // is set. This lets us turn on the 1.0 parser per platform. This
+  // pref is also set by the tests for 1.0 spec compliant CSP.
+  bool specCompliantEnabled =
+    Preferences::GetBool("security.csp.speccompliant");
+
+  if ((!cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty()) &&
+       !specCompliantEnabled) {
     // If spec compliant pref isn't set, pretend we never got
     // these headers.
     if (!specCompliantEnabled) {
       PR_LOG(gCspPRLog, PR_LOG_DEBUG,
              ("Got spec compliant CSP headers but pref was not set"));
       cspHeaderValue.Truncate();
       cspROHeaderValue.Truncate();
     }
@@ -2525,17 +2515,18 @@ nsDocument::InitCSP(nsIChannel* aChannel
       appCSP = Preferences::GetString("security.apps.privileged.CSP.default");
       NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.privileged.CSP.default");
     } else if (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED) {
       appCSP = Preferences::GetString("security.apps.certified.CSP.default");
       NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.certified.CSP.default");
     }
 
     if (appCSP)
-      csp->RefinePolicy(appCSP, chanURI, true);
+      // Use the 1.0 CSP parser for apps if the pref to do so is set.
+      csp->RefinePolicy(appCSP, chanURI, specCompliantEnabled);
   }
 
   // While we are supporting both CSP 1.0 and the x- headers, the 1.0 headers
   // take priority.  If any spec-compliant headers are present, the x- headers
   // are ignored, and the spec compliant parser is used.
   bool cspSpecCompliant = (!cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty());
 
   // If the old header is present, warn that it will be deprecated.
@@ -5008,17 +4999,17 @@ nsDocument::Register(const nsAString& aN
                      JSContext* aCx, uint8_t aOptionalArgc,
                      jsval* aConstructor /* out param */)
 {
   ElementRegistrationOptions options;
   if (aOptionalArgc > 0) {
     JSAutoCompartment ac(aCx, GetWrapper());
     NS_ENSURE_TRUE(JS_WrapValue(aCx, const_cast<JS::Value*>(&aOptions)),
                    NS_ERROR_UNEXPECTED);
-    NS_ENSURE_TRUE(options.Init(aCx, nullptr, aOptions),
+    NS_ENSURE_TRUE(options.Init(aCx, JS::NullPtr(), aOptions),
                    NS_ERROR_UNEXPECTED);
   }
 
   ErrorResult rv;
   JSObject* object = Register(aCx, aName, options, rv);
   if (rv.Failed()) {
     return rv.ErrorCode();
   }
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -737,17 +737,17 @@ AllDescendantsOfType(nsIDocShellTreeItem
 
   return true;
 }
 
 /**
  * A class that automatically sets mInShow to false when it goes
  * out of scope.
  */
-class NS_STACK_CLASS AutoResetInShow {
+class MOZ_STACK_CLASS AutoResetInShow {
   private:
     nsFrameLoader* mFrameLoader;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   public:
     AutoResetInShow(nsFrameLoader* aFrameLoader MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : mFrameLoader(aFrameLoader)
     {
       MOZ_GUARD_OBJECT_NOTIFIER_INIT;
@@ -814,16 +814,17 @@ nsFrameLoader::Show(int32_t marginWidth,
   NS_ASSERTION(baseWindow, "Found a nsIDocShell that isn't a nsIBaseWindow.");
   baseWindow->InitWindow(nullptr, view->GetWidget(), 0, 0,
                          size.width, size.height);
   // This is kinda whacky, this "Create()" call doesn't really
   // create anything, one starts to wonder why this was named
   // "Create"...
   baseWindow->Create();
   baseWindow->SetVisibility(true);
+  NS_ENSURE_TRUE(mDocShell, false);
 
   // Trigger editor re-initialization if midas is turned on in the
   // sub-document. This shouldn't be necessary, but given the way our
   // editor works, it is. See
   // https://bugzilla.mozilla.org/show_bug.cgi?id=284245
   nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
   if (presShell) {
     nsCOMPtr<nsIDOMHTMLDocument> doc =
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@