Merge from mozilla-central.
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 25 Jan 2013 10:20:00 +0100
changeset 127198 1cc0768e68c499567bcda62144a04c18f41f2d53
parent 127197 cd4699153db0c345f4fb2c8e1934e4e2ecbc47e8 (current diff)
parent 119809 a207f33adc1a12ef7e83183d557a34dcac7a8f01 (diff)
child 127199 8a3901221fd9bc9bb39e759f4a49bedffbaec287
push id24503
push userjandemooij@gmail.com
push dateWed, 03 Apr 2013 15:43:00 +0000
treeherdermozilla-central@b5cb88ccd907 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone21.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge from mozilla-central.
browser/base/content/test/social/browser_social.js
browser/components/privatebrowsing/test/unit/test_httpauth.js
build/macosx/universal/fix-buildconfig
config/optimizejars.py
content/base/crashtests/369216-1.html
content/base/test/badEventFieldName.eventsource
content/base/test/badEventFieldName.eventsource^headers^
content/svg/content/src/nsSVGMaskElement.cpp
content/svg/content/src/nsSVGMaskElement.h
content/xbl/test/test_bug296375.xul
editor/libeditor/html/tests/test_bug832025_2.html
gfx/angle/LICENSE.preprocessor
gfx/angle/angle-1317.patch
gfx/angle/angle-abort-on-oom-in-preprocessor.patch
gfx/angle/angle-enforce-readpixels-spec.patch
gfx/angle/angle-impl-read-bgra.patch
gfx/angle/angle-long-identifier-hash-spooky.patch
gfx/angle/angle-renaming-debug.patch
gfx/angle/angle-renaming-preprocessor-diagonostics.patch
gfx/angle/angle-renaming-preprocessor-directivehandler.patch
gfx/angle/angle-stdcall-alias.patch
gfx/angle/samples/build_samples.gyp
gfx/angle/samples/gles2_book/Common/Win32/esUtil_TGA.c
gfx/angle/samples/gles2_book/Common/Win32/esUtil_win32.c
gfx/angle/samples/gles2_book/Common/esShader.c
gfx/angle/samples/gles2_book/Common/esShapes.c
gfx/angle/samples/gles2_book/Common/esTransform.c
gfx/angle/samples/gles2_book/Common/esUtil.c
gfx/angle/samples/gles2_book/Common/esUtil.h
gfx/angle/samples/gles2_book/Common/esUtil.vcproj
gfx/angle/samples/gles2_book/Common/esUtil_win.h
gfx/angle/samples/gles2_book/Hello_Triangle/Hello_Triangle.c
gfx/angle/samples/gles2_book/Hello_Triangle/Hello_Triangle.vcproj
gfx/angle/samples/gles2_book/MipMap2D/MipMap2D.c
gfx/angle/samples/gles2_book/MipMap2D/MipMap2D.vcproj
gfx/angle/samples/gles2_book/MultiTexture/MultiTexture.c
gfx/angle/samples/gles2_book/MultiTexture/MultiTexture.vcproj
gfx/angle/samples/gles2_book/MultiTexture/basemap.tga
gfx/angle/samples/gles2_book/MultiTexture/lightmap.tga
gfx/angle/samples/gles2_book/ParticleSystem/ParticleSystem.c
gfx/angle/samples/gles2_book/ParticleSystem/ParticleSystem.vcproj
gfx/angle/samples/gles2_book/ParticleSystem/smoke.tga
gfx/angle/samples/gles2_book/PostSubBuffer/PostSubBuffer.c
gfx/angle/samples/gles2_book/PostSubBuffer/PostSubBuffer.vcproj
gfx/angle/samples/gles2_book/Simple_Texture2D/Simple_Texture2D.c
gfx/angle/samples/gles2_book/Simple_Texture2D/Simple_Texture2D.vcproj
gfx/angle/samples/gles2_book/Simple_TextureCubemap/Simple_TextureCubemap.c
gfx/angle/samples/gles2_book/Simple_TextureCubemap/Simple_TextureCubemap.vcproj
gfx/angle/samples/gles2_book/Simple_VertexShader/Simple_VertexShader.c
gfx/angle/samples/gles2_book/Simple_VertexShader/Simple_VertexShader.vcproj
gfx/angle/samples/gles2_book/Stencil_Test/Stencil_Test.c
gfx/angle/samples/gles2_book/Stencil_Test/Stencil_Test.vcproj
gfx/angle/samples/gles2_book/TextureWrap/TextureWrap.c
gfx/angle/samples/gles2_book/TextureWrap/TextureWrap.vcproj
gfx/angle/samples/samples.sln
gfx/angle/samples/translator/essl_to_glsl.vcproj
gfx/angle/samples/translator/essl_to_hlsl.vcproj
gfx/angle/samples/translator/translator.cpp
gfx/angle/src/compiler/compilerdebug.cpp
gfx/angle/src/compiler/compilerdebug.h
gfx/angle/src/compiler/preprocessor/atom.c
gfx/angle/src/compiler/preprocessor/atom.h
gfx/angle/src/compiler/preprocessor/compile.h
gfx/angle/src/compiler/preprocessor/cpp.c
gfx/angle/src/compiler/preprocessor/cpp.h
gfx/angle/src/compiler/preprocessor/cppstruct.c
gfx/angle/src/compiler/preprocessor/memory.c
gfx/angle/src/compiler/preprocessor/memory.h
gfx/angle/src/compiler/preprocessor/new/Diagnostics.h
gfx/angle/src/compiler/preprocessor/new/DirectiveHandler.h
gfx/angle/src/compiler/preprocessor/new/DirectiveParser.cpp
gfx/angle/src/compiler/preprocessor/new/DirectiveParser.h
gfx/angle/src/compiler/preprocessor/new/ExpressionParser.cpp
gfx/angle/src/compiler/preprocessor/new/ExpressionParser.h
gfx/angle/src/compiler/preprocessor/new/ExpressionParser.y
gfx/angle/src/compiler/preprocessor/new/Input.cpp
gfx/angle/src/compiler/preprocessor/new/Input.h
gfx/angle/src/compiler/preprocessor/new/Lexer.cpp
gfx/angle/src/compiler/preprocessor/new/Lexer.h
gfx/angle/src/compiler/preprocessor/new/Macro.cpp
gfx/angle/src/compiler/preprocessor/new/Macro.h
gfx/angle/src/compiler/preprocessor/new/MacroExpander.cpp
gfx/angle/src/compiler/preprocessor/new/MacroExpander.h
gfx/angle/src/compiler/preprocessor/new/Preprocessor.cpp
gfx/angle/src/compiler/preprocessor/new/Preprocessor.h
gfx/angle/src/compiler/preprocessor/new/PreprocessorDiagnostics.cpp
gfx/angle/src/compiler/preprocessor/new/PreprocessorDirectiveHandler.cpp
gfx/angle/src/compiler/preprocessor/new/SourceLocation.h
gfx/angle/src/compiler/preprocessor/new/Token.cpp
gfx/angle/src/compiler/preprocessor/new/Token.h
gfx/angle/src/compiler/preprocessor/new/Tokenizer.cpp
gfx/angle/src/compiler/preprocessor/new/Tokenizer.h
gfx/angle/src/compiler/preprocessor/new/Tokenizer.l
gfx/angle/src/compiler/preprocessor/new/generate_parser.sh
gfx/angle/src/compiler/preprocessor/new/numeric_lex.h
gfx/angle/src/compiler/preprocessor/new/pp_utils.h
gfx/angle/src/compiler/preprocessor/new/preprocessor.vcproj
gfx/angle/src/compiler/preprocessor/parser.h
gfx/angle/src/compiler/preprocessor/preprocess.h
gfx/angle/src/compiler/preprocessor/scanner.c
gfx/angle/src/compiler/preprocessor/scanner.h
gfx/angle/src/compiler/preprocessor/slglobals.h
gfx/angle/src/compiler/preprocessor/symbols.c
gfx/angle/src/compiler/preprocessor/symbols.h
gfx/angle/src/compiler/preprocessor/tokens.c
gfx/angle/src/compiler/preprocessor/tokens.h
gfx/angle/src/compiler/translator_common.vcproj
gfx/angle/src/compiler/translator_hlsl.vcproj
gfx/angle/src/libEGL/libEGL.vcproj
gfx/angle/src/libGLESv2/libGLESv2.vcproj
gfx/angle/tests/build_tests.gyp
gfx/angle/tests/compiler_tests/VariablePacker_test.cpp
gfx/angle/tests/preprocessor_tests/MockDiagnostics.h
gfx/angle/tests/preprocessor_tests/MockDirectiveHandler.h
gfx/angle/tests/preprocessor_tests/PreprocessorTest.cpp
gfx/angle/tests/preprocessor_tests/PreprocessorTest.h
gfx/angle/tests/preprocessor_tests/char_test.cpp
gfx/angle/tests/preprocessor_tests/comment_test.cpp
gfx/angle/tests/preprocessor_tests/define_test.cpp
gfx/angle/tests/preprocessor_tests/error_test.cpp
gfx/angle/tests/preprocessor_tests/extension_test.cpp
gfx/angle/tests/preprocessor_tests/identifier_test.cpp
gfx/angle/tests/preprocessor_tests/if_test.cpp
gfx/angle/tests/preprocessor_tests/input_test.cpp
gfx/angle/tests/preprocessor_tests/location_test.cpp
gfx/angle/tests/preprocessor_tests/number_test.cpp
gfx/angle/tests/preprocessor_tests/operator_test.cpp
gfx/angle/tests/preprocessor_tests/pragma_test.cpp
gfx/angle/tests/preprocessor_tests/space_test.cpp
gfx/angle/tests/preprocessor_tests/token_test.cpp
gfx/angle/tests/preprocessor_tests/version_test.cpp
gfx/thebes/woff-private.h
gfx/thebes/woff.c
gfx/thebes/woff.h
js/src/Makefile.in
js/src/ion/Ion.cpp
js/src/ion/IonCaches.cpp
js/src/ion/IonMacroAssembler.h
js/src/ion/VMFunctions.cpp
js/src/jsapi.h
js/src/jscompartment.cpp
js/src/jsinterp.cpp
js/src/jsscript.cpp
js/src/vm/ArgumentsObject.cpp
js/src/vm/ScopeObject.cpp
mobile/android/base/resources/drawable/desktop.png
mobile/android/base/resources/drawable/mobile.png
toolkit/components/places/tests/unit/test_399606.js
toolkit/mozapps/installer/Packager.pm
toolkit/mozapps/installer/link-manifests.py
toolkit/mozapps/installer/os2/sign.cmd
toolkit/mozapps/installer/os2/strip.cmd
toolkit/mozapps/installer/packager-deps.py
toolkit/mozapps/installer/pkgcp.pl
toolkit/mozapps/installer/windows/nsis/version.nsh
toolkit/mozapps/installer/xptlink.pl
--- a/b2g/chrome/content/forms.js
+++ b/b2g/chrome/content/forms.js
@@ -183,17 +183,16 @@ let FormAssistant = {
     addEventListener("focus", this, true, false);
     addEventListener("blur", this, true, false);
     addEventListener("resize", this, true, false);
     addEventListener("submit", this, true, false);
     addEventListener("pagehide", this, true, false);
     addMessageListener("Forms:Select:Choice", this);
     addMessageListener("Forms:Input:Value", this);
     addMessageListener("Forms:Select:Blur", this);
-    Services.obs.addObserver(this, "xpcom-shutdown", false);
   },
 
   ignoredInputTypes: new Set([
     'button', 'file', 'checkbox', 'radio', 'reset', 'submit', 'image'
   ]),
 
   isKeyboardOpened: false,
   selectionStart: 0,
@@ -340,22 +339,16 @@ let FormAssistant = {
 
       case "Forms:Select:Blur": {
         this.setFocusedElement(null);
         break;
       }
     }
   },
 
-  observe: function fa_observe(subject, topic, data) {
-    Services.obs.removeObserver(this, "xpcom-shutdown");
-    removeMessageListener("Forms:Select:Choice", this);
-    removeMessageListener("Forms:Input:Value", this);
-  },
-
   showKeyboard: function fa_showKeyboard(target) {
     if (this.isKeyboardOpened)
       return;
 
     if (target instanceof HTMLOptionElement)
       target = target.parentNode;
 
     let kbOpened = this.sendKeyboardState(target);
--- a/b2g/components/SignInToWebsite.jsm
+++ b/b2g/components/SignInToWebsite.jsm
@@ -175,17 +175,18 @@ let Pipe = {
     }
 
     function identityDelegateFinished() {
       removeMessageListeners();
 
       let detail = {
         type: kReceivedIdentityAssertion,
         showUI: aGaiaOptions.showUI || false,
-        id: kReceivedIdentityAssertion + "-" + uuid
+        id: kReceivedIdentityAssertion + "-" + uuid,
+        requestId: aRpOptions.id 
       };
       log('telling gaia to close the dialog');
       // tell gaia to close the dialog
       GaiaInterface.sendChromeEvent(detail);
     }
 
     content.addEventListener("mozContentEvent", function getAssertion(evt) {
       let msg = evt.detail;
@@ -243,17 +244,18 @@ let Pipe = {
 
     // Tell gaia to open the identity iframe or trusty popup. The parameter
     // showUI signals whether user interaction is needed.  If it is, gaia will
     // open a dialog; if not, a hidden iframe.  In each case, BrowserID is
     // available in the context.
     let detail = {
       type: kOpenIdentityDialog,
       showUI: aGaiaOptions.showUI || false,
-      id: kOpenIdentityDialog + "-" + uuid
+      id: kOpenIdentityDialog + "-" + uuid,
+      requestId: aRpOptions.id
     };
 
     GaiaInterface.sendChromeEvent(detail);
   }
 
 };
 
 /*
--- a/b2g/config/mozconfigs/ics_armv7a_gecko/debug
+++ b/b2g/config/mozconfigs/ics_armv7a_gecko/debug
@@ -1,8 +1,10 @@
+. "$topsrcdir/b2g/config/mozconfigs/common"
+
 mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-b2g
 
 ac_add_options --enable-application=b2g
 ac_add_options --enable-b2g-camera
 
 ac_add_options --target=arm-linux-androideabi
 ac_add_options --with-gonk="$topsrcdir/gonk-toolchain"
 export TOOLCHAIN_HOST=linux-x86
--- a/b2g/config/panda/releng-pandaboard.tt
+++ b/b2g/config/panda/releng-pandaboard.tt
@@ -1,8 +1,8 @@
 [
 {
-"size": 676664796,
-"digest": "d50e0a916bb54c12f55961b3155c1bb754e46fccd0cc965f6c37c2142a7080e58dc3980b7f7f1249b078a21553d6ae2edfefce18213cf8c8fc3f593db27bb0e6",
+"size": 676768064,
+"digest": "d51092cc586934f21b84e62447f49d32548cd3f222a092f2b7b8c04ff59231c97969140e0b1d897c22bcb12b8bf6a9c8b24e55891896c7eeb9bc0f190980813d",
 "algorithm": "sha512",
 "filename": "gonk.tar.xz"
 }
 ]
--- a/b2g/config/panda/sources.xml
+++ b/b2g/config/panda/sources.xml
@@ -5,36 +5,36 @@
   <remote fetch="https://android.googlesource.com/" name="aosp"/>
   <remote fetch="git://github.com/mozilla-b2g/" name="b2g"/>
   <remote fetch="git://android.git.linaro.org/" name="linaro"/>
   <remote fetch="git://github.com/mozilla/" name="mozilla"/>
   <remote fetch="http://git.mozilla.org/" name="mozillaorg"/>
   <default remote="linaro" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
 
   <!-- Gonk specific things and forks -->
-  <project name="platform_build" path="build" remote="b2g" revision="69380784721ae11af93ef4e1b5ce8a54e8570574">
+  <project name="platform_build" path="build" remote="b2g" revision="0784cdcb29ae45e5bf903cc03fa1bc206162665b">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="22629c7597d0d658fb472f76f2bf35e7eaaf98fd"/>
-  <project name="rilproxy" path="rilproxy" remote="b2g" revision="167b4c59a82b9130e385de786e8056d89a1cb8c3"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2f8c7d3546bda16b02a61b422786919875c19f15"/>
+  <project name="rilproxy" path="rilproxy" remote="b2g" revision="2d380d27c86263537f6b829cd0238f5dd702c735"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="6ee1f8987ef36d688f97064c003ad57849dfadf2"/>
 
   <!-- Stock Android things -->
   <!-- Information: platform/abi/cpp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <!-- Information: platform/bionic is tagged with android-4.0.4_r2.1 --><project name="platform/bionic" path="bionic" revision="3d11bf0f3f3cf848f6f1e8449bf8736d8d1c78a3"/>
   <!-- Information: platform/bootable/recovery is tagged with android-4.0.4_r2.1 --><project name="platform/bootable/recovery" path="bootable/recovery" revision="fadc5ac81d6400ebdd041f7d4ea64021596d6b7d"/>
   <!-- Information: device/common is tagged with android-sdk-adt_r20 --><project name="device/common" path="device/common" revision="7d4526582f88808a3194e1a3b304abb369d2745c"/>
   <!-- Information: device/sample is tagged with android-4.0.4_r2.1 --><project name="device/sample" path="device/sample" revision="ef228b8b377a9663e94be4b1aeb6c2bf7a07d098"/>
   <!-- Information: platform/external/apache-http is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/apache-http" path="external/apache-http" revision="6c9d8c58d3ed710f87c26820d903bb8aad81754f"/>
   <!-- Information: platform/external/bluetooth/bluez is tagged with android-4.0.4_r2.1 --><project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="966afbd88f0bfc325bf80274ad2723c238883fa1"/>
   <!-- Information: platform/external/bluetooth/glib is tagged with android-4.1.1_r6.1 --><project name="platform/external/bluetooth/glib" path="external/bluetooth/glib" revision="1143b9918eab068401b604eb11c3f651f4e38b25"/>
   <!-- Information: platform/external/bluetooth/hcidump is tagged with android-4.1.1_r6.1 --><project name="platform/external/bluetooth/hcidump" path="external/bluetooth/hcidump" revision="7322661808c2006b7848e79e6bb72b37fbcf6710"/>
   <!-- Information: platform/external/bsdiff is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.041 --><project name="platform/external/bsdiff" path="external/bsdiff" revision="81872540236d9bb15cccf963d05b9de48baa5375"/>
-  <project name="platform/external/busybox" path="external/busybox" remote="linaro" revision="4fa5717aed10a5617c52def848dbd42ec7eca35b"/>
+  <project name="platform/external/busybox" path="external/busybox" remote="linaro" revision="0a41b083cdaf2ddd602fb5c955329494da25f2b9"/>
   <!-- Information: platform/external/bzip2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/bzip2" path="external/bzip2" revision="048dacdca43eed1534689ececcf2781c63e1e4ba"/>
   <!-- Information: platform/external/dbus is tagged with android-4.1.1_r6.1 --><project name="platform/external/dbus" path="external/dbus" revision="537eaff5de9aace3348436166d4cde7adc1e488e"/>
   <!-- Information: platform/external/dhcpcd is tagged with android-sdk-adt_r20 --><project name="platform/external/dhcpcd" path="external/dhcpcd" revision="ddaa48f57b54b2862b3e6dcf18a44c9647f3baaa"/>
   <!-- Information: platform/external/dnsmasq is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.041 --><project name="platform/external/dnsmasq" path="external/dnsmasq" revision="f621afad94df46204c25fc2593a19d704d2637f5"/>
   <!-- Information: platform/external/expat is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/expat" path="external/expat" revision="6df134250feab71edb5915ecaa6268210bca76c5"/>
   <!-- Information: platform/external/fdlibm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/fdlibm" path="external/fdlibm" revision="988ffeb12a6e044ae3504838ef1fee3fe0716934"/>
   <!-- Information: platform/external/flac is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/flac" path="external/flac" revision="5893fbe890f5dab8e4146d2baa4bd2691c0739e0"/>
   <!-- Information: platform/external/freetype is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/freetype" path="external/freetype" revision="aeb407daf3711a10a27f3bc2223c5eb05158076e"/>
@@ -89,11 +89,11 @@
   <!-- Information: platform/system/netd is tagged with android-4.0.4_r2.1 --><project name="platform/system/netd" path="system/netd" revision="3c903b555975fa59d6688a0a6417ac7512c202e7"/>
   <!-- Information: platform/system/vold is tagged with android-4.0.4_r2.1 --><project name="platform/system/vold" path="system/vold" revision="3ad9072a5d6f6bda32123b367545649364e3c11d"/>
 
   <!-- Pandaboard specific things -->
   <project name="android-device-panda" path="device/ti/panda" remote="b2g" revision="0e9a89187970d6fa99930c8d9cb438b935c2337c"/>
   <!-- Information: platform/hardware/ti/omap4xxx is tagged with android-4.0.4_r2.1 --><project name="platform/hardware/ti/omap4xxx" path="hardware/ti/omap4xxx" revision="8be8e9a68c96b6cf43c08a58e7ecd7708737c599"/>
   <project name="platform/hardware/ti/wlan" path="hardware/ti/wlan" revision="60dfeb6e4448bfed707946ebca6612980f525e69"/>
   <project name="platform/hardware/ti/wpan" path="hardware/ti/wpan" revision="3ece7d9e08052989401e008bc397dbcd2557cfd0"/>
-  <project name="Negatus" path="external/negatus" remote="mozilla" revision="7ed4efcee655f605455f9375a4e6c68cd078c459"/>
+  <project name="Negatus" path="external/negatus" remote="mozilla" revision="151697c638a6781e83c76d4a01fb001aabde4e79"/>
   
 </manifest>
\ No newline at end of file
--- a/b2g/config/unagi/releng-unagi.tt
+++ b/b2g/config/unagi/releng-unagi.tt
@@ -1,12 +1,12 @@
 [
 {
-"size": 832341968,
-"digest": "e2d099677f5f930db0b2c03eaf3812a53f02be0773bdb3bad89b1c387041e920c17b90f041bf70d9c2ab715dd4556503534483d4ebc28fb035f41cd5ebba6a52",
+"size": 832272360,
+"digest": "bb7369106d32a184c61fc8c6658c4d1c64dd778e432a3dd39592b99a92ed0a8f4a9fede60399ec2c85ddaf077f27d77613848b253f0ac155383b23955446396f",
 "algorithm": "sha512",
 "filename": "gonk.tar.xz"
 },
 {
 "size": 8622080,
 "digest": "7a2bbf0c76f7b7d5e4b89f758f69b5d8bcf08ec579374877de8939ad69883ab8cd842f04fdaa03a4ef9cdf8170f242e0381dd437e969d5212ead6cdd6f79ab50",
 "algorithm": "sha512",
 "filename": "boot.img"
--- a/b2g/config/unagi/sources.xml
+++ b/b2g/config/unagi/sources.xml
@@ -1,109 +1,109 @@
 <?xml version="1.0" ?><manifest>
   <!-- This is only a record of which revisions were pulled to generate the
        gonk.tar.xz snapshot referred to by releng-unagi.tt -->
 
   <remote fetch="https://android.googlesource.com/" name="aosp"/>
   <remote fetch="git://github.com/mozilla-b2g/" name="b2g"/>
   <remote fetch="git://github.com/mozilla/" name="mozilla"/>
+  <remote fetch="http://git.mozilla.org/" name="mozillaorg"/>
   <remote fetch="git://codeaurora.org/" name="caf"/>
   <remote fetch="git://android.git.linaro.org/" name="linaro"/>
-  <remote fetch="https://git.mozilla.org" name="mozillaorg"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
 
   <!-- Gonk specific things and forks -->
-  <project name="platform_build" path="build" remote="b2g" revision="273ba23d5c6c9f6a34995a3cc429804d1449ca9f">
+  <project name="platform_build" path="build" remote="b2g" revision="0784cdcb29ae45e5bf903cc03fa1bc206162665b">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="654358494ba601a46ef9838debc95417ae464cc6"/>
-  <project name="rilproxy" path="rilproxy" remote="b2g" revision="32106d4ea635ebe17a1610b643b398db639b8b97"/>
-  <project name="librecovery" path="librecovery" remote="b2g" revision="e1bd90051c9e937221eb1f91c94e3cde747311a7"/>
+  <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2f8c7d3546bda16b02a61b422786919875c19f15"/>
+  <project name="rilproxy" path="rilproxy" remote="b2g" revision="2d380d27c86263537f6b829cd0238f5dd702c735"/>
+  <project name="librecovery" path="librecovery" remote="b2g" revision="601fc18b28c9d7cf6954b281ddd3b705c74a9215"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="6ee1f8987ef36d688f97064c003ad57849dfadf2"/>
 
   <!-- Stock Android things -->
-  <!-- Information: platform/abi/cpp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
+  <!-- Information: platform/abi/cpp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <!-- Information: platform/bionic is tagged with M8960AAAAANLYA100715A --><project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
   <!-- Information: platform/bootable/recovery is tagged with M8960AAAAANLYA100715A --><project name="platform/bootable/recovery" path="bootable/recovery" revision="e0a9ac010df3afaa47ba107192c05ac8b5516435"/>
   <!-- Information: platform/development is tagged with M8960AAAAANLYA100715A --><project name="platform/development" path="development" revision="a384622f5fcb1d2bebb9102591ff7ae91fe8ed2d"/>
   <!-- Information: device/common is tagged with M8960AAAAANLYA1005304 --><project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
   <!-- Information: device/sample is tagged with M8960AAAAANLYA100715A --><project name="device/sample" path="device/sample" revision="c328f3d4409db801628861baa8d279fb8855892f"/>
-  <!-- Information: platform/external/apache-http is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/apache-http" path="external/apache-http" revision="6c9d8c58d3ed710f87c26820d903bb8aad81754f"/>
+  <!-- Information: platform/external/apache-http is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/apache-http" path="external/apache-http" revision="6c9d8c58d3ed710f87c26820d903bb8aad81754f"/>
   <!-- Information: platform/external/bluetooth/bluez is tagged with M76XXUSNEKNLYA2040 --><project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="1023c91c66e9c3bd1132480051993bf7827770f6"/>
-  <!-- Information: platform/external/bluetooth/glib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/bluetooth/glib" path="external/bluetooth/glib" revision="c6b49241cc1a8950723a5f74f8f4b4f4c3fa970e"/>
+  <!-- Information: platform/external/bluetooth/glib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/bluetooth/glib" path="external/bluetooth/glib" revision="c6b49241cc1a8950723a5f74f8f4b4f4c3fa970e"/>
   <!-- Information: platform/external/bluetooth/hcidump is tagged with M8960AAAAANLYA1005304 --><project name="platform/external/bluetooth/hcidump" path="external/bluetooth/hcidump" revision="02b1eb24fbb3d0135a81edb4a2175b1397308d7d"/>
-  <!-- Information: platform/external/bsdiff is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.041 --><project name="platform/external/bsdiff" path="external/bsdiff" revision="81872540236d9bb15cccf963d05b9de48baa5375"/>
-  <!-- Information: platform/external/bzip2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/bzip2" path="external/bzip2" revision="048dacdca43eed1534689ececcf2781c63e1e4ba"/>
+  <!-- Information: platform/external/bsdiff is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.081 --><project name="platform/external/bsdiff" path="external/bsdiff" revision="81872540236d9bb15cccf963d05b9de48baa5375"/>
+  <!-- Information: platform/external/bzip2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/bzip2" path="external/bzip2" revision="048dacdca43eed1534689ececcf2781c63e1e4ba"/>
   <!-- Information: platform/external/dbus is tagged with M8960AAAAANLYA100715A --><project name="platform/external/dbus" path="external/dbus" revision="c7517b6195dc6926728352113e6cc335da3f9c9e"/>
   <!-- Information: platform/external/dhcpcd is tagged with M8960AAAAANLYA100715A --><project name="platform/external/dhcpcd" path="external/dhcpcd" revision="1e00fb67022d0921af0fead263f81762781b9ffa"/>
-  <!-- Information: platform/external/dnsmasq is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.041 --><project name="platform/external/dnsmasq" path="external/dnsmasq" revision="f621afad94df46204c25fc2593a19d704d2637f5"/>
-  <!-- Information: platform/external/e2fsprogs is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/e2fsprogs" path="external/e2fsprogs" revision="d5f550bb2f556c5d287f7c8d2b77223654bcec37"/>
-  <!-- Information: platform/external/expat is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/expat" path="external/expat" revision="6df134250feab71edb5915ecaa6268210bca76c5"/>
-  <!-- Information: platform/external/fdlibm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/fdlibm" path="external/fdlibm" revision="988ffeb12a6e044ae3504838ef1fee3fe0716934"/>
-  <!-- Information: platform/external/flac is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/flac" path="external/flac" revision="5893fbe890f5dab8e4146d2baa4bd2691c0739e0"/>
-  <!-- Information: platform/external/freetype is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/freetype" path="external/freetype" revision="aeb407daf3711a10a27f3bc2223c5eb05158076e"/>
-  <!-- Information: platform/external/giflib is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.041 --><project name="platform/external/giflib" path="external/giflib" revision="b2597268aef084202a8c349d1cc072c03c6e22eb"/>
-  <!-- Information: platform/external/gtest is tagged with android-4.2.1_r1 --><project name="platform/external/gtest" path="external/gtest" remote="linaro" revision="344e5f3db17615cc853073a02968a603efd39109"/>
-  <!-- Information: platform/external/harfbuzz is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/harfbuzz" path="external/harfbuzz" revision="116610d63a859521dacf00fb6818ee9ab2e666f6"/>
-  <!-- Information: platform/external/icu4c is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/icu4c" path="external/icu4c" revision="0fa67b93b831c6636ca18b152a1b1b14cc99b034"/>
-  <!-- Information: platform/external/iptables is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/iptables" path="external/iptables" revision="3b2deb17f065c5664bb25e1a28489e5792eb63ff"/>
-  <!-- Information: platform/external/jhead is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/jhead" path="external/jhead" revision="754078052c687f6721536009c816644c73e4f145"/>
+  <!-- Information: platform/external/dnsmasq is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.081 --><project name="platform/external/dnsmasq" path="external/dnsmasq" revision="f621afad94df46204c25fc2593a19d704d2637f5"/>
+  <!-- Information: platform/external/e2fsprogs is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/e2fsprogs" path="external/e2fsprogs" revision="d5f550bb2f556c5d287f7c8d2b77223654bcec37"/>
+  <!-- Information: platform/external/expat is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/expat" path="external/expat" revision="6df134250feab71edb5915ecaa6268210bca76c5"/>
+  <!-- Information: platform/external/fdlibm is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/fdlibm" path="external/fdlibm" revision="988ffeb12a6e044ae3504838ef1fee3fe0716934"/>
+  <!-- Information: platform/external/flac is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/flac" path="external/flac" revision="5893fbe890f5dab8e4146d2baa4bd2691c0739e0"/>
+  <!-- Information: platform/external/freetype is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/freetype" path="external/freetype" revision="aeb407daf3711a10a27f3bc2223c5eb05158076e"/>
+  <!-- Information: platform/external/giflib is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.081 --><project name="platform/external/giflib" path="external/giflib" revision="b2597268aef084202a8c349d1cc072c03c6e22eb"/>
+  <!-- Information: platform/external/gtest is tagged with android-sdk-support_r11 --><project name="platform/external/gtest" path="external/gtest" remote="linaro" revision="344e5f3db17615cc853073a02968a603efd39109"/>
+  <!-- Information: platform/external/harfbuzz is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/harfbuzz" path="external/harfbuzz" revision="116610d63a859521dacf00fb6818ee9ab2e666f6"/>
+  <!-- Information: platform/external/icu4c is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/icu4c" path="external/icu4c" revision="0fa67b93b831c6636ca18b152a1b1b14cc99b034"/>
+  <!-- Information: platform/external/iptables is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/iptables" path="external/iptables" revision="3b2deb17f065c5664bb25e1a28489e5792eb63ff"/>
+  <!-- Information: platform/external/jhead is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/jhead" path="external/jhead" revision="754078052c687f6721536009c816644c73e4f145"/>
   <!-- Information: platform/external/jpeg is tagged with M8960AAAAANLYA1005304 --><project name="platform/external/jpeg" path="external/jpeg" revision="a62e464d672a4623233180e4023034bf825f066e"/>
-  <!-- Information: platform/external/libgsm is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.041 --><project name="platform/external/libgsm" path="external/libgsm" revision="5e4516958690b9a1b2c98f88eeecba3edd2dbda4"/>
-  <!-- Information: platform/external/liblzf is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.041 --><project name="platform/external/liblzf" path="external/liblzf" revision="6946aa575b0949d045722794850896099d937cbb"/>
-  <!-- Information: platform/external/libnfc-nxp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/libnfc-nxp" path="external/libnfc-nxp" revision="3a912b065a31a72c63ad56ac224cfeaa933423b6"/>
-  <!-- Information: platform/external/libnl-headers is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.041 --><project name="platform/external/libnl-headers" path="external/libnl-headers" revision="6ccf7349d61f73ac26a0675d735d903ab919c658"/>
-  <!-- Information: platform/external/libphonenumber is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/libphonenumber" path="external/libphonenumber" revision="8d22c9a05eda1935c6dc27d188158e6ee38dc016"/>
+  <!-- Information: platform/external/libgsm is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.081 --><project name="platform/external/libgsm" path="external/libgsm" revision="5e4516958690b9a1b2c98f88eeecba3edd2dbda4"/>
+  <!-- Information: platform/external/liblzf is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.081 --><project name="platform/external/liblzf" path="external/liblzf" revision="6946aa575b0949d045722794850896099d937cbb"/>
+  <!-- Information: platform/external/libnfc-nxp is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/libnfc-nxp" path="external/libnfc-nxp" revision="3a912b065a31a72c63ad56ac224cfeaa933423b6"/>
+  <!-- Information: platform/external/libnl-headers is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.081 --><project name="platform/external/libnl-headers" path="external/libnl-headers" revision="6ccf7349d61f73ac26a0675d735d903ab919c658"/>
+  <!-- Information: platform/external/libphonenumber is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/libphonenumber" path="external/libphonenumber" revision="8d22c9a05eda1935c6dc27d188158e6ee38dc016"/>
   <!-- Information: platform/external/libpng is tagged with M8960AAAAANLYA100715A --><project name="platform/external/libpng" path="external/libpng" revision="9c3730f0efa69f580f03463c237cd928f3196404"/>
-  <!-- Information: platform/external/libvpx is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/libvpx" path="external/libvpx" revision="3a40da0d96da5c520e7707aa14f48a80956e20d7"/>
+  <!-- Information: platform/external/libvpx is tagged with M8960AAAAANLYA1519349 --><project name="platform/external/libvpx" path="external/libvpx" revision="3a40da0d96da5c520e7707aa14f48a80956e20d7"/>
   <!-- Information: platform/external/llvm is tagged with M8960AAAAANLYA1005304 --><project name="platform/external/llvm" path="external/llvm" revision="bff5923831940309f7d8ddbff5826ca6ed2dc050"/>
   <!-- Information: platform/external/mksh is tagged with M8960AAAAANLYA1005304 --><project name="platform/external/mksh" path="external/mksh" revision="ec646e8f5e7dac9a77d1de549c6ed92c04d0cd4b"/>
   <project name="platform_external_opensans" path="external/opensans" remote="b2g" revision="b5b4c226ca1d71e936153cf679dda6d3d60e2354"/>
   <!-- Information: platform/external/openssl is tagged with AU_LINUX_ANDROID_ICS.04.00.04.00.110 --><project name="platform/external/openssl" path="external/openssl" revision="27d333cce9a31c806b4bfa042925f045c727aecd"/>
-  <!-- Information: platform/external/protobuf is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.041 --><project name="platform/external/protobuf" path="external/protobuf" revision="e217977611c52bccde7f7c78e1d3c790c6357431"/>
-  <!-- Information: platform/external/safe-iop is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.041 --><project name="platform/external/safe-iop" path="external/safe-iop" revision="07073634e2e3aa4f518e36ed5dec3aabc549d5fb"/>
+  <!-- Information: platform/external/protobuf is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.081 --><project name="platform/external/protobuf" path="external/protobuf" revision="e217977611c52bccde7f7c78e1d3c790c6357431"/>
+  <!-- Information: platform/external/safe-iop is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.081 --><project name="platform/external/safe-iop" path="external/safe-iop" revision="07073634e2e3aa4f518e36ed5dec3aabc549d5fb"/>
   <project name="screencap-gonk" path="external/screencap-gonk" remote="b2g" revision="e6403c71e9eca8cb943739d5a0a192deac60fc51"/>
   <!-- Information: platform/external/skia is tagged with M8960AAAAANLYA100715A --><project name="platform/external/skia" path="external/skia" revision="7d90c85f2c0e3b747f7c7eff8bc9253b0063b439"/>
   <!-- Information: platform/external/sonivox is tagged with M8960AAAAANLYA1005304 --><project name="platform/external/sonivox" path="external/sonivox" revision="7c967779dfc61ac1f346e972de91d4bfce7dccbb"/>
-  <!-- Information: platform/external/speex is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.041 --><project name="platform/external/speex" path="external/speex" revision="ebe6230a7f7c69f5a4389f2b09b7b19ef9e94f32"/>
+  <!-- Information: platform/external/speex is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.081 --><project name="platform/external/speex" path="external/speex" revision="ebe6230a7f7c69f5a4389f2b09b7b19ef9e94f32"/>
   <project name="platform/external/sqlite" path="external/sqlite" revision="fb30e613139b8836fdc8e81e166cf3a76e5fa17f"/>
-  <!-- Information: platform/external/stlport is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/stlport" path="external/stlport" revision="a6734e0645fce81c9610de0488b729207bfa576e"/>
-  <!-- Information: platform/external/strace is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/strace" path="external/strace" revision="c9fd2e5ef7d002e12e7cf2512506c84a9414b0fd"/>
-  <!-- Information: platform/external/tagsoup is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.041 --><project name="platform/external/tagsoup" path="external/tagsoup" revision="68c2ec9e0acdb3214b7fb91dbab8c9fab8736817"/>
+  <!-- Information: platform/external/stlport is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/stlport" path="external/stlport" revision="a6734e0645fce81c9610de0488b729207bfa576e"/>
+  <!-- Information: platform/external/strace is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/strace" path="external/strace" revision="c9fd2e5ef7d002e12e7cf2512506c84a9414b0fd"/>
+  <!-- Information: platform/external/tagsoup is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.081 --><project name="platform/external/tagsoup" path="external/tagsoup" revision="68c2ec9e0acdb3214b7fb91dbab8c9fab8736817"/>
   <!-- Information: platform/external/tinyalsa is tagged with M8960AAAAANLYA1005304 --><project name="platform/external/tinyalsa" path="external/tinyalsa" revision="06cc244ee512c1352215e543615738bc8ac82814"/>
-  <!-- Information: platform/external/tremolo is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.041 --><project name="platform/external/tremolo" path="external/tremolo" revision="25bd78d2392dbdc879ae53382cde9d019f79cf6f"/>
+  <!-- Information: platform/external/tremolo is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.081 --><project name="platform/external/tremolo" path="external/tremolo" revision="25bd78d2392dbdc879ae53382cde9d019f79cf6f"/>
   <project name="unbootimg" path="external/unbootimg" remote="b2g" revision="9464623d92eb8668544916dc5a8f4f6337d0bc08"/>
-  <!-- Information: platform/external/webp is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.041 --><project name="platform/external/webp" path="external/webp" revision="88fe2b83c4b9232cd08729556fd0485d6a6a92cd"/>
-  <!-- Information: platform/external/webrtc is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/webrtc" path="external/webrtc" revision="137024dc8a2e9251a471e20518a9c3ae06f81f23"/>
-  <!-- Information: platform/external/wpa_supplicant is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/wpa_supplicant" path="external/wpa_supplicant" revision="a01d37870bbf9892d43e792e5de0683ca41c5497"/>
+  <!-- Information: platform/external/webp is tagged with AU_LINUX_ANDROID_JB_REL_2.0.3.04.01.02.21.081 --><project name="platform/external/webp" path="external/webp" revision="88fe2b83c4b9232cd08729556fd0485d6a6a92cd"/>
+  <!-- Information: platform/external/webrtc is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/webrtc" path="external/webrtc" revision="137024dc8a2e9251a471e20518a9c3ae06f81f23"/>
+  <!-- Information: platform/external/wpa_supplicant is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/wpa_supplicant" path="external/wpa_supplicant" revision="a01d37870bbf9892d43e792e5de0683ca41c5497"/>
   <!-- Information: platform/external/hostap is tagged with M8960AAAAANLYA1047 --><project name="platform/external/hostap" path="external/hostap" revision="bf04b0faadbdeb4b7943f2e2c4c5aa59df872bb1"/>
-  <!-- Information: platform/external/zlib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/zlib" path="external/zlib" revision="f96a1d1ebfdf1cd582210fd09c23d8f59e0ae094"/>
-  <!-- Information: platform/external/yaffs2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/external/yaffs2" path="external/yaffs2" revision="0afa916204c664b3114429637b63af1321a0aeca"/>
+  <!-- Information: platform/external/zlib is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/zlib" path="external/zlib" revision="f96a1d1ebfdf1cd582210fd09c23d8f59e0ae094"/>
+  <!-- Information: platform/external/yaffs2 is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/external/yaffs2" path="external/yaffs2" revision="0afa916204c664b3114429637b63af1321a0aeca"/>
   <!-- Information: platform/frameworks/base is tagged with M76XXUSNEKNLYA2040 --><project name="platform/frameworks/base" path="frameworks/base" revision="eb2bc75803ca179353c24c364a9c8a8ce23e8b78"/>
-  <!-- Information: platform/frameworks/opt/emoji is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/frameworks/opt/emoji" path="frameworks/opt/emoji" revision="a95d8db002770469d72dfaf59ff37ac96db29a87"/>
+  <!-- Information: platform/frameworks/opt/emoji is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/frameworks/opt/emoji" path="frameworks/opt/emoji" revision="a95d8db002770469d72dfaf59ff37ac96db29a87"/>
   <!-- Information: platform/frameworks/support is tagged with M8960AAAAANLYA1005304 --><project name="platform/frameworks/support" path="frameworks/support" revision="27208692b001981f1806f4f396434f4eac78b909"/>
   <!-- Information: platform/hardware/libhardware is tagged with M8960AAAAANLYA1049B --><project name="platform/hardware/libhardware" path="hardware/libhardware" revision="4a619901847621f8a7305edf42dd07347a140484"/>
   <!-- Information: platform/hardware/libhardware_legacy is tagged with M8960AAAAANLYA153611 --><project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="87b4d7afa8f854b445e2d0d95091f6f6069f2b30"/>
   <!-- Information: platform/libcore is tagged with M8960AAAAANLYA100715A --><project name="platform/libcore" path="libcore" revision="30841f9fba9ccd5c54f4f079f495994db97f283e"/>
-  <!-- Information: platform/ndk is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/ndk" path="ndk" revision="9f555971e1481854d5b4dc11b3e6af9fff4f241f"/>
+  <!-- Information: platform/ndk is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/ndk" path="ndk" revision="9f555971e1481854d5b4dc11b3e6af9fff4f241f"/>
   <!-- Information: platform/prebuilt is tagged with M8960AAAAANLYA1005304 --><project name="platform/prebuilt" path="prebuilt" revision="447ea790fcc957dde59730ecc2a65ca263bdc733"/>
   <!-- Information: platform/system/bluetooth is tagged with M8960AAAAANLYA100703 --><project name="platform/system/bluetooth" path="system/bluetooth" revision="7772cad4823f1f427ce1d4df84a55982386d6d18"/>
   <!-- Information: platform/system/core is tagged with M76XXUSNEKNLYA2040 --><project name="platform/system/core" path="system/core" revision="bf1970408676ce570b8f4dc3efa038e47552137f"/>
   <!-- Information: platform/system/extras is tagged with M8960AAAAANLYA1005304 --><project name="platform/system/extras" path="system/extras" revision="01db6c1254e1407740a543f24317fc540fc4c049"/>
   <!-- Information: platform/system/media is tagged with M8960AAAAANLYA1005304 --><project name="platform/system/media" path="system/media" revision="7f71c7fd362bbd992ff2e0e80f7af5859ad116ad"/>
   <!-- Information: platform/system/netd is tagged with M8960AAAAANLYA1049 --><project name="platform/system/netd" path="system/netd" revision="306e765248e3900041bf2737e9f57b1b5694a4ce"/>
   <!-- Information: platform/system/vold is tagged with M8960AAAAANLYA100715A --><project name="platform/system/vold" path="system/vold" revision="99fff257d53cc045d1460841edca5d901dacfcf5"/>
 
   <!-- Otoro/Unagi specific things -->
   <!-- Information: device/qcom/common is tagged with M8960AAAAANLYA100715A --><project name="device/qcom/common" path="device/qcom/common" revision="b9cdab8e1e1a215a8c65b8d5816f666bec7be205"/>
   <!-- Information: platform/vendor/qcom/msm7627a is tagged with M8960AAAAANLYA100715A --><project name="platform/vendor/qcom/msm7627a" path="device/qcom/msm7627a" revision="d920a502ba17cf4d716f8b1a615f07e796b0501a"/>
-  <project name="android-device-otoro" path="device/qcom/otoro" remote="b2g" revision="e3e99b264dd0230108aa78f2b653db4ce0e494fb"/>
-  <project name="android-device-unagi" path="device/qcom/unagi" remote="b2g" revision="e74925a10e11a4f0bc56158c248bd20c521d3dd7"/>
+  <project name="android-device-otoro" path="device/qcom/otoro" remote="b2g" revision="7662275433fc0b1d8b035f03185b24b7ca965ab4"/>
+  <project name="android-device-unagi" path="device/qcom/unagi" remote="b2g" revision="6c014552d1b26bee611d9a9b23bd4cd014e392ee"/>
   <project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="0a01247e4b0880f93424b27251cd3a1f6b19dbb2"/>
   <!-- Information: platform/hardware/qcom/camera is tagged with M76XXUSNEKNLYA2040 --><project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="1acf77a75e30f3fc8b1eed2057c97adf1cb1633f"/>
   <project name="hardware_qcom_display" path="hardware/qcom/display" remote="b2g" revision="6405d30f2fac7d8a1f2cb17b99fb7dd0a8bcfdac"/>
   <!-- Information: platform/hardware/qcom/media is tagged with M8960AAAAANLYA100715A --><project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="552c3ddb7174a01f3508782d40c4d8c845ab441a"/>
   <!-- Information: platform/hardware/qcom/gps is tagged with M8960AAAAANLYA100705 --><project name="platform/hardware/qcom/gps" path="hardware/qcom/gps" revision="23d5707b320d7fc69f8ba3b7d84d78a1c5681708"/>
-  <!-- Information: platform/hardware/msm7k is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.138 --><project name="platform/hardware/msm7k" path="hardware/msm7k" revision="8892d46805c5639b55dd07547745c5180da861e7"/>
+  <!-- Information: platform/hardware/msm7k is tagged with AU_LINUX_GECKO_ICS_STRAWBERRY.01.00.00.19.183 --><project name="platform/hardware/msm7k" path="hardware/msm7k" revision="8892d46805c5639b55dd07547745c5180da861e7"/>
   <!-- Information: platform/vendor/qcom-opensource/omx/mm-core is tagged with M8960AAAAANLYA100715A --><project name="platform/vendor/qcom-opensource/omx/mm-core" path="vendor/qcom/opensource/omx/mm-core" revision="ab17ac9a074b4bb69986a8436336bdfbbaf9cd39"/>
   <!-- Information: platform/hardware/ril is tagged with M76XXUSNEKNLYA1610 --><project name="platform/hardware/ril" path="hardware/ril" remote="caf" revision="fe9a3f63922143b57e79ed570bab2328df8c83a5"/>
 </manifest>
\ No newline at end of file
--- a/b2g/installer/Makefile.in
+++ b/b2g/installer/Makefile.in
@@ -23,22 +23,17 @@ MOZ_NONLOCALIZED_PKG_LIST = \
 MOZ_LOCALIZED_PKG_LIST = $(AB_CD) multilocale
 
 DEFINES += \
 	-DAB_CD=$(AB_CD) \
 	-DMOZ_APP_NAME=$(MOZ_APP_NAME) \
 	-DPREF_DIR=$(PREF_DIR) \
 	$(NULL)
 
-ifeq ($(MOZ_CHROME_FILE_FORMAT),jar)
-JAREXT=.jar
-else
-JAREXT=
-endif
-DEFINES += -DJAREXT=$(JAREXT)
+DEFINES += -DJAREXT=
 
 include $(topsrcdir)/ipc/app/defs.mk
 DEFINES += -DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME)
 
 # Set MSVC dlls version to package, if any.
 ifdef WIN32_REDIST_DIR
 DEFINES += -DMOZ_MSVC_REDIST=$(_MSC_VER)
 endif
@@ -46,17 +41,17 @@ endif
 ifdef ENABLE_MARIONETTE
 DEFINES += -DENABLE_MARIONETTE=1
 endif
 
 ifdef MOZ_PKG_MANIFEST_P
 MOZ_PKG_MANIFEST = package-manifest
 endif
 
-MOZ_POST_STAGING_CMD = find chrome -type f -name *.properties -exec $(PERL) -n -i -e 'print unless /^\#/' {} \;
+MOZ_PACKAGER_MINIFY=1
 
 include $(topsrcdir)/toolkit/mozapps/installer/packager.mk
 
 ifeq (bundle, $(MOZ_FS_LAYOUT))
 BINPATH = $(_BINPATH)
 DEFINES += -DAPPNAME=$(_APPNAME)
 else
 # Every other platform just winds up in dist/bin
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1052,16 +1052,17 @@ pref("devtools.debugger.remote-timeout",
 
 // The default Debugger UI settings
 pref("devtools.debugger.ui.win-x", 0);
 pref("devtools.debugger.ui.win-y", 0);
 pref("devtools.debugger.ui.win-width", 900);
 pref("devtools.debugger.ui.win-height", 400);
 pref("devtools.debugger.ui.stackframes-width", 200);
 pref("devtools.debugger.ui.variables-width", 300);
+pref("devtools.debugger.ui.pause-on-exceptions", false);
 pref("devtools.debugger.ui.panes-visible-on-startup", false);
 pref("devtools.debugger.ui.variables-sorting-enabled", true);
 pref("devtools.debugger.ui.variables-only-enum-visible", false);
 pref("devtools.debugger.ui.variables-searchbox-visible", false);
 
 // Enable the Profiler
 pref("devtools.profiler.enabled", true);
 
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -650,16 +650,19 @@ var gPluginHandler = {
         let notification = PopupNotifications.getNotification("click-to-play-plugins", aBrowser);
         if (notification)
           notification.remove();
         gPluginHandler._removeClickToPlayOverlays(contentWindow);
       }
     }];
     let notification = PopupNotifications.getNotification("click-to-play-plugins", aBrowser);
     let dismissed = notification ? notification.dismissed : true;
+    // Always show the doorhanger if the anchor is not available.
+    if (!isElementVisible(gURLBar))
+      dismissed = false;
     let options = { dismissed: dismissed, centerActions: centerActions };
     let icon = haveVulnerablePlugin ? "blocked-plugins-notification-icon" : "plugins-notification-icon"
     PopupNotifications.show(aBrowser, "click-to-play-plugins",
                             messageString, icon,
                             mainAction, secondaryActions, options);
   },
 
   _removeClickToPlayOverlays: function PH_removeClickToPlayOverlays(aContentWindow) {
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -13,16 +13,21 @@ let SocialUI = {
   // Called on delayed startup to initialize the UI
   init: function SocialUI_init() {
     Services.obs.addObserver(this, "social:pref-changed", false);
     Services.obs.addObserver(this, "social:ambient-notification-changed", false);
     Services.obs.addObserver(this, "social:profile-changed", false);
     Services.obs.addObserver(this, "social:recommend-info-changed", false);
     Services.obs.addObserver(this, "social:frameworker-error", false);
     Services.obs.addObserver(this, "social:provider-set", false);
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+    // this observer is necessary so things are also correctly updated
+    // when per-window PB isn't active
+    Services.obs.addObserver(this, "private-browsing", false);
+#endif
 
     Services.prefs.addObserver("social.sidebar.open", this, false);
     Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
 
     gBrowser.addEventListener("ActivateSocialFeature", this._activationEventHandler, true, true);
 
     // Called when we enter DOM full-screen mode.
     window.addEventListener("mozfullscreenchange", function () {
@@ -36,16 +41,19 @@ let SocialUI = {
   // Called on window unload
   uninit: function SocialUI_uninit() {
     Services.obs.removeObserver(this, "social:pref-changed");
     Services.obs.removeObserver(this, "social:ambient-notification-changed");
     Services.obs.removeObserver(this, "social:profile-changed");
     Services.obs.removeObserver(this, "social:recommend-info-changed");
     Services.obs.removeObserver(this, "social:frameworker-error");
     Services.obs.removeObserver(this, "social:provider-set");
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+    Services.obs.removeObserver(this, "private-browsing");
+#endif
 
     Services.prefs.removeObserver("social.sidebar.open", this);
     Services.prefs.removeObserver("social.toast-notifications.enabled", this);
   },
 
   // Called once, after window load, once Social.jsm's provider has been set.
   _providerReady: function SocialUI_providerReady() {
     this._updateActiveUI();
@@ -115,28 +123,36 @@ let SocialUI = {
           }
           break;
         case "social:recommend-info-changed":
           if (this._matchesCurrentProvider(data)) {
             SocialShareButton.updateShareState();
           }
           break;
         case "social:frameworker-error":
-          if (Social.provider && Social.provider.origin == data) {
+          if (this.enabled && Social.provider.origin == data) {
             SocialSidebar.setSidebarErrorMessage("frameworker-error");
           }
           break;
 
         case "nsPref:changed":
           if (data == "social.sidebar.open") {
             SocialSidebar.update();
           } else if (data == "social.toast-notifications.enabled") {
             SocialToolbar.updateButton();
           }
           break;
+
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+        case "private-browsing":
+          this._updateEnabledState();
+          this._updateActiveUI();
+          SocialToolbar.init();
+          break;
+#endif
       }
     } catch (e) {
       Components.utils.reportError(e + "\n" + e.stack);
       throw e;
     }
   },
 
   nonBrowserWindowInit: function SocialUI_nonBrowserInit() {
@@ -150,34 +166,41 @@ let SocialUI = {
       openUILinkIn(Social.provider.profile.profileURL, "tab");
     else {
       // XXX Bug 789585 will implement an API for provider-specified login pages.
       openUILinkIn(Social.provider.origin, "tab");
     }
   },
 
   _updateActiveUI: function SocialUI_updateActiveUI() {
+    // The "active" UI isn't dependent on there being a provider, just on
+    // social being "active" (but also chromeless/PB)
+    let enabled = Social.active && !this._chromeless
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+                  && !PrivateBrowsingUtils.isWindowPrivate(window)
+#endif
+        ;
     let broadcaster = document.getElementById("socialActiveBroadcaster");
-    broadcaster.hidden = !Social.active;
+    broadcaster.hidden = !enabled;
 
     if (!Social.provider)
       return;
 
     let toggleCommand = document.getElementById("Social:Toggle");
     // We only need to update the command itself - all our menu items use it.
     let label = gNavigatorBundle.getFormattedString(Social.provider.enabled ?
                                                       "social.turnOff.label" :
                                                       "social.turnOn.label",
                                                     [Social.provider.name]);
     let accesskey = gNavigatorBundle.getString(Social.provider.enabled ?
                                                  "social.turnOff.accesskey" :
                                                  "social.turnOn.accesskey");
     toggleCommand.setAttribute("label", label);
     toggleCommand.setAttribute("accesskey", accesskey);
-    toggleCommand.setAttribute("hidden", Social.active ? "false" : "true");
+    toggleCommand.setAttribute("hidden", enabled ? "false" : "true");
   },
 
   _updateMenuItems: function () {
     if (!Social.provider)
       return;
 
     // The View->Sidebar and Menubar->Tools menu.
     for (let id of ["menu_socialSidebar", "menu_socialAmbientMenu"])
@@ -198,16 +221,21 @@ let SocialUI = {
       return;
 
     // Check that the associated document's origin is in our whitelist
     let providerOrigin = targetDoc.nodePrincipal.origin;
     let whitelist = Services.prefs.getCharPref("social.activation.whitelist");
     if (whitelist.split(",").indexOf(providerOrigin) == -1)
       return;
 
+    // If we are in PB mode, we silently do nothing (bug 829404 exists to
+    // do something sensible here...)
+    if (PrivateBrowsingUtils.isWindowPrivate(window))
+      return;
+
     // If the last event was received < 1s ago, ignore this one
     let now = Date.now();
     if (now - Social.lastEventReceived < 1000)
       return;
     Social.lastEventReceived = now;
 
     // Keep track of the old provider in case of undo
     let oldOrigin = Social.provider ? Social.provider.origin : "";
@@ -280,31 +308,50 @@ let SocialUI = {
                 ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1 +
                 ps.BUTTON_POS_0_DEFAULT;
 
     let confirmationIndex = ps.confirmEx(null, dialogTitle, text, flags,
                                          okButtonText, null, null, null, {});
     if (confirmationIndex == 0) {
       Social.deactivateFromOrigin(Social.provider.origin);
     }
-  }
+  },
+
+  get _chromeless() {
+    // Is this a popup window that doesn't want chrome shown?
+    let docElem = document.documentElement;
+    let chromeless = docElem.getAttribute("chromehidden").indexOf("extrachrome") >= 0;
+    // This property is "fixed" for a window, so avoid doing the check above
+    // multiple times...
+    delete this._chromeless;
+    this._chromeless = chromeless;
+    return chromeless;
+  },
+
+  get enabled() {
+    // Returns whether social is enabled *for this window*.
+    if (this._chromeless
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+        || PrivateBrowsingUtils.isWindowPrivate(window)
+#endif
+       )
+      return false;
+    return !!(Social.active && Social.provider && Social.provider.enabled);
+  },
+
 }
 
 let SocialChatBar = {
   get chatbar() {
     return document.getElementById("pinnedchats");
   },
   // Whether the chatbar is available for this window.  Note that in full-screen
   // mode chats are available, but not shown.
   get isAvailable() {
-    if (!Social.haveLoggedInUser())
-      return false;
-    let docElem = document.documentElement;
-    let chromeless = docElem.getAttribute("chromehidden").indexOf("extrachrome") >= 0;
-    return Social.uiVisible && !chromeless;
+    return SocialUI.enabled && Social.haveLoggedInUser();
   },
   // Does this chatbar have any chats (whether minimized, collapsed or normal)
   get hasChats() {
     return !!this.chatbar.firstElementChild;
   },
   openChat: function(aProvider, aURL, aCallback, aMode) {
     if (this.isAvailable)
       this.chatbar.openChat(aProvider, aURL, aCallback, aMode);
@@ -389,17 +436,17 @@ let SocialFlyout = {
     let doc = this.panel.firstChild.contentDocument;
     let evt = doc.createEvent("CustomEvent");
     evt.initCustomEvent(name, true, true, {});
     doc.documentElement.dispatchEvent(evt);
   },
 
   _createFrame: function() {
     let panel = this.panel;
-    if (!Social.provider || panel.firstChild)
+    if (!SocialUI.enabled || panel.firstChild)
       return;
     // create and initialize the panel for this window
     let iframe = document.createElement("iframe");
     iframe.setAttribute("type", "content");
     iframe.setAttribute("class", "social-panel-frame");
     iframe.setAttribute("flex", "1");
     iframe.setAttribute("origin", Social.provider.origin);
     panel.appendChild(iframe);
@@ -466,17 +513,17 @@ let SocialFlyout = {
     this.panel.firstChild.docShell.isActive = false;
     this.dispatchPanelEvent("socialFrameHide");
   },
 
   open: function(aURL, yOffset, aCallback) {
     // Hide any other social panels that may be open.
     document.getElementById("social-notification-panel").hidePopup();
 
-    if (!Social.provider)
+    if (!SocialUI.enabled)
       return;
     let panel = this.panel;
     if (!panel.firstChild)
       this._createFrame();
     panel.hidden = false;
     let iframe = panel.firstChild;
 
     let src = iframe.getAttribute("src");
@@ -532,17 +579,17 @@ let SocialShareButton = {
       return;
     this.updateProfileInfo();
   },
 
   // Called when the provider's profile info changes (or when the provider
   // changes, via updateProvider)
   updateProfileInfo: function SSB_updateProfileInfo() {
     let profileRow = document.getElementById("unsharePopupHeader");
-    let profile = Social.provider.profile;
+    let profile = SocialUI.enabled ? Social.provider.profile : null;
     if (profile && profile.displayName) {
       profileRow.hidden = false;
       let portrait = document.getElementById("socialUserPortrait");
       portrait.setAttribute("src", profile.portrait || "chrome://global/skin/icons/information-32.png");
       let displayName = document.getElementById("socialUserDisplayName");
       displayName.setAttribute("label", profile.displayName);
     } else {
       profileRow.hidden = true;
@@ -564,17 +611,17 @@ let SocialShareButton = {
   canSharePage: function SSB_canSharePage(aURI) {
     // We only allow sharing of http or https
     return aURI && (aURI.schemeIs('http') || aURI.schemeIs('https'));
   },
 
   updateButtonHiddenState: function SSB_updateButtonHiddenState() {
     let shareButton = this.shareButton;
     if (shareButton)
-      shareButton.hidden = !Social.uiVisible || Social.provider.recommendInfo == null ||
+      shareButton.hidden = !SocialUI.enabled || Social.provider.recommendInfo == null ||
                            !Social.haveLoggedInUser() ||
                            !this.canSharePage(gBrowser.currentURI);
 
     // also update the relevent command's disabled state so the keyboard
     // shortcut only works when available.
     let cmd = document.getElementById("Social:SharePage");
     cmd.setAttribute("disabled", shareButton.hidden ? "true" : "false");
   },
@@ -630,17 +677,17 @@ let SocialShareButton = {
   },
 
   updateShareState: function SSB_updateShareState() {
     this.updateButtonHiddenState();
 
     let shareButton = this.shareButton;
     let currentPageShared = shareButton && !shareButton.hidden && Social.isPageShared(gBrowser.currentURI);
 
-    let recommendInfo = Social.provider ? Social.provider.recommendInfo : null;
+    let recommendInfo = SocialUI.enabled ? Social.provider.recommendInfo : null;
     // Provide a11y-friendly notification of share.
     let status = document.getElementById("share-button-status");
     if (status) {
       // XXX - this should also be capable of reflecting that the page was
       // unshared (ie, it needs to manage three-states: (1) nothing done, (2)
       // shared, (3) shared then unshared)
       // Note that we *do* have an appropriate string from the provider for
       // this (recommendInfo.messages.unsharedLabel) but currently lack a way of
@@ -672,20 +719,20 @@ var SocialMenu = {
   populate: function SocialMenu_populate() {
     let submenu = document.getElementById("menu_social-statusarea-popup");
     let ambientMenuItems = submenu.getElementsByClassName("ambient-menuitem");
     while (ambientMenuItems.length)
       submenu.removeChild(ambientMenuItems.item(0));
 
     let separator = document.getElementById("socialAmbientMenuSeparator");
     separator.hidden = true;
-    if (!Social.uiVisible)
+    let provider = SocialUI.enabled ? Social.provider : null;
+    if (!provider)
       return;
 
-    let provider = Social.provider;
     let iconNames = Object.keys(provider.ambientNotificationIcons);
     for (let name of iconNames) {
       let icon = provider.ambientNotificationIcons[name];
       if (!icon.label || !icon.menuURL)
         continue;
       separator.hidden = false;
       let menuitem = document.createElement("menuitem");
       menuitem.setAttribute("label", icon.label);
@@ -713,17 +760,17 @@ var SocialToolbar = {
     removeCommand.setAttribute("accesskey", accesskey);
 
     this.updateProvider();
     this._dynamicResizer = new DynamicResizeWatcher();
   },
 
   // Called when the Social.provider changes
   updateProvider: function () {
-    if (!Social.provider)
+    if (!SocialUI.enabled)
       return;
     this.button.style.listStyleImage = "url(" + Social.provider.iconURL + ")";
     this.button.setAttribute("label", Social.provider.name);
     this.button.setAttribute("tooltiptext", Social.provider.name);
     this.updateButton();
     this.updateProfile();
     this.populateProviderMenus();
   },
@@ -731,17 +778,17 @@ var SocialToolbar = {
   get button() {
     return document.getElementById("social-provider-button");
   },
 
   // Note: this doesn't actually handle hiding the toolbar button,
   // socialActiveBroadcaster is responsible for that.
   updateButtonHiddenState: function SocialToolbar_updateButtonHiddenState() {
     let tbi = document.getElementById("social-toolbar-item");
-    let socialEnabled = Social.uiVisible;
+    let socialEnabled = SocialUI.enabled;
     for (let className of ["social-statusarea-separator", "social-statusarea-user"]) {
       for (let element of document.getElementsByClassName(className))
         element.hidden = !socialEnabled;
     }
     let toggleNotificationsCommand = document.getElementById("Social:ToggleNotifications");
     toggleNotificationsCommand.setAttribute("hidden", !socialEnabled);
 
     if (!Social.haveLoggedInUser() || !socialEnabled) {
@@ -787,17 +834,17 @@ var SocialToolbar = {
     panel.hidden = false;
 
     let command = document.getElementById("Social:ToggleNotifications");
     command.setAttribute("checked", Services.prefs.getBoolPref("social.toast-notifications.enabled"));
 
     const CACHE_PREF_NAME = "social.cached.ambientNotificationIcons";
     // provider.profile == undefined means no response yet from the provider
     // to tell us whether the user is logged in or not.
-    if (!provider.enabled ||
+    if (!SocialUI.enabled ||
         (!Social.haveLoggedInUser() && provider.profile !== undefined)) {
       // Either no enabled provider, or there is a provider and it has
       // responded with a profile and the user isn't loggedin.  The icons
       // etc have already been removed by updateButtonHiddenState, so we want
       // to nuke any cached icons we have and get out of here!
       Services.prefs.clearUserPref(CACHE_PREF_NAME);
       return;
     }
@@ -999,18 +1046,18 @@ var SocialToolbar = {
 
   _populateProviderMenu: function SocialToolbar_renderProviderMenu(providerMenuSep, providers) {
     let menu = providerMenuSep.parentNode;
     // selectable providers are inserted before the provider-menu seperator,
     // remove any menuitems in that area
     while (providerMenuSep.previousSibling.nodeName == "menuitem") {
       menu.removeChild(providerMenuSep.previousSibling);
     }
-    // only show a selection if there is more than one
-    if (!Social.enabled || providers.length < 2) {
+    // only show a selection if enabled and there is more than one
+    if (!SocialUI.enabled || Social.providers.length < 2) {
       providerMenuSep.hidden = true;
       return;
     }
     for (let provider of providers) {
       let menuitem = document.createElement("menuitem");
       menuitem.className = "menuitem-iconic social-provider-menuitem";
       menuitem.setAttribute("image", provider.iconURL);
       menuitem.setAttribute("label", provider.name);
@@ -1041,25 +1088,17 @@ var SocialSidebar = {
     aDocShell.QueryInterface(Ci.nsIWebProgress)
              .addProgressListener(SocialSidebar.errorListener,
                                   Ci.nsIWebProgress.NOTIFY_STATE_REQUEST |
                                   Ci.nsIWebProgress.NOTIFY_LOCATION);
   },
 
   // Whether the sidebar can be shown for this window.
   get canShow() {
-    return Social.uiVisible && Social.provider.sidebarURL && !this.chromeless;
-  },
-
-  // Whether this is a "chromeless window" (e.g. popup window). We don't show
-  // the sidebar in these windows.
-  get chromeless() {
-    let docElem = document.documentElement;
-    return docElem.getAttribute('disablechrome') ||
-           docElem.getAttribute('chromehidden').contains("toolbar");
+    return SocialUI.enabled && Social.provider.sidebarURL;
   },
 
   // Whether the user has toggled the sidebar on (for windows where it can appear)
   get opened() {
     return Services.prefs.getBoolPref("social.sidebar.open") && !document.mozFullScreen;
   },
 
   setSidebarVisibilityState: function(aEnabled) {
--- a/browser/base/content/newtab/newTab.css
+++ b/browser/base/content/newtab/newTab.css
@@ -13,16 +13,29 @@ input[type=button] {
   -moz-box-flex: 1;
   -moz-user-focus: normal;
 }
 
 #newtab-scrollbox:not([page-disabled]) {
   overflow: auto;
 }
 
+/* UNDO */
+#newtab-undo-container {
+  transition: opacity 100ms ease-out;
+  display: -moz-box;
+  -moz-box-align: center;
+  -moz-box-pack: center;
+}
+
+#newtab-undo-container[undo-disabled] {
+  opacity: 0;
+  pointer-events: none;
+}
+
 /* TOGGLE */
 #newtab-toggle {
   position: absolute;
   top: 12px;
   right: 12px;
 }
 
 #newtab-toggle:-moz-locale-dir(rtl) {
@@ -36,17 +49,20 @@ input[type=button] {
   position: relative;
   -moz-box-flex: 1;
   -moz-box-orient: vertical;
 }
 
 #newtab-margin-top {
   min-height: 50px;
   max-height: 80px;
+  display: -moz-box;
   -moz-box-flex: 1;
+  -moz-box-align: center;
+  -moz-box-pack: center;
 }
 
 #newtab-margin-bottom {
   min-height: 40px;
   max-height: 100px;
   -moz-box-flex: 1;
 }
 
--- a/browser/base/content/newtab/newTab.js
+++ b/browser/base/content/newtab/newTab.js
@@ -46,11 +46,12 @@ const HTML_NAMESPACE = "http://www.w3.or
 #include cells.js
 #include sites.js
 #include drag.js
 #include dragDataHelper.js
 #include drop.js
 #include dropTargetShim.js
 #include dropPreview.js
 #include updater.js
+#include undo.js
 
 // Everything is loaded. Initialize the New Tab Page.
 gPage.init();
--- a/browser/base/content/newtab/newTab.xul
+++ b/browser/base/content/newtab/newTab.xul
@@ -15,17 +15,30 @@
 
 <xul:window id="newtab-window" xmlns="http://www.w3.org/1999/xhtml"
             xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
             disablefastfind="true" title="&newtab.pageTitle;">
 
   <div id="newtab-scrollbox">
 
     <div id="newtab-vertical-margin">
-      <div id="newtab-margin-top"/>
+      <div id="newtab-margin-top">
+        <div id="newtab-undo-container" undo-disabled="true">
+          <xul:label id="newtab-undo-label"
+                     value="&newtab.undo.removedLabel;" />
+          <xul:button id="newtab-undo-button" tabindex="-1"
+                      label="&newtab.undo.undoButton;"
+                      class="newtab-undo-button" />
+          <xul:button id="newtab-undo-restore-button" tabindex="-1"
+                      label="&newtab.undo.restoreButton;"
+                      class="newtab-undo-button" />
+          <xul:toolbarbutton id="newtab-undo-close-button" tabindex="-1"
+                             title="&newtab.undo.closeTooltip;" />
+        </div>
+      </div>
 
       <div id="newtab-horizontal-margin">
         <div class="newtab-side-margin"/>
 
         <div id="newtab-grid">
         </div>
 
         <div class="newtab-side-margin"/>
--- a/browser/base/content/newtab/page.js
+++ b/browser/base/content/newtab/page.js
@@ -34,18 +34,21 @@ let gPage = {
   /**
    * Listens for notifications specific to this page.
    */
   observe: function Page_observe() {
     let enabled = gAllPages.enabled;
     this._updateAttributes(enabled);
 
     // Initialize the whole page if we haven't done that, yet.
-    if (enabled)
+    if (enabled) {
       this._init();
+    } else {
+      gUndoDialog.hide();
+    }
   },
 
   /**
    * Updates the whole page and the grid when the storage has changed.
    */
   update: function Page_update() {
     gGrid.refresh();
   },
--- a/browser/base/content/newtab/sites.js
+++ b/browser/base/content/newtab/sites.js
@@ -79,16 +79,17 @@ Site.prototype = {
   },
 
   /**
    * Blocks the site (removes it from the grid) and calls the given callback
    * when done.
    */
   block: function Site_block() {
     if (!gBlockedLinks.isBlocked(this._link)) {
+      gUndoDialog.show(this);
       gBlockedLinks.block(this._link);
       gUpdater.updateGrid();
     }
   },
 
   /**
    * Gets the DOM node specified by the given query selector.
    * @param aSelector The query selector.
new file mode 100644
--- /dev/null
+++ b/browser/base/content/newtab/undo.js
@@ -0,0 +1,116 @@
+#ifdef 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/. */
+#endif
+
+/**
+ * Dialog allowing to undo the removal of single site or to completely restore
+ * the grid's original state.
+ */
+let gUndoDialog = {
+  /**
+   * The undo dialog's timeout in miliseconds.
+   */
+  HIDE_TIMEOUT_MS: 15000,
+
+  /**
+   * Contains undo information.
+   */
+  _undoData: null,
+
+  /**
+   * Initializes the undo dialog.
+   */
+  init: function UndoDialog_init() {
+    this._undoContainer = document.getElementById("newtab-undo-container");
+    this._undoContainer.addEventListener("click", this, false);
+    this._undoButton = document.getElementById("newtab-undo-button");
+    this._undoCloseButton = document.getElementById("newtab-undo-close-button");
+    this._undoRestoreButton = document.getElementById("newtab-undo-restore-button");
+  },
+
+  /**
+   * Shows the undo dialog.
+   * @param aSite The site that just got removed.
+   */
+  show: function UndoDialog_show(aSite) {
+    if (this._undoData)
+      clearTimeout(this._undoData.timeout);
+
+    this._undoData = {
+      index: aSite.cell.index,
+      wasPinned: aSite.isPinned(),
+      blockedLink: aSite.link,
+      timeout: setTimeout(this.hide.bind(this), this.HIDE_TIMEOUT_MS)
+    };
+
+    this._undoContainer.removeAttribute("undo-disabled");
+    this._undoButton.removeAttribute("tabindex");
+    this._undoCloseButton.removeAttribute("tabindex");
+    this._undoRestoreButton.removeAttribute("tabindex");
+  },
+
+  /**
+   * Hides the undo dialog.
+   */
+  hide: function UndoDialog_hide() {
+    if (!this._undoData)
+      return;
+
+    clearTimeout(this._undoData.timeout);
+    this._undoData = null;
+    this._undoContainer.setAttribute("undo-disabled", "true");
+    this._undoButton.setAttribute("tabindex", "-1");
+    this._undoCloseButton.setAttribute("tabindex", "-1");
+    this._undoRestoreButton.setAttribute("tabindex", "-1");
+  },
+
+  /**
+   * The undo dialog event handler.
+   * @param aEvent The event to handle.
+   */
+  handleEvent: function UndoDialog_handleEvent(aEvent) {
+    switch (aEvent.target.id) {
+      case "newtab-undo-button":
+        this._undo();
+        break;
+      case "newtab-undo-restore-button":
+        this._undoAll();
+        break;
+      case "newtab-undo-close-button":
+        this.hide();
+        break;
+    }
+  },
+
+  /**
+   * Undo the last blocked site.
+   */
+  _undo: function UndoDialog_undo() {
+    if (!this._undoData)
+      return;
+
+    let {index, wasPinned, blockedLink} = this._undoData;
+    gBlockedLinks.unblock(blockedLink);
+
+    if (wasPinned) {
+      gPinnedLinks.pin(blockedLink, index);
+    }
+
+    gUpdater.updateGrid();
+    this.hide();
+  },
+
+  /**
+   * Undo all blocked sites.
+   */
+  _undoAll: function UndoDialog_undoAll() {
+    NewTabUtils.undoAll(function() {
+      gUpdater.updateGrid();
+      this.hide();
+    }.bind(this));
+  }
+};
+
+gUndoDialog.init();
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1270,19 +1270,19 @@
                 tabContainer._handleNewTab(t);
               }, 0, this.tabContainer);
             } else {
               setTimeout(function (tabContainer) {
                 if (t.pinned)
                   tabContainer._handleNewTab(t);
                 else {
                   if (tabContainer._tabAnimationLoggingEnabled) {
-                    window.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDOMWindowUtils)
-                          .startFrameTimeRecording();
+                    t._recordingHandle = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                                               .getInterface(Ci.nsIDOMWindowUtils)
+                                               .startFrameTimeRecording();
                   }
                   t._animStartTime = Date.now();
                   t.setAttribute("fadein", "true");
 
                   // This call to adjustTabstrip is redundant but needed so that
                   // when opening a second tab, the first tab's close buttons
                   // appears immediately rather than when the transition ends.
                   if (tabContainer.childNodes.length == 2)
@@ -1575,19 +1575,19 @@
                 aTab.getAttribute("fadein") != "true" /* fade-in transition hasn't been triggered yet */ ||
                 window.getComputedStyle(aTab).maxWidth == "0.1px" /* fade-in transition hasn't moved yet */ ||
                 !Services.prefs.getBoolPref("browser.tabs.animate")) {
               this._endRemoveTab(aTab);
               return;
             }
 
             if (this.tabContainer._tabAnimationLoggingEnabled) {
-              window.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIDOMWindowUtils)
-                    .startFrameTimeRecording();
+              aTab._recordingHandle = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                                            .getInterface(Ci.nsIDOMWindowUtils)
+                                            .startFrameTimeRecording();
             }
             aTab._animStartTime = Date.now();
 
             this._blurTab(aTab);
             aTab.style.maxWidth = ""; // ensure that fade-out transition happens
             aTab.removeAttribute("fadein");
 
             setTimeout(function (tab, tabbrowser) {
@@ -3539,21 +3539,21 @@
 
         if (tab._animStartTime) {
           Services.telemetry.getHistogramById(tab.closing ?
                                               "FX_TAB_ANIM_CLOSE_MS" :
                                               "FX_TAB_ANIM_OPEN_MS")
                             .add(Date.now() - tab._animStartTime);
           tab._animStartTime = 0;
 
-          if (this._tabAnimationLoggingEnabled) {
+          if (this._tabAnimationLoggingEnabled && tab.hasOwnProperty("_recordingHandle")) {
             let paints = {};
             let intervals = window.QueryInterface(Ci.nsIInterfaceRequestor)
                                   .getInterface(Ci.nsIDOMWindowUtils)
-                                  .stopFrameTimeRecording(paints);
+                                  .stopFrameTimeRecording(tab._recordingHandle, paints);
             let msg = "Tab " + (tab.closing ? "close" : "open") + " (Frame-interval / paint-processing):\n";
             for (let i = 0; i < intervals.length; i++) {
               msg += Math.round(intervals[i]) + " / " + Math.round(paints.value[i]) + "\n";
             }
             Services.console.logStringMessage(msg);
           }
         }
 
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -249,16 +249,17 @@ endif
                  plugin_test_scriptedNoPopup2.html \
                  plugin_test_scriptedNoPopup3.html \
                  plugin_alternate_content.html \
                  plugin_both.html \
                  plugin_both2.html \
                  plugin_add_dynamically.html \
                  plugin_clickToPlayAllow.html \
                  plugin_clickToPlayDeny.html \
+                 plugin_bug744745.html \
                  plugin_bug749455.html \
                  plugin_bug797677.html \
                  plugin_bug818009.html \
                  plugin_bug820497.html \
                  plugin_hidden_to_visible.html \
                  plugin_two_types.html \
                  alltabslistener.html \
                  zoom_test.html \
@@ -278,16 +279,17 @@ endif
                  authenticate.sjs \
                  browser_minimize.js \
                  browser_aboutSyncProgress.js \
                  browser_middleMouse_inherit.js \
                  redirect_bug623155.sjs \
                  browser_tabDrop.js \
                  browser_lastAccessedTab.js \
                  browser_bug734076.js \
+                 browser_bug744745.js \
                  browser_bug812562.js \
                  browser_bug818009.js \
                  browser_bug818118.js \
                  browser_bug820497.js \
                  blockPluginVulnerableUpdatable.xml \
                  blockPluginVulnerableNoUpdate.xml \
                  blockNoPlugins.xml \
                  browser_utilityOverlay.js \
--- a/browser/base/content/test/browser_bookmark_titles.js
+++ b/browser/base/content/test/browser_bookmark_titles.js
@@ -1,37 +1,40 @@
 /* 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/. */
 
 // This file is tests for the default titles that new bookmarks get.
 
 let tests = [
+    // Common page.
     ['http://example.com/browser/browser/base/content/test/dummy_page.html',
      'Dummy test page'],
-    ['data:text/html;charset=utf-8,<title>test data: url</title>',
+    // Data URI.
+    ['data:text/html;charset=utf-8,<title>test%20data:%20url</title>',
      'test data: url'],
-    ['http://unregistered-domain.example',
-     'http://unregistered-domain.example/'],
+    // about:neterror
+    ['data:application/vnd.mozilla.xul+xml,',
+     'data:application/vnd.mozilla.xul+xml,'],
+    // about:certerror
     ['https://untrusted.example.com/somepage.html',
      'https://untrusted.example.com/somepage.html']
 ];
 
 function generatorTest() {
     gBrowser.selectedTab = gBrowser.addTab();
     let browser = gBrowser.selectedBrowser;
+    browser.stop(); // stop the about:blank load.
 
     browser.addEventListener("DOMContentLoaded", nextStep, true);
     registerCleanupFunction(function () {
         browser.removeEventListener("DOMContentLoaded", nextStep, true);
         gBrowser.removeCurrentTab();
     });
 
-    yield; // Wait for the new tab to load.
-
     // Test that a bookmark of each URI gets the corresponding default title.
     for (let i = 0; i < tests.length; ++i) {
         let [uri, title] = tests[i];
         content.location = uri;
         yield;
         checkBookmark(uri, title);
     }
 
@@ -59,17 +62,16 @@ function generatorTest() {
         "Offline mode successfully simulated network outage.");
     checkBookmark(uri, title);
 }
 
 // Bookmark the current page and confirm that the new bookmark has the expected
 // title. (Then delete the bookmark.)
 function checkBookmark(uri, expected_title) {
     PlacesCommandHook.bookmarkCurrentPage(false);
-    
+
     let id = PlacesUtils.getMostRecentBookmarkForURI(PlacesUtils._uri(uri));
+    ok(id > 0, "Found the expected bookmark");
     let title = PlacesUtils.bookmarks.getItemTitle(id);
-
     is(title, expected_title, "Bookmark got a good default title.");
 
     PlacesUtils.bookmarks.removeItem(id);
 }
-
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_bug744745.js
@@ -0,0 +1,42 @@
+/* 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/. */
+
+var gTestBrowser = null;
+var gNumPluginBindingsAttached = 0;
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function test() {
+  waitForExplicitFinish();
+  registerCleanupFunction(function() {
+    Services.prefs.clearUserPref("plugins.click_to_play");
+    gTestBrowser.removeEventListener("PluginBindingAttached", pluginBindingAttached, true, true);
+    gBrowser.removeCurrentTab();
+    window.focus();
+  });
+
+  Services.prefs.setBoolPref("plugins.click_to_play", true);
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gTestBrowser = gBrowser.selectedBrowser;
+  gTestBrowser.addEventListener("PluginBindingAttached", pluginBindingAttached, true, true);
+  var gHttpTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
+  gTestBrowser.contentWindow.location = gHttpTestRoot + "plugin_bug744745.html";
+}
+
+function pluginBindingAttached() {
+  gNumPluginBindingsAttached++;
+
+  if (gNumPluginBindingsAttached == 1) {
+    var doc = gTestBrowser.contentDocument;
+    var testplugin = doc.getElementById("test");
+    ok(testplugin, "should have test plugin");
+    var style = getComputedStyle(testplugin);
+    ok('opacity' in style, "style should have opacity set");
+    is(style.opacity, 1, "opacity should be 1");
+    finish();
+  } else {
+    ok(false, "if we've gotten here, something is quite wrong");
+  }
+}
--- a/browser/base/content/test/browser_gestureSupport.js
+++ b/browser/base/content/test/browser_gestureSupport.js
@@ -12,18 +12,16 @@ let test_utils;
 let test_commandset;
 let test_prefBranch = "browser.gesture.";
 
 function test()
 {
   // Disable the default gestures support during the test
   gGestureSupport.init(false);
 
-  // Enable privileges so we can use nsIDOMWindowUtils interface
-  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
   test_utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
     getInterface(Components.interfaces.nsIDOMWindowUtils);
 
   // Run the tests of "simple gesture" events generally
   test_EnsureConstantsAreDisjoint();
   test_TestEventListeners();
   test_TestEventCreation();
 
--- a/browser/base/content/test/browser_save_link-perwindowpb.js
+++ b/browser/base/content/test/browser_save_link-perwindowpb.js
@@ -1,13 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 var MockFilePicker = SpecialPowers.MockFilePicker;
-MockFilePicker.init();
+MockFilePicker.init(window);
 
 let tempScope = {};
 Cu.import("resource://gre/modules/NetUtil.jsm", tempScope);
 let NetUtil = tempScope.NetUtil;
 
 // Trigger a save of a link in public mode, then trigger an identical save
 // in private mode and ensure that the second request is differentiated from
 // the first by checking the cookies that are sent.
--- a/browser/base/content/test/browser_save_link.js
+++ b/browser/base/content/test/browser_save_link.js
@@ -1,13 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 var MockFilePicker = SpecialPowers.MockFilePicker;
-MockFilePicker.init();
+MockFilePicker.init(window);
 
 let tempScope = {};
 Cu.import("resource://gre/modules/NetUtil.jsm", tempScope);
 let NetUtil = tempScope.NetUtil;
 
 // Trigger a save of a link in public mode, then trigger an identical save
 // in private mode and ensure that the second request is differentiated from
 // the first by checking the cookies that are sent.
--- a/browser/base/content/test/browser_save_private_link.js
+++ b/browser/base/content/test/browser_save_private_link.js
@@ -1,13 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 var MockFilePicker = SpecialPowers.MockFilePicker;
-MockFilePicker.init();
+MockFilePicker.init(window);
 
 function checkDiskCacheFor(filename) {
   let visitor = {
     visitDevice: function(deviceID, deviceInfo) {
       if (deviceID == "disk")
         info(deviceID + " device contains " + deviceInfo.entryCount + " entries");
       return deviceID == "disk";
     },
--- a/browser/base/content/test/browser_save_private_link_perwindowpb.js
+++ b/browser/base/content/test/browser_save_private_link_perwindowpb.js
@@ -111,17 +111,17 @@ function test() {
 
    // this function is called after calling finish() on the test.
   registerCleanupFunction(function() {
     windowsToClose.forEach(function(aWin) {
       aWin.close();
     });
   });
 
-  MockFilePicker.init();
+  MockFilePicker.init(window);
   // then test when on private mode
   testOnWindow({private: true}, function(aWin) {
     doTest(true, aWin, finish);
   });
 }
 
 Cc["@mozilla.org/moz/jssubscript-loader;1"]
   .getService(Ci.mozIJSSubScriptLoader)
--- a/browser/base/content/test/browser_save_video.js
+++ b/browser/base/content/test/browser_save_video.js
@@ -1,13 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 var MockFilePicker = SpecialPowers.MockFilePicker;
-MockFilePicker.init();
+MockFilePicker.init(window);
 
 /**
  * TestCase for bug 564387
  * <https://bugzilla.mozilla.org/show_bug.cgi?id=564387>
  */
 function test() {
   waitForExplicitFinish();
   var fileName;
--- a/browser/base/content/test/newtab/Makefile.in
+++ b/browser/base/content/test/newtab/Makefile.in
@@ -14,16 +14,17 @@ include $(topsrcdir)/config/rules.mk
 _BROWSER_FILES = \
 	browser_newtab_block.js \
 	browser_newtab_disable.js \
 	browser_newtab_drag_drop.js \
 	browser_newtab_drop_preview.js \
 	browser_newtab_focus.js \
 	browser_newtab_reset.js \
 	browser_newtab_tabsync.js \
+	browser_newtab_undo.js \
 	browser_newtab_unpin.js \
 	browser_newtab_bug721442.js \
 	browser_newtab_bug722273.js \
 	browser_newtab_bug723102.js \
 	browser_newtab_bug723121.js \
 	browser_newtab_bug725996.js \
 	browser_newtab_bug734043.js \
 	browser_newtab_bug735987.js \
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/newtab/browser_newtab_undo.js
@@ -0,0 +1,49 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * These tests make sure that the undo dialog works as expected.
+ */
+function runTests() {
+  // remove unpinned sites and undo it
+  yield setLinks("0,1,2,3,4,5,6,7,8");
+  setPinnedLinks("5");
+
+  yield addNewTabPageTab();
+  checkGrid("5p,0,1,2,3,4,6,7,8");
+
+  yield blockCell(4);
+  yield blockCell(4);
+  checkGrid("5p,0,1,2,6,7,8");
+
+  yield undo();
+  checkGrid("5p,0,1,2,4,6,7,8");
+
+  // now remove a pinned site and undo it
+  yield blockCell(0);
+  checkGrid("0,1,2,4,6,7,8");
+
+  yield undo();
+  checkGrid("5p,0,1,2,4,6,7,8");
+
+  // remove a site and restore all
+  yield blockCell(1);
+  checkGrid("5p,1,2,4,6,7,8");
+
+  yield undoAll();
+  checkGrid("5p,0,1,2,3,4,6,7,8");
+}
+
+function undo() {
+  let cw = getContentWindow();
+  let target = cw.document.getElementById("newtab-undo-button");
+  EventUtils.synthesizeMouseAtCenter(target, {}, cw);
+  whenPagesUpdated();
+}
+
+function undoAll() {
+  let cw = getContentWindow();
+  let target = cw.document.getElementById("newtab-undo-restore-button");
+  EventUtils.synthesizeMouseAtCenter(target, {}, cw);
+  whenPagesUpdated();
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/plugin_bug744745.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head><meta charset="utf-8"/></head>
+<body>
+<style>
+.x {
+  opacity: 0 !important;
+}
+</style>
+<object id="test" class="x" type="application/x-test" width=200 height=200></object>
+</body>
+</html>
--- a/browser/base/content/test/social/Makefile.in
+++ b/browser/base/content/test/social/Makefile.in
@@ -24,18 +24,22 @@ include $(DEPTH)/config/autoconf.mk
                  social_share_image.png \
                  social_sidebar.html \
                  social_chat.html \
                  social_flyout.html \
                  social_window.html \
                  social_worker.js \
                  $(NULL)
 
-ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
 _BROWSER_FILES += \
-                browser_social.js \
+                browser_social_perwindowPB.js \
+                $(NULL)
+else
+_BROWSER_FILES += \
+                browser_social_globalPB.js \
                 $(NULL)
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
deleted file mode 100644
--- a/browser/base/content/test/social/browser_social.js
+++ /dev/null
@@ -1,147 +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/. */
-
-// a place for miscellaneous social tests
-
-
-const pb = Cc["@mozilla.org/privatebrowsing;1"].
-           getService(Ci.nsIPrivateBrowsingService);
-
-function waitForPortMessage(port, topic, callback) {
-  port.onmessage = function(evt) {
-    if (evt.data.topic == topic)
-      callback(evt.data);
-  }
-}
-
-function portClosed(port) {
-  try {
-    port.postMessage({topic: "ping"});
-    return false; // worked - port can't be closed!
-  } catch (ex) {
-    return true;
-  }
-}
-
-function test() {
-  waitForExplicitFinish();
-
-  let manifest = { // normal provider
-    name: "provider 1",
-    origin: "https://example.com",
-    sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
-    workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
-    iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
-  };
-  runSocialTestWithProvider(manifest, function (finishcb) {
-    runSocialTests(tests, undefined, undefined, finishcb);
-  });
-}
-
-var tests = {
-  testPrivateBrowsing: function(next) {
-    let port = Social.provider.getWorkerPort();
-    ok(port, "provider has a port");
-    port.postMessage({topic: "test-init"});
-    port.onmessage = function (e) {
-      let topic = e.data.topic;
-      switch (topic) {
-        case "got-sidebar-message":
-          ok(true, "got sidebar message");
-          port.close();
-          togglePrivateBrowsing(function () {
-            ok(!Social.enabled, "Social shuts down during private browsing");
-            togglePrivateBrowsing(function () {
-              ok(Social.provider.getWorkerPort(), "port still obtainable after PB")
-              ok(Social.enabled, "Social enabled after private browsing");
-              next();
-            });
-          });
-          break;
-      }
-    };
-  },
-
-  testPrivateBrowsingSocialDisabled: function(next) {
-    // test PB from the perspective of entering PB without social enabled
-    // we expect social to be enabled at the start of the test, we need
-    // to disable it before testing PB transitions.
-    ok(Social.enabled, "social is still enabled");
-    let port = Social.provider.getWorkerPort();
-    ok(port, "provider has a port");
-    port.postMessage({topic: "test-init"});
-    port.onmessage = function (e) {
-      let topic = e.data.topic;
-      switch (topic) {
-        case "got-sidebar-message":
-          ok(true, "got sidebar message");
-          port.close();
-          Social.enabled = false;
-          break;
-      }
-    }
-
-    // wait for disable, then do some pb toggling. We expect social to remain
-    // disabled through these tests
-    Services.obs.addObserver(function observer(aSubject, aTopic) {
-      Services.obs.removeObserver(observer, aTopic);
-      ok(!Social.enabled, "Social is not enabled");
-      togglePrivateBrowsing(function () {
-        ok(!Social.enabled, "Social not available during private browsing");
-        togglePrivateBrowsing(function () {
-          ok(!Social.enabled, "Social is not enabled after private browsing");
-          // reenable social for next social test
-          Social.enabled = true;
-          next();
-        });
-      });
-    }, "social:pref-changed", false);
-  },
-
-  testPrivateBrowsingExitReloads: function(next) {
-    let port = Social.provider.getWorkerPort();
-    waitForPortMessage(port, "got-sidebar-message", function(data) {
-      ok(!portClosed(port), "port not closed before PB transition");
-      togglePrivateBrowsing(function () {
-        ok(!Social.enabled, "Social shuts down during private browsing");
-        // check the port we had before is dead.
-        ok(portClosed(port), "port closed after PB transition");
-        // enable it and stick a value in the window object.
-        Social.enabled = true;
-        port = Social.provider.getWorkerPort();
-        waitForPortMessage(port, "got-sidebar-message", function(data) {
-          // now just stuff a value in the sidebar - it should end
-          // up being removed when we leave PB mode via the sidebar
-          // being reloaded.
-          let sbw = document.getElementById("social-sidebar-browser").contentWindow;
-          sbw.wrappedJSObject.foo = "bar";
-          // Now toggle PB mode back to off.
-          togglePrivateBrowsing(function () {
-            ok(Social.enabled, "Social still enabled after leaving private browsing");
-            ok(portClosed(port), "port closed after PB transition");
-            port = Social.provider.getWorkerPort();
-            waitForPortMessage(port, "got-sidebar-message", function() {
-              sbw = document.getElementById("social-sidebar-browser").contentWindow;
-              is(sbw.wrappedJSObject.foo, undefined, "should have lost window variable when exiting")
-              next();
-            });
-            port.postMessage({topic: "test-init"});
-          });
-        });
-        port.postMessage({topic: "test-init"});
-      });
-    });
-    port.postMessage({topic: "test-init"});
-  },
-
-}
-
-function togglePrivateBrowsing(aCallback) {
-  Services.obs.addObserver(function observe(subject, topic, data) {
-    Services.obs.removeObserver(observe, topic);
-    executeSoon(aCallback);
-  }, "private-browsing-transition-complete", false);
-
-  pb.privateBrowsingEnabled = !pb.privateBrowsingEnabled;
-}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/social/browser_social_globalPB.js
@@ -0,0 +1,147 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// a place for miscellaneous social tests
+
+
+const pb = Cc["@mozilla.org/privatebrowsing;1"].
+           getService(Ci.nsIPrivateBrowsingService);
+
+function waitForPortMessage(port, topic, callback) {
+  port.onmessage = function(evt) {
+    if (evt.data.topic == topic)
+      callback(evt.data);
+  }
+}
+
+function portClosed(port) {
+  try {
+    port.postMessage({topic: "ping"});
+    return false; // worked - port can't be closed!
+  } catch (ex) {
+    return true;
+  }
+}
+
+function test() {
+  waitForExplicitFinish();
+
+  let manifest = { // normal provider
+    name: "provider 1",
+    origin: "https://example.com",
+    sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
+    workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
+    iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
+  };
+  runSocialTestWithProvider(manifest, function (finishcb) {
+    runSocialTests(tests, undefined, undefined, finishcb);
+  });
+}
+
+var tests = {
+  testPrivateBrowsing: function(next) {
+    let port = Social.provider.getWorkerPort();
+    ok(port, "provider has a port");
+    port.postMessage({topic: "test-init"});
+    port.onmessage = function (e) {
+      let topic = e.data.topic;
+      switch (topic) {
+        case "got-sidebar-message":
+          ok(true, "got sidebar message");
+          port.close();
+          togglePrivateBrowsing(function () {
+            ok(!Social.enabled, "Social shuts down during private browsing");
+            togglePrivateBrowsing(function () {
+              ok(Social.provider.getWorkerPort(), "port still obtainable after PB")
+              ok(Social.enabled, "Social enabled after private browsing");
+              next();
+            });
+          });
+          break;
+      }
+    };
+  },
+
+  testPrivateBrowsingSocialDisabled: function(next) {
+    // test PB from the perspective of entering PB without social enabled
+    // we expect social to be enabled at the start of the test, we need
+    // to disable it before testing PB transitions.
+    ok(Social.enabled, "social is still enabled");
+    let port = Social.provider.getWorkerPort();
+    ok(port, "provider has a port");
+    port.postMessage({topic: "test-init"});
+    port.onmessage = function (e) {
+      let topic = e.data.topic;
+      switch (topic) {
+        case "got-sidebar-message":
+          ok(true, "got sidebar message");
+          port.close();
+          Social.enabled = false;
+          break;
+      }
+    }
+
+    // wait for disable, then do some pb toggling. We expect social to remain
+    // disabled through these tests
+    Services.obs.addObserver(function observer(aSubject, aTopic) {
+      Services.obs.removeObserver(observer, aTopic);
+      ok(!Social.enabled, "Social is not enabled");
+      togglePrivateBrowsing(function () {
+        ok(!Social.enabled, "Social not available during private browsing");
+        togglePrivateBrowsing(function () {
+          ok(!Social.enabled, "Social is not enabled after private browsing");
+          // reenable social for next social test
+          Social.enabled = true;
+          next();
+        });
+      });
+    }, "social:pref-changed", false);
+  },
+
+  testPrivateBrowsingExitReloads: function(next) {
+    let port = Social.provider.getWorkerPort();
+    waitForPortMessage(port, "got-sidebar-message", function(data) {
+      ok(!portClosed(port), "port not closed before PB transition");
+      togglePrivateBrowsing(function () {
+        ok(!Social.enabled, "Social shuts down during private browsing");
+        // check the port we had before is dead.
+        ok(portClosed(port), "port closed after PB transition");
+        // enable it and stick a value in the window object.
+        Social.enabled = true;
+        port = Social.provider.getWorkerPort();
+        waitForPortMessage(port, "got-sidebar-message", function(data) {
+          // now just stuff a value in the sidebar - it should end
+          // up being removed when we leave PB mode via the sidebar
+          // being reloaded.
+          let sbw = document.getElementById("social-sidebar-browser").contentWindow;
+          sbw.wrappedJSObject.foo = "bar";
+          // Now toggle PB mode back to off.
+          togglePrivateBrowsing(function () {
+            ok(Social.enabled, "Social still enabled after leaving private browsing");
+            ok(portClosed(port), "port closed after PB transition");
+            port = Social.provider.getWorkerPort();
+            waitForPortMessage(port, "got-sidebar-message", function() {
+              sbw = document.getElementById("social-sidebar-browser").contentWindow;
+              is(sbw.wrappedJSObject.foo, undefined, "should have lost window variable when exiting")
+              next();
+            });
+            port.postMessage({topic: "test-init"});
+          });
+        });
+        port.postMessage({topic: "test-init"});
+      });
+    });
+    port.postMessage({topic: "test-init"});
+  },
+
+}
+
+function togglePrivateBrowsing(aCallback) {
+  Services.obs.addObserver(function observe(subject, topic, data) {
+    Services.obs.removeObserver(observe, topic);
+    executeSoon(aCallback);
+  }, "private-browsing-transition-complete", false);
+
+  pb.privateBrowsingEnabled = !pb.privateBrowsingEnabled;
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/social/browser_social_perwindowPB.js
@@ -0,0 +1,82 @@
+/* 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/. */
+
+function openTab(win, url, callback) {
+  let newTab = win.gBrowser.addTab(url);
+  let tabBrowser = win.gBrowser.getBrowserForTab(newTab);
+  tabBrowser.addEventListener("load", function tabLoadListener() {
+    tabBrowser.removeEventListener("load", tabLoadListener, true);
+    win.gBrowser.selectedTab = newTab;
+    callback(newTab);
+  }, true)
+}
+
+// Tests for per-window private browsing.
+function openPBWindow(callback) {
+  let w = OpenBrowserWindow({private: true});
+  w.addEventListener("load", function loadListener() {
+    w.removeEventListener("load", loadListener);
+    openTab(w, "http://example.com", function() {
+      callback(w);
+    });
+  });
+}
+
+function postAndReceive(port, postTopic, receiveTopic, callback) {
+  port.onmessage = function(e) {
+    if (e.data.topic == receiveTopic)
+      callback();
+  }
+  port.postMessage({topic: postTopic});
+}
+
+function test() {
+  waitForExplicitFinish();
+
+  let manifest = { // normal provider
+    name: "provider 1",
+    origin: "https://example.com",
+    sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
+    workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
+    iconURL: "https://example.com/browser/browser/base/content/test/social/moz.png"
+  };
+  runSocialTestWithProvider(manifest, function (finishcb) {
+    openTab(window, "http://example.com", function(newTab) {
+      runSocialTests(tests, undefined, undefined, function() {
+        window.gBrowser.removeTab(newTab);
+        finishcb();
+      });
+    });
+  });
+}
+
+var tests = {
+  testPrivateBrowsing: function(next) {
+    let port = Social.provider.getWorkerPort();
+    ok(port, "provider has a port");
+    postAndReceive(port, "test-init", "test-init-done", function() {
+      // social features should all be enabled in the existing window.
+      info("checking main window ui");
+      ok(window.SocialUI.enabled, "social is enabled in normal window");
+      checkSocialUI(window);
+      // open a new private-window
+      openPBWindow(function(pbwin) {
+        // The provider should remain alive.
+        postAndReceive(port, "ping", "pong", function() {
+          // the new window should have no social features at all.
+          info("checking private window ui");
+          ok(!pbwin.SocialUI.enabled, "social is disabled in a PB window");
+          checkSocialUI(pbwin);
+          // but they should all remain enabled in the initial window
+          info("checking main window ui");
+          ok(window.SocialUI.enabled, "social is still enabled in normal window");
+          checkSocialUI(window);
+          // that's all folks...
+          pbwin.close();
+          next();
+        })
+      });
+    });
+  },
+}
--- a/browser/base/content/test/social/head.js
+++ b/browser/base/content/test/social/head.js
@@ -143,8 +143,41 @@ function runSocialTests(tests, cbPreTest
           ok(false, "sub-test " + name + " failed: " + ex.toString() +"\n"+ex.stack);
           cleanupAndRunNextTest();
         }
       })
     });
   }
   runNextTest();
 }
+
+// A fairly large hammer which checks all aspects of the SocialUI for
+// internal consistency.
+function checkSocialUI(win) {
+  let win = win || window;
+  let doc = win.document;
+  let provider = Social.provider;
+  let enabled = win.SocialUI.enabled;
+  function isbool(a, b, msg) {
+    is(!!a, !!b, msg);
+  }
+  isbool(win.SocialSidebar.canShow, enabled, "social sidebar active?");
+  if (enabled)
+    isbool(win.SocialSidebar.opened, enabled, "social sidebar open?");
+  isbool(win.SocialChatBar.isAvailable, enabled && Social.haveLoggedInUser(), "chatbar available?");
+  isbool(!win.SocialChatBar.chatbar.hidden, enabled && Social.haveLoggedInUser(), "chatbar visible?");
+  isbool(!win.SocialShareButton.shareButton.hidden, enabled && provider.recommendInfo, "share button visible?");
+  isbool(!doc.getElementById("social-toolbar-item").hidden, enabled, "toolbar items visible?");
+  if (enabled)
+    todo_is(win.SocialToolbar.button.style.listStyleImage, 'url("' + provider.iconURL + '")', "Bug 821262 - toolbar button has provider icon");
+
+  // and for good measure, check all the social commands.
+  // Social:Remove - never disabled directly but parent nodes are
+  isbool(!doc.getElementById("Social:Toggle").hidden, enabled, "Social:Toggle visible?");
+  // Until bug 821262 is fixed, ToggleNotifications might not be updated correctly...
+  // isbool(!doc.getElementById("Social:ToggleNotifications").hidden, enabled, "Bug 821262 - Social:ToggleNotifications visible?");
+  isbool(!doc.getElementById("Social:FocusChat").hidden, enabled && Social.haveLoggedInUser(), "Social:FocusChat visible?");
+  isbool(doc.getElementById("Social:FocusChat").getAttribute("disabled"), enabled ? "false" : "true", "Social:FocusChat disabled?");
+  is(doc.getElementById("Social:SharePage").getAttribute("disabled"), enabled && provider.recommendInfo ? "false" : "true", "Social:SharePage visible?");
+
+  // broadcasters.
+  isbool(!doc.getElementById("socialActiveBroadcaster").hidden, enabled, "socialActiveBroadcaster hidden?");
+}
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -523,18 +523,18 @@ const DownloadsPanel = {
         this._state = this.kStateHidden;
         return;
       }
 
       if (aAnchor) {
         this.panel.openPopup(aAnchor, "bottomcenter topright", 0, 0, false,
                              null);
       } else {
-        this.panel.openPopup(document.getElementById("TabsToolbar"),
-                             "after_end", 0, 0, false, null);
+        Components.utils.reportError(
+          "Downloads button cannot be found");
       }
     }.bind(this));
   }
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 //// DownloadsOverlayLoader
 
--- a/browser/components/places/content/controller.js
+++ b/browser/components/places/content/controller.js
@@ -1609,21 +1609,19 @@ let PlacesControllerDragHelper = {
 
 XPCOMUtils.defineLazyServiceGetter(PlacesControllerDragHelper, "dragService",
                                    "@mozilla.org/widget/dragservice;1",
                                    "nsIDragService");
 
 function goUpdatePlacesCommands() {
   // Get the controller for one of the places commands.
   var placesController = doGetPlacesControllerForCommand("placesCmd_open");
-  if (!placesController)
-    return;
-
   function updatePlacesCommand(aCommand) {
-    goSetCommandEnabled(aCommand, placesController.isCommandEnabled(aCommand));
+    goSetCommandEnabled(aCommand, placesController &&
+                                  placesController.isCommandEnabled(aCommand));
   }
 
   updatePlacesCommand("placesCmd_open");
   updatePlacesCommand("placesCmd_open:window");
   updatePlacesCommand("placesCmd_open:tab");
   updatePlacesCommand("placesCmd_new:folder");
   updatePlacesCommand("placesCmd_new:bookmark");
   updatePlacesCommand("placesCmd_new:livemark");
--- a/browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_downloadLastDir.js
+++ b/browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_downloadLastDir.js
@@ -14,17 +14,17 @@ function test() {
   let gDownloadLastDir = new downloadModule.DownloadLastDir(window);
 
   let launcher = {
     source: Services.io.newURI("http://test1.com/file", null, null)
   };
   let pb = Cc["@mozilla.org/privatebrowsing;1"].
            getService(Ci.nsIPrivateBrowsingService);
 
-  MockFilePicker.init();
+  MockFilePicker.init(window);
    MockFilePicker.returnValue = Ci.nsIFilePicker.returnOK;
 
   Services.prefs.setBoolPref("browser.privatebrowsing.keep_current_session", true);
   let prefs = Services.prefs.getBranch("browser.download.");
   let launcherDialog = Cc["@mozilla.org/helperapplauncherdialog;1"].
                        getService(Ci.nsIHelperAppLauncherDialog);
   let tmpDir = FileUtils.getDir("TmpD", [], true);
   function newDirectory() {
--- a/browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_downloadLastDir_c.js
+++ b/browser/components/privatebrowsing/test/browser/global/browser_privatebrowsing_downloadLastDir_c.js
@@ -11,17 +11,17 @@ function test() {
   let FileUtils = tmpScope.FileUtils;
   Cu.import("resource://gre/modules/Services.jsm");
   let MockFilePicker = SpecialPowers.MockFilePicker;
   let gDownloadLastDir = new downloadModule.DownloadLastDir(window);
 
   let pb = Cc["@mozilla.org/privatebrowsing;1"].
            getService(Ci.nsIPrivateBrowsingService);
 
-  MockFilePicker.init();
+  MockFilePicker.init(window);
   MockFilePicker.returnValue = Ci.nsIFilePicker.returnOK;
 
   //let stringBundleToRestore = ContentAreaUtils.stringBundle;
   let validateFileNameToRestore = validateFileName;
 
   Services.prefs.setBoolPref("browser.privatebrowsing.keep_current_session", true);
   let prefs = Services.prefs.getBranch("browser.download.");
   let tmpDir = FileUtils.getDir("TmpD", [], true);
--- a/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_downloadLastDir.js
+++ b/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_downloadLastDir.js
@@ -10,17 +10,17 @@ function test() {
     Cu.import("resource://gre/modules/FileUtils.jsm", {}).FileUtils;
   let DownloadLastDir =
     Cu.import("resource://gre/modules/DownloadLastDir.jsm", {}).DownloadLastDir;
   let MockFilePicker = SpecialPowers.MockFilePicker;
   let launcher = {
     source: Services.io.newURI("http://test1.com/file", null, null)
   };
 
-  MockFilePicker.init();
+  MockFilePicker.init(window);
   MockFilePicker.returnValue = Ci.nsIFilePicker.returnOK;
 
   let prefs = Services.prefs.getBranch("browser.download.");
   let launcherDialog =
     Cc["@mozilla.org/helperapplauncherdialog;1"].
     getService(Ci.nsIHelperAppLauncherDialog);
   let tmpDir = FileUtils.getDir("TmpD", [], true);
   let dir1 = newDirectory();
--- a/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_downloadLastDir_c.js
+++ b/browser/components/privatebrowsing/test/browser/perwindow/browser_privatebrowsing_downloadLastDir_c.js
@@ -7,17 +7,17 @@ function test() {
   waitForExplicitFinish();
 
   let FileUtils =
     Cu.import("resource://gre/modules/FileUtils.jsm", {}).FileUtils;
   let DownloadLastDir =
     Cu.import("resource://gre/modules/DownloadLastDir.jsm", {}).DownloadLastDir;
   let MockFilePicker = SpecialPowers.MockFilePicker;
 
-  MockFilePicker.init();
+  MockFilePicker.init(window);
   MockFilePicker.returnValue = Ci.nsIFilePicker.returnOK;
 
   let validateFileNameToRestore = validateFileName;
   let prefs = Services.prefs.getBranch("browser.download.");
   let tmpDir = FileUtils.getDir("TmpD", [], true);
   let dir1 = newDirectory();
   let dir2 = newDirectory();
   let dir3 = newDirectory();
--- a/browser/components/privatebrowsing/test/unit/xpcshell.ini
+++ b/browser/components/privatebrowsing/test/unit/xpcshell.ini
@@ -1,16 +1,15 @@
 [DEFAULT]
 head = head_privatebrowsing.js
 tail = tail_privatebrowsing.js
 
 [test_0-privatebrowsing.js]
 [test_0-privatebrowsingwrapper.js]
 [test_aboutprivatebrowsing.js]
-[test_httpauth.js]
 [test_placesTitleNoUpdate.js]
 [test_privatebrowsing_autostart.js]
 [test_privatebrowsing_commandline.js]
 [test_privatebrowsing_exit.js]
 [test_privatebrowsing_telemetry.js]
 [test_privatebrowsingwrapper_autostart.js]
 [test_privatebrowsingwrapper_commandline.js]
 [test_privatebrowsingwrapper_exit.js]
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -105,16 +105,20 @@ XPCOMUtils.defineLazyServiceGetter(this,
 #endif
 
 function debug(aMsg) {
   aMsg = ("SessionStore: " + aMsg).replace(/\S{80}/g, "$&\n");
   Services.console.logStringMessage(aMsg);
 }
 
 this.SessionStore = {
+  get promiseInitialized() {
+    return SessionStoreInternal.promiseInitialized.promise;
+  },
+
   get canRestoreLastSession() {
     return SessionStoreInternal.canRestoreLastSession;
   },
 
   set canRestoreLastSession(val) {
     SessionStoreInternal.canRestoreLastSession = val;
   },
 
@@ -307,16 +311,23 @@ let SessionStoreInternal = {
   // "sessionstore.resume_session_once" to true when the
   // the "sessionstore.resume_from_crash" preference is false (crash recovery
   // is disabled) so that pinned tabs will be restored in the case of a
   // crash.  This variable is used to restore the original value so the
   // previous session is not always restored when
   // "sessionstore.resume_from_crash" is true.
   _resume_session_once_on_shutdown: null,
 
+  /**
+   * A promise fulfilled once initialization is complete.
+   */
+  get promiseInitialized() {
+    return this._promiseInitialization;
+  },
+
   /* ........ Public Getters .............. */
   get canRestoreLastSession() {
 #ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
     return this._lastSessionState;
 #else
     // Always disallow restoring the previous session when in private browsing
     return this._lastSessionState && !this._inPrivateBrowsing;
 #endif
--- a/browser/components/sessionstore/test/Makefile.in
+++ b/browser/components/sessionstore/test/Makefile.in
@@ -9,17 +9,16 @@ VPATH		= @srcdir@
 relativesrcdir  = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 # browser_506482.js is disabled because of frequent failures (bug 538672)
 # browser_526613.js is disabled because of frequent failures (bug 534489)
 # browser_589246.js is disabled for leaking browser windows (bug 752467)
 # browser_580512.js is disabled for leaking browser windows (bug 752467)
-# browser_586068-reload.js is disabled due to generally being broken (bug 809123, 797263)
 
 XPCSHELL_TESTS = \
 	unit \
 	$(NULL)
 
 MOCHITEST_BROWSER_FILES = \
 	head.js \
 	browser_form_restore_events.js \
@@ -84,16 +83,17 @@ MOCHITEST_BROWSER_FILES = \
 	browser_581593.js \
 	browser_581937.js \
 	browser_586147.js \
 	browser_586068-apptabs.js \
 	browser_586068-apptabs_ondemand.js \
 	browser_586068-browser_state_interrupted.js \
 	browser_586068-cascade.js \
 	browser_586068-multi_window.js \
+	browser_586068-reload.js \
 	browser_586068-select.js \
 	browser_586068-window_state.js \
 	browser_586068-window_state_override.js \
 	browser_588426.js \
 	browser_590268.js \
 	browser_590563.js \
 	browser_595601-restore_hidden.js \
 	browser_597315.js \
--- a/browser/components/sessionstore/test/browser_586068-apptabs.js
+++ b/browser/components/sessionstore/test/browser_586068-apptabs.js
@@ -1,19 +1,19 @@
 /* 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 PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
 
-let stateBackup = ss.getBrowserState();
+function test() {
+  TestRunner.run();
+}
 
-function test() {
-  waitForExplicitFinish();
-
+function runTests() {
   Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, true);
   registerCleanupFunction(function () {
     Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
   });
 
   let state = { windows: [{ tabs: [
     { entries: [{ url: "http://example.org/#1" }], extData: { "uniq": r() }, pinned: true },
     { entries: [{ url: "http://example.org/#2" }], extData: { "uniq": r() }, pinned: true },
@@ -41,15 +41,13 @@ function test() {
     ok(tab.pinned || tab.selected,
        "load came from pinned or selected tab");
 
     // We should get 4 loads: 3 app tabs + 1 normal selected tab
     if (loadCount < 4)
       return;
 
     gProgressListener.unsetCallback();
-    executeSoon(function () {
-      waitForBrowserState(JSON.parse(stateBackup), finish);
-    });
+    executeSoon(next);
   });
 
-  ss.setBrowserState(JSON.stringify(state));
+  yield ss.setBrowserState(JSON.stringify(state));
 }
--- a/browser/components/sessionstore/test/browser_586068-apptabs_ondemand.js
+++ b/browser/components/sessionstore/test/browser_586068-apptabs_ondemand.js
@@ -1,20 +1,20 @@
 /* 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 PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
 const PREF_RESTORE_PINNED_TABS_ON_DEMAND = "browser.sessionstore.restore_pinned_tabs_on_demand";
 
-let stateBackup = ss.getBrowserState();
+function test() {
+  TestRunner.run();
+}
 
-function test() {
-  waitForExplicitFinish();
-
+function runTests() {
   Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, true);
   Services.prefs.setBoolPref(PREF_RESTORE_PINNED_TABS_ON_DEMAND, true);
 
   registerCleanupFunction(function () {
     Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
     Services.prefs.clearUserPref(PREF_RESTORE_PINNED_TABS_ON_DEMAND);
   });
 
@@ -38,15 +38,13 @@ function test() {
 
     // Check that the load only comes from the selected tab.
     ok(tab.selected, "load came from selected tab");
     is(aNeedRestore, 6, "six tabs left to restore");
     is(aRestoring, 1, "one tab is restoring");
     is(aRestored, 0, "no tabs have been restored, yet");
 
     gProgressListener.unsetCallback();
-    executeSoon(function () {
-      waitForBrowserState(JSON.parse(stateBackup), finish);
-    });
+    executeSoon(next);
   });
 
-  ss.setBrowserState(JSON.stringify(state));
+  yield ss.setBrowserState(JSON.stringify(state));
 }
--- a/browser/components/sessionstore/test/browser_586068-browser_state_interrupted.js
+++ b/browser/components/sessionstore/test/browser_586068-browser_state_interrupted.js
@@ -1,19 +1,19 @@
 /* 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 PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
 
-let stateBackup = ss.getBrowserState();
+function test() {
+  TestRunner.run();
+}
 
-function test() {
-  waitForExplicitFinish();
-
+function runTests() {
   Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, false);
   registerCleanupFunction(function () {
     Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
   });
 
   // The first state will be loaded using setBrowserState, followed by the 2nd
   // state also being loaded using setBrowserState, interrupting the first restore.
   let state1 = { windows: [
@@ -84,28 +84,25 @@ function test() {
     // We don't actually care about load order in this test, just that they all
     // do load.
     is(loadCount, numTabs + interruptedAfter, "all tabs were restored");
     is(aNeedRestore, 0, "there are no tabs left needing restore");
 
     // Remove the progress listener from this window, it will be removed from
     // theWin when that window is closed (in setBrowserState).
     gProgressListener.unsetCallback();
-    executeSoon(function () {
-      closeAllButPrimaryWindow();
-      waitForBrowserState(JSON.parse(stateBackup), finish);
-    });
+    executeSoon(next);
   });
 
   // We also want to catch the extra windows (there should be 2), so we need to observe domwindowopened
   Services.ww.registerNotification(function observer(aSubject, aTopic, aData) {
     if (aTopic == "domwindowopened") {
       let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
       win.addEventListener("load", function onLoad() {
         win.removeEventListener("load", onLoad);
         Services.ww.unregisterNotification(observer);
         win.gBrowser.addTabsProgressListener(gProgressListener);
       });
     }
   });
 
-  ss.setBrowserState(JSON.stringify(state1));
+  yield ss.setBrowserState(JSON.stringify(state1));
 }
--- a/browser/components/sessionstore/test/browser_586068-cascade.js
+++ b/browser/components/sessionstore/test/browser_586068-cascade.js
@@ -1,19 +1,19 @@
 /* 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 PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
 
-let stateBackup = ss.getBrowserState();
+function test() {
+  TestRunner.run();
+}
 
-function test() {
-  waitForExplicitFinish();
-
+function runTests() {
   Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, false);
   registerCleanupFunction(function () {
     Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
   });
 
   let state = { windows: [{ tabs: [
     { entries: [{ url: "http://example.com" }], extData: { "uniq": r() } },
     { entries: [{ url: "http://example.com" }], extData: { "uniq": r() } },
@@ -38,16 +38,14 @@ function test() {
     let expected = expectedCounts[loadCount - 1];
 
     is(aNeedRestore, expected[0], "load " + loadCount + " - # tabs that need to be restored");
     is(aRestoring, expected[1], "load " + loadCount + " - # tabs that are restoring");
     is(aRestored, expected[2], "load " + loadCount + " - # tabs that has been restored");
 
     if (loadCount == state.windows[0].tabs.length) {
       gProgressListener.unsetCallback();
-      executeSoon(function () {
-        waitForBrowserState(JSON.parse(stateBackup), finish);
-      });
+      executeSoon(next);
     }
   });
 
-  ss.setBrowserState(JSON.stringify(state));
+  yield ss.setBrowserState(JSON.stringify(state));
 }
--- a/browser/components/sessionstore/test/browser_586068-multi_window.js
+++ b/browser/components/sessionstore/test/browser_586068-multi_window.js
@@ -1,19 +1,19 @@
 /* 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 PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
 
-let stateBackup = ss.getBrowserState();
+function test() {
+  TestRunner.run();
+}
 
-function test() {
-  waitForExplicitFinish();
-
+function runTests() {
   Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, false);
   registerCleanupFunction(function () {
     Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
   });
 
   // The first window will be put into the already open window and the second
   // window will be opened with _openWindowWithState, which is the source of the problem.
   let state = { windows: [
@@ -41,29 +41,26 @@ function test() {
   gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
     if (++loadCount == numTabs) {
       // We don't actually care about load order in this test, just that they all
       // do load.
       is(loadCount, numTabs, "all tabs were restored");
       is(aNeedRestore, 0, "there are no tabs left needing restore");
 
       gProgressListener.unsetCallback();
-      executeSoon(function () {
-        closeAllButPrimaryWindow();
-        waitForBrowserState(JSON.parse(stateBackup), finish);
-      });
+      executeSoon(next);
     }
   });
 
   // We also want to catch the 2nd window, so we need to observe domwindowopened
   Services.ww.registerNotification(function observer(aSubject, aTopic, aData) {
     if (aTopic == "domwindowopened") {
       let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
       win.addEventListener("load", function onLoad() {
         win.removeEventListener("load", onLoad);
         Services.ww.unregisterNotification(observer);
         win.gBrowser.addTabsProgressListener(gProgressListener);
       });
     }
   });
 
-  ss.setBrowserState(JSON.stringify(state));
+  yield ss.setBrowserState(JSON.stringify(state));
 }
--- a/browser/components/sessionstore/test/browser_586068-reload.js
+++ b/browser/components/sessionstore/test/browser_586068-reload.js
@@ -1,18 +1,23 @@
 /* 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 PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
 
-let stateBackup = ss.getBrowserState();
+function test() {
+  TestRunner.run();
+}
 
-function test() {
-  waitForExplicitFinish();
+function runTests() {
+  // Request a longer timeout because the test takes quite a while
+  // to complete on slow Windows debug machines and we would otherwise
+  // see a lot of (not so) intermittent test failures.
+  requestLongerTimeout(2);
 
   Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, true);
   registerCleanupFunction(function () {
     Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
   });
 
   let state = { windows: [{ tabs: [
     { entries: [{ url: "http://example.org/#1" }], extData: { "uniq": r() } },
@@ -51,27 +56,27 @@ function test() {
       }
       is(ss.getTabValue(tab, "uniq"), expectedData,
          "load " + loadCount + " - correct tab was restored");
 
       if (loadCount == state.windows[0].tabs.length) {
         gProgressListener.unsetCallback();
         executeSoon(function () {
           reloadAllTabs(state, function () {
-            waitForBrowserState(JSON.parse(stateBackup), testCascade);
+            waitForBrowserState(TestRunner.backupState, testCascade);
           });
         });
       } else {
         // reload the next tab
         window.gBrowser.reloadTab(window.gBrowser.tabs[loadCount]);
       }
     }
   });
 
-  ss.setBrowserState(JSON.stringify(state));
+  yield ss.setBrowserState(JSON.stringify(state));
 }
 
 function testCascade() {
   Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, false);
 
   let state = { windows: [{ tabs: [
     { entries: [{ url: "http://example.com/#1" }], extData: { "uniq": r() } },
     { entries: [{ url: "http://example.com/#2" }], extData: { "uniq": r() } },
@@ -84,19 +89,17 @@ function testCascade() {
   let loadCount = 0;
   gProgressListener.setCallback(function (aBrowser, aNeedRestore, aRestoring, aRestored) {
     if (++loadCount < state.windows[0].tabs.length) {
       return;
     }
 
     gProgressListener.unsetCallback();
     executeSoon(function () {
-      reloadAllTabs(state, function () {
-        waitForBrowserState(JSON.parse(stateBackup), finish);
-      });
+      reloadAllTabs(state, next);
     });
   });
 
   ss.setBrowserState(JSON.stringify(state));
 }
 
 function reloadAllTabs(aState, aCallback) {
   // Simulate a left mouse button click with no modifiers, which is what
--- a/browser/components/sessionstore/test/browser_586068-select.js
+++ b/browser/components/sessionstore/test/browser_586068-select.js
@@ -1,19 +1,19 @@
 /* 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 PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
 
-let stateBackup = ss.getBrowserState();
+function test() {
+  TestRunner.run();
+}
 
-function test() {
-  waitForExplicitFinish();
-
+function runTests() {
   Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, true);
   registerCleanupFunction(function () {
     Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
   });
 
   let state = { windows: [{ tabs: [
     { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
     { entries: [{ url: "http://example.org" }], extData: { "uniq": r() } },
@@ -53,16 +53,14 @@ function test() {
 
       is(ss.getTabValue(tab, "uniq"), expectedData,
         "load " + loadCount + " - correct tab was restored");
 
       // select the next tab
       window.gBrowser.selectTabAtIndex(tabOrder[loadCount]);
     } else {
       gProgressListener.unsetCallback();
-      executeSoon(function () {
-        waitForBrowserState(JSON.parse(stateBackup), finish);
-      });
+      executeSoon(next);
     }
   });
 
-  ss.setBrowserState(JSON.stringify(state));
+  yield ss.setBrowserState(JSON.stringify(state));
 }
--- a/browser/components/sessionstore/test/browser_586068-window_state.js
+++ b/browser/components/sessionstore/test/browser_586068-window_state.js
@@ -1,19 +1,19 @@
 /* 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 PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
 
-let stateBackup = ss.getBrowserState();
+function test() {
+  TestRunner.run();
+}
 
-function test() {
-  waitForExplicitFinish();
-
+function runTests() {
   Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, false);
   registerCleanupFunction(function () {
     Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
   });
 
   // We'll use 2 states so that we can make sure calling setWindowState doesn't
   // wipe out currently restoring data.
   let state1 = { windows: [{ tabs: [
@@ -48,15 +48,13 @@ function test() {
     is(loadCount, numTabs, "test_setWindowStateNoOverwrite: all tabs were restored");
     // window.__SS_tabsToRestore isn't decremented until after the progress
     // listener is called. Since we get in here before that, we still expect
     // the count to be 1.
     is(window.__SS_tabsToRestore, 1, "window doesn't think there are more tabs to restore");
     is(aNeedRestore, 0, "there are no tabs left needing restore");
 
     gProgressListener.unsetCallback();
-    executeSoon(function () {
-      waitForBrowserState(JSON.parse(stateBackup), finish);
-    });
+    executeSoon(next);
   });
 
-  ss.setWindowState(window, JSON.stringify(state1), true);
+  yield ss.setWindowState(window, JSON.stringify(state1), true);
 }
--- a/browser/components/sessionstore/test/browser_586068-window_state_override.js
+++ b/browser/components/sessionstore/test/browser_586068-window_state_override.js
@@ -1,19 +1,19 @@
 /* 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 PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
 
-let stateBackup = ss.getBrowserState();
+function test() {
+  TestRunner.run();
+}
 
-function test() {
-  waitForExplicitFinish();
-
+function runTests() {
   Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, false);
   registerCleanupFunction(function () {
     Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
   });
 
   // We'll use 2 states so that we can make sure calling setWindowState doesn't
   // wipe out currently restoring data.
   let state1 = { windows: [{ tabs: [
@@ -48,15 +48,13 @@ function test() {
     is(loadCount, numTabs, "all tabs were restored");
     // window.__SS_tabsToRestore isn't decremented until after the progress
     // listener is called. Since we get in here before that, we still expect
     // the count to be 1.
     is(window.__SS_tabsToRestore, 1, "window doesn't think there are more tabs to restore");
     is(aNeedRestore, 0, "there are no tabs left needing restore");
 
     gProgressListener.unsetCallback();
-    executeSoon(function () {
-      waitForBrowserState(JSON.parse(stateBackup), finish);
-    });
+    executeSoon(next);
   });
 
-  ss.setWindowState(window, JSON.stringify(state1), true);
+  yield ss.setWindowState(window, JSON.stringify(state1), true);
 }
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -1,15 +1,19 @@
 /* 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 TAB_STATE_NEEDS_RESTORE = 1;
 const TAB_STATE_RESTORING = 2;
 
+let tmp = {};
+Cu.import("resource:///modules/sessionstore/SessionStore.jsm", tmp);
+let SessionStore = tmp.SessionStore;
+
 let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
 
 // Some tests here assume that all restored tabs are loaded without waiting for
 // the user to bring them to the foreground. We ensure this by resetting the
 // related preference (see the "firefox.js" defaults file for details).
 Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", false);
 registerCleanupFunction(function () {
   Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
@@ -280,8 +284,59 @@ function closeAllButPrimaryWindow() {
 
 function whenNewWindowLoaded(aIsPrivate, aCallback) {
   let win = OpenBrowserWindow({private: aIsPrivate});
   win.addEventListener("load", function onLoad() {
     win.removeEventListener("load", onLoad, false);
     aCallback(win);
   }, false);
 }
+
+/**
+ * The test runner that controls the execution flow of our tests.
+ */
+let TestRunner = {
+  _iter: null,
+
+  /**
+   * Holds the browser state from before we started so
+   * that we can restore it after all tests ran.
+   */
+  backupState: {},
+
+  /**
+   * Starts the test runner.
+   */
+  run: function () {
+    waitForExplicitFinish();
+
+    SessionStore.promiseInitialized.then(function () {
+      executeSoon(function () {
+        this.backupState = JSON.parse(ss.getBrowserState());
+        this._iter = runTests();
+        this.next();
+      }.bind(this));
+    }.bind(this));
+  },
+
+  /**
+   * Runs the next available test or finishes if there's no test left.
+   */
+  next: function () {
+    try {
+      TestRunner._iter.next();
+    } catch (e if e instanceof StopIteration) {
+      TestRunner.finish();
+    }
+  },
+
+  /**
+   * Finishes all tests and cleans up.
+   */
+  finish: function () {
+    closeAllButPrimaryWindow();
+    waitForBrowserState(this.backupState, finish);
+  }
+};
+
+function next() {
+  TestRunner.next();
+}
--- a/browser/config/mozconfigs/linux32/valgrind
+++ b/browser/config/mozconfigs/linux32/valgrind
@@ -1,7 +1,11 @@
 . $topsrcdir/browser/config/mozconfigs/linux32/nightly
 
 ac_add_options --enable-valgrind
 ac_add_options --disable-jemalloc
 ac_add_options --disable-elf-hack
 ac_add_options --enable-optimize="-g -O -freorder-blocks"
 ac_add_options --disable-install-strip
+
+# Include the override mozconfig again (even though the above includes it)
+# since it's supposed to override everything.
+. "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/linux64/valgrind
+++ b/browser/config/mozconfigs/linux64/valgrind
@@ -1,7 +1,11 @@
 . $topsrcdir/browser/config/mozconfigs/linux64/nightly
 
 ac_add_options --enable-valgrind
 ac_add_options --disable-jemalloc
 ac_add_options --disable-elf-hack
 ac_add_options --enable-optimize="-g -O -freorder-blocks"
 ac_add_options --disable-install-strip
+
+# Include the override mozconfig again (even though the above includes it)
+# since it's supposed to override everything.
+. "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/mozconfigs/win64/debug
+++ b/browser/config/mozconfigs/win64/debug
@@ -1,8 +1,10 @@
+. "$topsrcdir/browser/config/mozconfigs/common"
+
 ac_add_options --target=x86_64-pc-mingw32
 ac_add_options --host=x86_64-pc-mingw32
 
 ac_add_options --enable-debug
 ac_add_options --enable-trace-malloc
 ac_add_options --enable-signmar
 ENABLE_MARIONETTE=1
 
--- a/browser/config/mozconfigs/win64/nightly
+++ b/browser/config/mozconfigs/win64/nightly
@@ -1,8 +1,10 @@
+. "$topsrcdir/browser/config/mozconfigs/common"
+
 ac_add_options --target=x86_64-pc-mingw32
 ac_add_options --host=x86_64-pc-mingw32
 
 # for pgo
 mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) $(MOZ_OBJDIR)/_profile/pgo/profileserver.py'
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
--- a/browser/devtools/commandline/BuiltinCommands.jsm
+++ b/browser/devtools/commandline/BuiltinCommands.jsm
@@ -1255,16 +1255,51 @@ XPCOMUtils.defineLazyModuleGetter(this, 
       let thread = controller.activeThread;
       if (thread.paused) {
         thread.stepOut();
       }
     }
   });
 
   /**
+  * 'dbg list' command
+  */
+  gcli.addCommand({
+    name: "dbg list",
+    description: gcli.lookup("dbgListSourcesDesc"),
+    params: [],
+    returnType: "html",
+    exec: function(args, context) {
+      let dbg = getPanel(context, "jsdebugger");
+      let doc = context.environment.chromeDocument;
+      if (!dbg) {
+        return gcli.lookup("debuggerClosed");
+      }
+      let sources = dbg._view.Sources.values;
+      let div = createXHTMLElement(doc, "div");
+      let ol = createXHTMLElement(doc, "ol");
+      sources.forEach(function(src) {
+        let li = createXHTMLElement(doc, "li");
+        li.textContent = src;
+        ol.appendChild(li);
+      });
+      div.appendChild(ol);
+
+      return div;
+    }
+  });
+
+  /**
+  * A helper to create xhtml namespaced elements
+  */
+  function createXHTMLElement(document, tagname) {
+    return document.createElementNS("http://www.w3.org/1999/xhtml", tagname);
+  }
+
+  /**
   * A helper to go from a command context to a debugger panel
   */
   function getPanel(context, id) {
     let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
     let target = TargetFactory.forTab(gBrowser.selectedTab);
     let toolbox = gDevTools.getToolbox(target);
     return toolbox == null ? undefined : toolbox.getPanel(id);
   }
--- a/browser/devtools/commandline/test/Makefile.in
+++ b/browser/devtools/commandline/test/Makefile.in
@@ -7,19 +7,21 @@ DEPTH     = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 relativesrcdir  = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_BROWSER_FILES = \
+  browser_cmd_addon.js \
+  browser_cmd_calllog.js \
+  browser_cmd_calllog_chrome.js \
   browser_dbg_cmd_break.html \
   browser_dbg_cmd_break.js \
-  browser_cmd_addon.js \
   browser_cmd_commands.js \
   browser_cmd_cookie.js \
   browser_cmd_integrate.js \
   browser_cmd_jsb.js \
   browser_cmd_jsb_script.jsi \
   browser_cmd_pagemod_export.html \
   browser_cmd_pagemod_export.js \
   browser_cmd_pref.js \
@@ -62,18 +64,14 @@ MOCHITEST_BROWSER_FILES += \
  browser_cmd_screenshot.js \
  $(NULL)
 endif
 
 ifneq ($(OS_ARCH),WINNT)
 MOCHITEST_BROWSER_FILES += \
   browser_dbg_cmd.html \
   browser_dbg_cmd.js \
-  browser_cmd_calllog.js \
-  browser_cmd_calllog_chrome.js \
   $(NULL)
 else
 $(filter disabled-temporarily--bug-820221, browser_dbg_cmd.js)
-$(filter disabled-temporarily--bug-817304, browser_cmd_calllog.js)
-$(filter disabled-temporarily--bug-819017, browser_cmd_calllog_chrome.js)
 endif
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/devtools/commandline/test/browser_dbg_cmd.js
+++ b/browser/devtools/commandline/test/browser_dbg_cmd.js
@@ -13,22 +13,27 @@ function test() {
   DeveloperToolbarTest.test(TEST_URI, function() {
     testDbgCmd();
   });
 }
 
 function testCommands(dbg, cmd) {
   // Wait for the initial resume...
   dbg._controller.activeThread.addOneTimeListener("resumed", function () {
-    info("Starting tests.");
+    info("Starting tests");
 
     let contentDoc = content.window.document;
     let output = contentDoc.querySelector("input[type=text]");
     let btnDoit = contentDoc.querySelector("input[type=button]");
 
+    DeveloperToolbarTest.exec({
+      typed: "dbg list",
+      outputMatch: /browser_dbg_cmd.html/
+    });
+
     cmd("dbg interrupt", function() {
       ok(true, "debugger is paused");
       dbg._controller.activeThread.addOneTimeListener("resumed", function () {
         ok(true, "debugger continued");
         dbg._controller.activeThread.addOneTimeListener("paused", function() {
           cmd("dbg step in", function() {
             cmd("dbg step in", function() {
               cmd("dbg step in", function() {
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -10,16 +10,27 @@ const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
 const NEW_SCRIPT_IGNORED_URLS = ["debugger eval code", "self-hosted"];
 const NEW_SCRIPT_DISPLAY_DELAY = 200; // ms
 const FETCH_SOURCE_RESPONSE_DELAY = 50; // ms
 const FRAME_STEP_CLEAR_DELAY = 100; // ms
 const CALL_STACK_PAGE_SIZE = 25; // frames
+const VARIABLES_VIEW_NON_SORTABLE = [
+  "Array",
+  "Int8Array",
+  "Uint8Array",
+  "Int16Array",
+  "Uint16Array",
+  "Int32Array",
+  "Uint32Array",
+  "Float32Array",
+  "Float64Array"
+];
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
 Cu.import("resource:///modules/source-editor.jsm");
 Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource:///modules/devtools/VariablesView.jsm");
@@ -45,17 +56,16 @@ let DebuggerController = {
   /**
    * Initializes the view and connects a debugger client to the server.
    */
   _startupDebugger: function DC__startupDebugger() {
     if (this._isInitialized) {
       return;
     }
     this._isInitialized = true;
-
     window.removeEventListener("load", this._startupDebugger, true);
 
     DebuggerView.initialize(function() {
       DebuggerView._isInitialized = true;
 
       window.dispatchEvent("Debugger:Loaded");
       this._connect();
     }.bind(this));
@@ -360,16 +370,17 @@ ThreadState.prototype = {
   /**
    * Connect to the current thread client.
    */
   connect: function TS_connect() {
     dumpn("ThreadState is connecting...");
     this.activeThread.addListener("paused", this._update);
     this.activeThread.addListener("resumed", this._update);
     this.activeThread.addListener("detached", this._update);
+    this.activeThread.pauseOnExceptions(Prefs.pauseOnExceptions);
     this._handleTabNavigation();
   },
 
   /**
    * Disconnect from the client.
    */
   disconnect: function TS_disconnect() {
     if (!this.activeThread) {
@@ -645,38 +656,37 @@ StackFrames.prototype = {
     DebuggerView.Variables.createHierarchy();
     // Clear existing scopes and create each one dynamically.
     DebuggerView.Variables.empty();
 
     // If watch expressions evaluation results are available, create a scope
     // to contain all the values.
     if (this.syncedWatchExpressions && watchExpressionsEvaluation) {
       let label = L10N.getStr("watchExpressionsScopeLabel");
-      let arrow = L10N.getStr("watchExpressionsSeparatorLabel");
       let scope = DebuggerView.Variables.addScope(label);
-      scope.separator = arrow;
-      scope.showDescriptorTooltip = false;
-      scope.allowNameInput = true;
-      scope.allowDeletion = true;
-      scope.contextMenu = "debuggerWatchExpressionsContextMenu";
+
+      // Customize the scope for holding watch expressions evaluations.
+      scope.descriptorTooltip = false;
+      scope.contextMenuId = "debuggerWatchExpressionsContextMenu";
+      scope.separatorStr = L10N.getStr("watchExpressionsSeparatorLabel");
       scope.switch = DebuggerView.WatchExpressions.switchExpression;
       scope.delete = DebuggerView.WatchExpressions.deleteExpression;
 
       // The evaluation hasn't thrown, so display the returned results and
       // always expand the watch expressions scope by default.
       this._fetchWatchExpressions(scope, watchExpressionsEvaluation);
       scope.expand();
     }
 
     do {
       // Create a scope to contain all the inspected variables.
       let label = this._getScopeLabel(environment);
       let scope = DebuggerView.Variables.addScope(label);
 
-      // Special additions to the innermost scope.
+      // Handle additions to the innermost scope.
       if (environment == frame.environment) {
         this._insertScopeFrameReferences(scope, frame);
         this._fetchScopeVariables(scope, environment);
         // Always expand the innermost scope by default.
         scope.expand();
       }
       // Lazily add nodes for every other environment scope.
       else {
@@ -698,17 +708,17 @@ StackFrames.prototype = {
    *        The scope where the variables will be placed into.
    * @param object aEnv
    *        The scope's environment.
    */
   _addScopeExpander: function SF__addScopeExpander(aScope, aEnv) {
     let callback = this._fetchScopeVariables.bind(this, aScope, aEnv);
 
     // It's a good idea to be prepared in case of an expansion.
-    aScope.onmouseover = callback;
+    aScope.addEventListener("mouseover", callback, false);
     // Make sure that variables are always available on expansion.
     aScope.onexpand = callback;
   },
 
   /**
    * Adds an 'onexpand' callback for a variable, lazily handling
    * the addition of new properties.
    *
@@ -722,47 +732,53 @@ StackFrames.prototype = {
     if (VariablesView.isPrimitive({ value: aGrip })) {
       return;
     }
     let callback = this._fetchVarProperties.bind(this, aVar, aGrip);
 
     // Some variables are likely to contain a very large number of properties.
     // It's a good idea to be prepared in case of an expansion.
     if (aVar.name == "window" || aVar.name == "this") {
-      aVar.onmouseover = callback;
+      aVar.addEventListener("mouseover", callback, false);
     }
     // Make sure that properties are always available on expansion.
     aVar.onexpand = callback;
   },
 
   /**
    * Adds the watch expressions evaluation results to a scope in the view.
    *
    * @param Scope aScope
    *        The scope where the watch expressions will be placed into.
    * @param object aExp
    *        The grip of the evaluation results.
    */
   _fetchWatchExpressions: function SF__fetchWatchExpressions(aScope, aExp) {
-    // Retrieve the expressions only once.
-    if (aScope.fetched) {
+    // Fetch the expressions only once.
+    if (aScope._fetched) {
       return;
     }
-    aScope.fetched = true;
+    aScope._fetched = true;
 
     // Add nodes for every watch expression in scope.
     this.activeThread.pauseGrip(aExp).getPrototypeAndProperties(function(aResponse) {
       let ownProperties = aResponse.ownProperties;
       let totalExpressions = DebuggerView.WatchExpressions.totalItems;
 
       for (let i = 0; i < totalExpressions; i++) {
         let name = DebuggerView.WatchExpressions.getExpression(i);
         let expVal = ownProperties[i].value;
         let expRef = aScope.addVar(name, ownProperties[i]);
         this._addVarExpander(expRef, expVal);
+
+        // Revert some of the custom watch expressions scope presentation flags.
+        expRef.switch = null;
+        expRef.delete = null;
+        expRef.descriptorTooltip = true;
+        expRef.separatorStr = L10N.getStr("variablesSeparatorLabel");
       }
 
       // Signal that watch expressions have been fetched.
       window.dispatchEvent("Debugger:FetchedWatchExpressions");
       DebuggerView.Variables.commitHierarchy();
     }.bind(this));
   },
 
@@ -771,21 +787,21 @@ StackFrames.prototype = {
    * expanded or is hovered. It does not expand the scope.
    *
    * @param Scope aScope
    *        The scope where the variables will be placed into.
    * @param object aEnv
    *        The scope's environment.
    */
   _fetchScopeVariables: function SF__fetchScopeVariables(aScope, aEnv) {
-    // Retrieve the variables only once.
-    if (aScope.fetched) {
+    // Fetch the variables only once.
+    if (aScope._fetched) {
       return;
     }
-    aScope.fetched = true;
+    aScope._fetched = true;
 
     switch (aEnv.type) {
       case "with":
       case "object":
         // Add nodes for every variable in scope.
         this.activeThread.pauseGrip(aEnv.object).getPrototypeAndProperties(function(aResponse) {
           this._insertScopeVariables(aResponse.ownProperties, aScope);
 
@@ -794,16 +810,20 @@ StackFrames.prototype = {
           DebuggerView.Variables.commitHierarchy();
         }.bind(this));
         break;
       case "block":
       case "function":
         // Add nodes for every argument and every other variable in scope.
         this._insertScopeArguments(aEnv.bindings.arguments, aScope);
         this._insertScopeVariables(aEnv.bindings.variables, aScope);
+
+        // No need to signal that variables have been fetched, since
+        // the scope arguments and variables are already attached to the
+        // environment bindings, so pausing the active thread is unnecessary.
         break;
       default:
         Cu.reportError("Unknown Debugger.Environment type: " + aEnv.type);
         break;
     }
   },
 
   /**
@@ -856,21 +876,21 @@ StackFrames.prototype = {
    *        The scope where the nodes will be placed into.
    */
   _insertScopeVariables: function SF__insertScopeVariables(aVariables, aScope) {
     if (!aVariables) {
       return;
     }
     let variableNames = Object.keys(aVariables);
 
-    // Sort all of the variables before adding them if preferred.
+    // Sort all of the variables before adding them, if preferred.
     if (Prefs.variablesSortingEnabled) {
       variableNames.sort();
     }
-    // Add the sorted variables to the specified scope.
+    // Add the variables to the specified scope.
     for (let name of variableNames) {
       let varRef = aScope.addVar(name, aVariables[name]);
       let varVal = aVariables[name].value;
       this._addVarExpander(varRef, varVal);
     }
   },
 
   /**
@@ -878,41 +898,43 @@ StackFrames.prototype = {
    * expanded or certain variables are hovered. It does not expand the variable.
    *
    * @param Variable aVar
    *        The variable where the properties will be placed into.
    * @param any aGrip
    *        The grip of the variable.
    */
   _fetchVarProperties: function SF__fetchVarProperties(aVar, aGrip) {
-    // Retrieve the properties only once.
-    if (aVar.fetched) {
+    // Fetch the properties only once.
+    if (aVar._fetched) {
       return;
     }
-    aVar.fetched = true;
+    aVar._fetched = true;
 
     this.activeThread.pauseGrip(aGrip).getPrototypeAndProperties(function(aResponse) {
       let { ownProperties, prototype } = aResponse;
+      let sortable = VARIABLES_VIEW_NON_SORTABLE.indexOf(aGrip.class) == -1;
 
       // Add all the variable properties.
       if (ownProperties) {
-        aVar.addProperties(ownProperties);
+        aVar.addProperties(ownProperties, { sorted: sortable });
         // Expansion handlers must be set after the properties are added.
         for (let name in ownProperties) {
           this._addVarExpander(aVar.get(name), ownProperties[name].value);
         }
       }
 
       // Add the variable's __proto__.
       if (prototype && prototype.type != "null") {
         aVar.addProperty("__proto__", { value: prototype });
         // Expansion handlers must be set after the properties are added.
         this._addVarExpander(aVar.get("__proto__"), prototype);
       }
 
+      // Mark the variable as having retrieved all its properties.
       aVar._retrieved = true;
 
       // Signal that properties have been fetched.
       window.dispatchEvent("Debugger:FetchedProperties");
       DebuggerView.Variables.commitHierarchy();
     }.bind(this));
   },
 
@@ -1632,16 +1654,17 @@ let Prefs = {
 };
 
 Prefs.map("Int", "windowX", "devtools.debugger.ui.win-x");
 Prefs.map("Int", "windowY", "devtools.debugger.ui.win-y");
 Prefs.map("Int", "windowWidth", "devtools.debugger.ui.win-width");
 Prefs.map("Int", "windowHeight", "devtools.debugger.ui.win-height");
 Prefs.map("Int", "stackframesWidth", "devtools.debugger.ui.stackframes-width");
 Prefs.map("Int", "variablesWidth", "devtools.debugger.ui.variables-width");
+Prefs.map("Bool", "pauseOnExceptions", "devtools.debugger.ui.pause-on-exceptions");
 Prefs.map("Bool", "panesVisibleOnStartup", "devtools.debugger.ui.panes-visible-on-startup");
 Prefs.map("Bool", "variablesSortingEnabled", "devtools.debugger.ui.variables-sorting-enabled");
 Prefs.map("Bool", "variablesOnlyEnumVisible", "devtools.debugger.ui.variables-only-enum-visible");
 Prefs.map("Bool", "variablesSearchboxVisible", "devtools.debugger.ui.variables-searchbox-visible");
 Prefs.map("Char", "remoteHost", "devtools.debugger.remote-host");
 Prefs.map("Int", "remotePort", "devtools.debugger.remote-port");
 Prefs.map("Bool", "remoteAutoConnect", "devtools.debugger.remote-autoconnect");
 Prefs.map("Int", "remoteConnectionRetries", "devtools.debugger.remote-connection-retries");
--- a/browser/devtools/debugger/debugger-toolbar.js
+++ b/browser/devtools/debugger/debugger-toolbar.js
@@ -188,17 +188,17 @@ OptionsView.prototype = {
   initialize: function DVO_initialize() {
     dumpn("Initializing the OptionsView");
     this._button = document.getElementById("debugger-options");
     this._pauseOnExceptionsItem = document.getElementById("pause-on-exceptions");
     this._showPanesOnStartupItem = document.getElementById("show-panes-on-startup");
     this._showVariablesOnlyEnumItem = document.getElementById("show-vars-only-enum");
     this._showVariablesFilterBoxItem = document.getElementById("show-vars-filter-box");
 
-    this._pauseOnExceptionsItem.setAttribute("checked", "false");
+    this._pauseOnExceptionsItem.setAttribute("checked", Prefs.pauseOnExceptions);
     this._showPanesOnStartupItem.setAttribute("checked", Prefs.panesVisibleOnStartup);
     this._showVariablesOnlyEnumItem.setAttribute("checked", Prefs.variablesOnlyEnumVisible);
     this._showVariablesFilterBoxItem.setAttribute("checked", Prefs.variablesSearchboxVisible);
   },
 
   /**
    * Destruction function, called when the debugger is closed.
    */
@@ -220,17 +220,17 @@ OptionsView.prototype = {
   _onPopupHiding: function DVO__onPopupHiding() {
     this._button.removeAttribute("open");
   },
 
   /**
    * Listener handling the 'pause on exceptions' menuitem command.
    */
   _togglePauseOnExceptions: function DVO__togglePauseOnExceptions() {
-    DebuggerController.activeThread.pauseOnExceptions(
+    DebuggerController.activeThread.pauseOnExceptions(Prefs.pauseOnExceptions =
       this._pauseOnExceptionsItem.getAttribute("checked") == "true");
   },
 
   /**
    * Listener handling the 'show panes on startup' menuitem command.
    */
   _toggleShowPanesOnStartup: function DVO__toggleShowPanesOnStartup() {
     Prefs.panesVisibleOnStartup =
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -29,16 +29,17 @@ MOCHITEST_BROWSER_TESTS = \
 	browser_dbg_propertyview-05.js \
 	browser_dbg_propertyview-06.js \
 	browser_dbg_propertyview-07.js \
 	browser_dbg_propertyview-08.js \
 	browser_dbg_propertyview-09.js \
 	browser_dbg_propertyview-10.js \
 	browser_dbg_propertyview-edit.js \
 	browser_dbg_propertyview-edit-watch.js \
+	browser_dbg_propertyview-big-data.js \
 	browser_dbg_propertyview-data.js \
 	browser_dbg_propertyview-filter-01.js \
 	browser_dbg_propertyview-filter-02.js \
 	browser_dbg_propertyview-filter-03.js \
 	browser_dbg_propertyview-filter-04.js \
 	browser_dbg_propertyview-filter-05.js \
 	browser_dbg_propertyview-filter-06.js \
 	browser_dbg_propertyview-filter-07.js \
@@ -97,16 +98,17 @@ MOCHITEST_BROWSER_TESTS = \
 MOCHITEST_BROWSER_PAGES = \
 	browser_dbg_tab1.html \
 	browser_dbg_tab2.html \
 	browser_dbg_debuggerstatement.html \
 	browser_dbg_stack.html \
 	browser_dbg_script-switching.html \
 	test-script-switching-01.js \
 	test-script-switching-02.js \
+	browser_dbg_big-data.html \
 	browser_dbg_frame-parameters.html \
 	browser_dbg_update-editor-mode.html \
 	test-editor-mode \
 	browser_dbg_displayName.html \
 	browser_dbg_iframes.html \
 	browser_dbg_with-frame.html \
 	browser_dbg_pause-exceptions.html \
 	browser_dbg_breakpoint-new-script.html \
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_big-data.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'/>
+    <title>Debugger Big Data Test</title>
+    <!-- Any copyright is dedicated to the Public Domain.
+         http://creativecommons.org/publicdomain/zero/1.0/ -->
+    <script type="text/javascript">
+      window.addEventListener("load", function() {
+        function test(aNumber) {
+          var buffer = new ArrayBuffer(aNumber);
+          var z = new Int8Array(buffer);
+          debugger;
+        };
+        function load() {
+          test(10000);
+        }
+        var button = document.querySelector("button");
+        button.addEventListener("click", load, false);
+      });
+    </script>
+
+  </head>
+  <body>
+    <button>Click me!</button>
+  </body>
+</html>
--- a/browser/devtools/debugger/test/browser_dbg_pause-exceptions.js
+++ b/browser/devtools/debugger/test/browser_dbg_pause-exceptions.js
@@ -6,62 +6,73 @@
  * Make sure that the pause-on-exceptions toggle works.
  */
 
 const TAB_URL = EXAMPLE_URL + "browser_dbg_pause-exceptions.html";
 
 var gPane = null;
 var gTab = null;
 var gDebugger = null;
-var gCount = 0;
+var gPrevPref = null;
 
 requestLongerTimeout(2);
 
 function test()
 {
+  gPrevPref = Services.prefs.getBoolPref(
+    "devtools.debugger.ui.pause-on-exceptions");
+  Services.prefs.setBoolPref(
+    "devtools.debugger.ui.pause-on-exceptions", true);
+
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gPane = aPane;
     gDebugger = gPane.panelWin;
 
     gDebugger.DebuggerController.StackFrames.autoScopeExpand = true;
     gDebugger.DebuggerView.Variables.nonEnumVisible = false;
     testWithFrame();
   });
 }
 
 function testWithFrame()
 {
+  let count = 0;
   gPane.panelWin.gClient.addOneTimeListener("paused", function() {
     gDebugger.addEventListener("Debugger:FetchedVariables", function testA() {
       // We expect 2 Debugger:FetchedVariables events, one from the global object
       // scope and the regular one.
-      if (++gCount < 2) {
-        is(gCount, 1, "A. First Debugger:FetchedVariables event received.");
+      if (++count < 2) {
+        is(count, 1, "A. First Debugger:FetchedVariables event received.");
         return;
       }
-      is(gCount, 2, "A. Second Debugger:FetchedVariables event received.");
+      is(count, 2, "A. Second Debugger:FetchedVariables event received.");
       gDebugger.removeEventListener("Debugger:FetchedVariables", testA, false);
 
       is(gDebugger.DebuggerController.activeThread.state, "paused",
         "Should be paused now.");
 
-      gDebugger.DebuggerView.Options._pauseOnExceptionsItem.setAttribute("checked", "true");
-      gDebugger.DebuggerView.Options._togglePauseOnExceptions();
+      // Pause on exceptions should be already enabled.
+      is(gPrevPref, false,
+        "The pause-on-exceptions functionality should be disabled by default.");
+      is(gDebugger.Prefs.pauseOnExceptions, true,
+        "The pause-on-exceptions pref should be true from startup.");
+      is(gDebugger.DebuggerView.Options._pauseOnExceptionsItem.getAttribute("checked"), "true",
+        "Pause on exceptions should be enabled from startup. ")
 
-      gCount = 0;
+      count = 0;
       gPane.panelWin.gClient.addOneTimeListener("resumed", function() {
         gDebugger.addEventListener("Debugger:FetchedVariables", function testB() {
           // We expect 2 Debugger:FetchedVariables events, one from the global object
           // scope and the regular one.
-          if (++gCount < 2) {
-            is(gCount, 1, "B. First Debugger:FetchedVariables event received.");
+          if (++count < 2) {
+            is(count, 1, "B. First Debugger:FetchedVariables event received.");
             return;
           }
-          is(gCount, 2, "B. Second Debugger:FetchedVariables event received.");
+          is(count, 2, "B. Second Debugger:FetchedVariables event received.");
           gDebugger.removeEventListener("Debugger:FetchedVariables", testB, false);
           Services.tm.currentThread.dispatch({ run: function() {
 
             var frames = gDebugger.DebuggerView.StackFrames._container._list,
                 scopes = gDebugger.DebuggerView.Variables._list,
                 innerScope = scopes.firstChild,
                 innerNodes = innerScope.querySelector(".details").childNodes;
 
@@ -74,17 +85,25 @@ function testWithFrame()
             is(scopes.children.length, 3, "Should have 3 variable scopes.");
 
             is(innerNodes[0].querySelector(".name").getAttribute("value"), "<exception>",
               "Should have the right property name for the exception.");
 
             is(innerNodes[0].querySelector(".value").getAttribute("value"), "[object Error]",
               "Should have the right property value for the exception.");
 
+            // Disable pause on exceptions.
+            gDebugger.DebuggerView.Options._pauseOnExceptionsItem.setAttribute("checked", "false");
+            gDebugger.DebuggerView.Options._togglePauseOnExceptions();
+
+            is(gDebugger.Prefs.pauseOnExceptions, false,
+              "The pause-on-exceptions pref should have been set to false.");
+
             resumeAndFinish();
+
           }}, 0);
         }, false);
       });
 
       EventUtils.sendMouseEvent({ type: "mousedown" },
         gDebugger.document.getElementById("resume"),
         gDebugger);
     }, false);
@@ -94,17 +113,18 @@ function testWithFrame()
     content.document.querySelector("button"),
     content.window);
 }
 
 function resumeAndFinish() {
   gPane.panelWin.gClient.addOneTimeListener("resumed", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
-      closeDebuggerAndFinish(false);
+      closeDebuggerAndFinish();
+
     }}, 0);
   });
 
   // Resume to let the exception reach it's catch clause.
   gDebugger.DebuggerController.activeThread.resume();
 }
 
 registerCleanupFunction(function() {
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-05.js
@@ -21,26 +21,26 @@ function test() {
 
 function testSimpleCall() {
   gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
       let testScope = gDebugger.DebuggerView.Variables.addScope("test");
       let testVar = testScope.addVar("something");
 
-      testVar._setGrip(1.618);
+      testVar.setGrip(1.618);
 
       is(testVar.target.querySelector(".value").getAttribute("value"), "1.618",
         "The grip information for the variable wasn't set correctly.");
 
       is(testVar.target.querySelector(".details").childNodes.length, 0,
         "Adding a value property shouldn't add any new tree nodes.");
 
 
-      testVar._setGrip({ "type": "object", "class": "Window" });
+      testVar.setGrip({ "type": "object", "class": "Window" });
 
       is(testVar.target.querySelector(".details").childNodes.length, 0,
         "Adding type and class properties shouldn't add any new tree nodes.");
 
       is(testVar.target.querySelector(".value").getAttribute("value"), "[object Window]",
         "The information for the variable wasn't set correctly.");
 
 
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-06.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-06.js
@@ -30,23 +30,23 @@ function testSimpleCall() {
       let documentVar = globalScope.addVar("document");
       let localVar0 = localScope.addVar("localVariable");
       let localVar1 = localScope.addVar("localVar1");
       let localVar2 = localScope.addVar("localVar2");
       let localVar3 = localScope.addVar("localVar3");
       let localVar4 = localScope.addVar("localVar4");
       let localVar5 = localScope.addVar("localVar5");
 
-      localVar0._setGrip(42);
-      localVar1._setGrip(true);
-      localVar2._setGrip("nasu");
+      localVar0.setGrip(42);
+      localVar1.setGrip(true);
+      localVar2.setGrip("nasu");
 
-      localVar3._setGrip({ "type": "undefined" });
-      localVar4._setGrip({ "type": "null" });
-      localVar5._setGrip({ "type": "object", "class": "Object" });
+      localVar3.setGrip({ "type": "undefined" });
+      localVar4.setGrip({ "type": "null" });
+      localVar5.setGrip({ "type": "object", "class": "Object" });
 
       localVar5.addProperties({ "someProp0": { "value": 42, "enumerable": true },
                                 "someProp1": { "value": true , "enumerable": true},
                                 "someProp2": { "value": "nasu", "enumerable": true},
                                 "someProp3": { "value": { "type": "undefined" }, "enumerable": true},
                                 "someProp4": { "value": { "type": "null" }, "enumerable": true },
                                 "someProp5": {
                                   "value": { "type": "object", "class": "Object" },
@@ -58,20 +58,20 @@ function testSimpleCall() {
                                                  "someProp1": { "value": true, "enumerable": true },
                                                  "someProp2": { "value": "nasu", "enumerable": true },
                                                  "someProp3": { "value": { "type": "undefined" }, "enumerable": true },
                                                  "someProp4": { "value": { "type": "null" }, "enumerable": true },
                                                  "someAccessor": { "get": { "type": "object", "class": "Function" },
                                                                    "set": { "type": "undefined" },
                                                                    "enumerable": true } });
 
-      windowVar._setGrip({ "type": "object", "class": "Window" });
+      windowVar.setGrip({ "type": "object", "class": "Window" });
       windowVar.addProperties({ "helloWorld": { "value": "hello world" } });
 
-      documentVar._setGrip({ "type": "object", "class": "HTMLDocument" });
+      documentVar.setGrip({ "type": "object", "class": "HTMLDocument" });
       documentVar.addProperties({ "onload": { "value": { "type": "null" } },
                                   "onunload": { "value": { "type": "null" } },
                                   "onfocus": { "value": { "type": "null" } },
                                   "onblur": { "value": { "type": "null" } },
                                   "onclick": { "value": { "type": "null" } },
                                   "onkeypress": { "value": { "type": "null" } } });
 
 
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-07.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-07.js
@@ -20,37 +20,25 @@ function test()
     gDebugger = gPane.panelWin;
 
     testFrameParameters();
   });
 }
 
 function testFrameParameters()
 {
-  dump("Started testFrameParameters!\n");
-
   gDebugger.addEventListener("Debugger:FetchedVariables", function test() {
-    dump("Entered Debugger:FetchedVariables!\n");
-
     gDebugger.removeEventListener("Debugger:FetchedVariables", test, false);
     Services.tm.currentThread.dispatch({ run: function() {
 
-      dump("After currentThread.dispatch!\n");
-
       var frames = gDebugger.DebuggerView.StackFrames._container._list,
           childNodes = frames.childNodes,
           localScope = gDebugger.DebuggerView.Variables._list.querySelector(".scope"),
           localNodes = localScope.querySelector(".details").childNodes;
 
-      dump("Got our variables:\n");
-      dump("frames     - " + frames.constructor + "\n");
-      dump("childNodes - " + childNodes.constructor + "\n");
-      dump("localScope - " + localScope.constructor + "\n");
-      dump("localNodes - " + localNodes.constructor + "\n");
-
       is(gDebugger.DebuggerController.activeThread.state, "paused",
         "Should only be getting stack frames while paused.");
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 3,
         "Should have three frames.");
 
       is(localNodes.length, 11,
         "The localScope should contain all the created variable elements.");
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-08.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-08.js
@@ -20,72 +20,89 @@ function test()
     gDebugger = gPane.panelWin;
 
     testFrameParameters();
   });
 }
 
 function testFrameParameters()
 {
-  dump("Started testFrameParameters!\n");
-
   gDebugger.addEventListener("Debugger:FetchedVariables", function test() {
-    dump("Entered Debugger:FetchedVariables!\n");
-
     gDebugger.removeEventListener("Debugger:FetchedVariables", test, false);
     Services.tm.currentThread.dispatch({ run: function() {
 
-      dump("After currentThread.dispatch!\n");
-
       var frames = gDebugger.DebuggerView.StackFrames._container._list,
           localScope = gDebugger.DebuggerView.Variables._list.querySelectorAll(".scope")[0],
           localNodes = localScope.querySelector(".details").childNodes,
           localNonEnums = localScope.querySelector(".nonenum").childNodes;
 
-      dump("Got our variables:\n");
-      dump("frames     - " + frames.constructor + "\n");
-      dump("localScope - " + localScope.constructor + "\n");
-      dump("localNodes - " + localNodes.constructor + "\n");
-      dump("localNonEnums - " + localNonEnums.constructor + "\n");
-
       is(gDebugger.DebuggerController.activeThread.state, "paused",
         "Should only be getting stack frames while paused.");
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 3,
         "Should have three frames.");
 
       is(localNodes.length + localNonEnums.length, 11,
         "The localScope and localNonEnums should contain all the created variable elements.");
 
       is(localNodes[0].querySelector(".value").getAttribute("value"), "[object Proxy]",
         "Should have the right property value for 'this'.");
+      is(localNodes[8].querySelector(".value").getAttribute("value"), "[object Arguments]",
+        "Should have the right property value for 'arguments'.");
+      is(localNodes[10].querySelector(".value").getAttribute("value"), "[object Object]",
+        "Should have the right property value for 'c'.");
 
-      let thisNode, argumentsNode, cNode;
-      for (let [id, scope] in gDebugger.DebuggerView.Variables) {
-        if (scope.target === localScope) {
-          for (let [name, variable] in scope) {
-            if (variable.target === localNodes[0]) {
-              thisNode = variable;
-            }
-            if (variable.target === localNodes[8]) {
-              argumentsNode = variable;
-            }
-            if (variable.target === localNodes[10]) {
-              cNode = variable;
-            }
-          }
-        }
-      }
+
+      let gVars = gDebugger.DebuggerView.Variables;
+
+      is(gVars.getScopeForNode(
+         gVars._list.querySelectorAll(".scope")[0]).target,
+         gVars._list.querySelectorAll(".scope")[0],
+        "getScopeForNode([0]) didn't return the expected scope.");
+      is(gVars.getScopeForNode(
+         gVars._list.querySelectorAll(".scope")[1]).target,
+         gVars._list.querySelectorAll(".scope")[1],
+        "getScopeForNode([1]) didn't return the expected scope.");
+      is(gVars.getScopeForNode(
+         gVars._list.querySelectorAll(".scope")[2]).target,
+         gVars._list.querySelectorAll(".scope")[2],
+        "getScopeForNode([2]) didn't return the expected scope.");
+
+      is(gVars.getScopeForNode(gVars._list.querySelectorAll(".scope")[0]).expanded, true,
+        "The local scope should be expanded by default.");
+      is(gVars.getScopeForNode(gVars._list.querySelectorAll(".scope")[1]).expanded, false,
+        "The block scope should be collapsed by default.");
+      is(gVars.getScopeForNode(gVars._list.querySelectorAll(".scope")[2]).expanded, false,
+        "The global scope should be collapsed by default.");
+
+
+      let thisNode = gVars.getVariableOrPropertyForNode(localNodes[0]);
+      let argumentsNode = gVars.getVariableOrPropertyForNode(localNodes[8]);
+      let cNode = gVars.getVariableOrPropertyForNode(localNodes[10]);
+
+      is(thisNode.expanded, false,
+        "The thisNode should not be expanded at this point.");
+      is(argumentsNode.expanded, false,
+        "The argumentsNode should not be expanded at this point.");
+      is(cNode.expanded, false,
+        "The cNode should not be expanded at this point.");
 
       // Expand the 'this', 'arguments' and 'c' tree nodes. This causes
       // their properties to be retrieved and displayed.
       thisNode.expand();
       argumentsNode.expand();
       cNode.expand();
 
+      is(thisNode.expanded, true,
+        "The thisNode should be expanded at this point.");
+      is(argumentsNode.expanded, true,
+        "The argumentsNode should be expanded at this point.");
+      is(cNode.expanded, true,
+        "The cNode should be expanded at this point.");
+
       // Poll every few milliseconds until the properties are retrieved.
       // It's important to set the timer in the chrome window, because the
       // content window timers are disabled while the debuggee is paused.
       let count = 0;
       let intervalID = window.setInterval(function(){
         info("count: " + count + " ");
         if (++count > 50) {
           ok(false, "Timed out while polling for the properties.");
@@ -93,62 +110,113 @@ function testFrameParameters()
           return resumeAndFinish();
         }
         if (!thisNode._retrieved ||
             !argumentsNode._retrieved ||
             !cNode._retrieved) {
           return;
         }
         window.clearInterval(intervalID);
-        is(thisNode.target.querySelector(".property > .title > .name")
-                        .getAttribute("value"), "InstallTrigger",
-          "Should have the right property name for InstallTrigger.");
-        ok(thisNode.target.querySelector(".property > .title > .value")
-                        .getAttribute("value").search(/object/) == -1,
-          "InstallTrigger should not be an object.");
+
+        is(thisNode.target.querySelector(".value")
+           .getAttribute("value"), "[object Proxy]",
+          "Should have the right property value for 'this'.");
+
+        is(thisNode.get("window").target.querySelector(".name")
+           .getAttribute("value"), "window",
+          "Should have the right property name for 'window'.");
+        ok(thisNode.get("window").target.querySelector(".value")
+           .getAttribute("value").search(/object/) != -1,
+          "'window' should be an object.");
+
+        is(thisNode.get("document").target.querySelector(".name")
+           .getAttribute("value"), "document",
+          "Should have the right property name for 'document'.");
+        ok(thisNode.get("document").target.querySelector(".value")
+           .getAttribute("value").search(/object/) != -1,
+          "'document' should be an object.");
+
 
         is(argumentsNode.target.querySelector(".value")
-                        .getAttribute("value"), "[object Arguments]",
-         "Should have the right property value for 'arguments'.");
-        ok(argumentsNode.target.querySelector(".property > .title > .value")
-                        .getAttribute("value").search(/object/) != -1,
-          "Arguments should be an object.");
+           .getAttribute("value"), "[object Arguments]",
+          "Should have the right property value for 'arguments'.");
+
+        is(argumentsNode.target.querySelectorAll(".property > .title > .name")[0]
+           .getAttribute("value"), "0",
+          "Should have the right property name for 'arguments[0]'.");
+        ok(argumentsNode.target.querySelectorAll(".property > .title > .value")[0]
+           .getAttribute("value").search(/object/) != -1,
+          "'arguments[0]' should be an object.");
 
         is(argumentsNode.target.querySelectorAll(".property > .title > .name")[7]
-                        .getAttribute("value"), "__proto__",
-         "Should have the right property name for '__proto__'.");
+           .getAttribute("value"), "__proto__",
+          "Should have the right property name for '__proto__'.");
         ok(argumentsNode.target.querySelectorAll(".property > .title > .value")[7]
-                        .getAttribute("value").search(/object/) != -1,
-          "__proto__ should be an object.");
+           .getAttribute("value").search(/object/) != -1,
+          "'__proto__' should be an object.");
+
 
         is(cNode.target.querySelector(".value")
-                         .getAttribute("value"), "[object Object]",
+           .getAttribute("value"), "[object Object]",
           "Should have the right property value for 'c'.");
 
         is(cNode.target.querySelectorAll(".property > .title > .name")[0]
-                         .getAttribute("value"), "a",
+           .getAttribute("value"), "a",
           "Should have the right property name for 'c.a'.");
         is(cNode.target.querySelectorAll(".property > .title > .value")[0]
-                         .getAttribute("value"), "1",
+           .getAttribute("value"), "1",
           "Should have the right value for 'c.a'.");
 
         is(cNode.target.querySelectorAll(".property > .title > .name")[1]
-                         .getAttribute("value"), "b",
+           .getAttribute("value"), "b",
           "Should have the right property name for 'c.b'.");
         is(cNode.target.querySelectorAll(".property > .title > .value")[1]
-                         .getAttribute("value"), "\"beta\"",
+           .getAttribute("value"), "\"beta\"",
           "Should have the right value for 'c.b'.");
 
         is(cNode.target.querySelectorAll(".property > .title > .name")[2]
-                         .getAttribute("value"), "c",
+           .getAttribute("value"), "c",
           "Should have the right property name for 'c.c'.");
         is(cNode.target.querySelectorAll(".property > .title > .value")[2]
-                         .getAttribute("value"), "true",
+           .getAttribute("value"), "true",
           "Should have the right value for 'c.c'.");
 
+
+        is(gVars.getVariableOrPropertyForNode(
+           cNode.target.querySelectorAll(".property")[0]).target,
+           cNode.target.querySelectorAll(".property")[0],
+          "getVariableOrPropertyForNode([0]) didn't return the expected property.");
+
+        is(gVars.getVariableOrPropertyForNode(
+           cNode.target.querySelectorAll(".property")[1]).target,
+           cNode.target.querySelectorAll(".property")[1],
+          "getVariableOrPropertyForNode([1]) didn't return the expected property.");
+
+        is(gVars.getVariableOrPropertyForNode(
+           cNode.target.querySelectorAll(".property")[2]).target,
+           cNode.target.querySelectorAll(".property")[2],
+          "getVariableOrPropertyForNode([2]) didn't return the expected property.");
+
+
+        is(cNode.find(
+           cNode.target.querySelectorAll(".property")[0]).target,
+           cNode.target.querySelectorAll(".property")[0],
+          "find([0]) didn't return the expected property.");
+
+        is(cNode.find(
+           cNode.target.querySelectorAll(".property")[1]).target,
+           cNode.target.querySelectorAll(".property")[1],
+          "find([1]) didn't return the expected property.");
+
+        is(cNode.find(
+           cNode.target.querySelectorAll(".property")[2]).target,
+           cNode.target.querySelectorAll(".property")[2],
+          "find([2]) didn't return the expected property.");
+
+
         resumeAndFinish();
       }, 100);
     }}, 0);
   }, false);
 
   EventUtils.sendMouseEvent({ type: "click" },
     content.document.querySelector("button"),
     content.window);
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-big-data.js
@@ -0,0 +1,143 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Make sure that the property view remains responsive when faced with
+ * huge ammounts of data.
+ */
+
+const TAB_URL = EXAMPLE_URL + "browser_dbg_big-data.html";
+
+var gPane = null;
+var gTab = null;
+var gDebugger = null;
+
+requestLongerTimeout(10);
+
+function test()
+{
+  debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+    gTab = aTab;
+    gPane = aPane;
+    gDebugger = gPane.panelWin;
+
+    gDebugger.DebuggerController.StackFrames.autoScopeExpand = true;
+    gDebugger.DebuggerView.Variables.nonEnumVisible = false;
+    gDebugger.DebuggerView.Variables.lazyAppend = true;
+    testWithFrame();
+  });
+}
+
+function testWithFrame()
+{
+  let count = 0;
+  gDebugger.addEventListener("Debugger:FetchedVariables", function test1() {
+    // We expect 2 Debugger:FetchedVariables events, one from the global object
+    // scope and the regular one.
+    if (++count < 2) {
+      info("Number of received Debugger:FetchedVariables events: " + count);
+      return;
+    }
+    gDebugger.removeEventListener("Debugger:FetchedVariables", test1, false);
+    Services.tm.currentThread.dispatch({ run: function() {
+
+      var scopes = gDebugger.DebuggerView.Variables._list,
+          innerScope = scopes.querySelectorAll(".scope")[0],
+          loadScope = scopes.querySelectorAll(".scope")[1],
+          globalScope = scopes.querySelectorAll(".scope")[2],
+          innerNodes = innerScope.querySelector(".details").childNodes,
+          arrayNodes = innerNodes[4].querySelector(".details").childNodes;
+
+      is(innerNodes[3].querySelector(".name").getAttribute("value"), "buffer",
+        "Should have the right property name for |buffer|.");
+
+      is(innerNodes[3].querySelector(".value").getAttribute("value"), "[object ArrayBuffer]",
+        "Should have the right property value for |buffer|.");
+
+      is(innerNodes[4].querySelector(".name").getAttribute("value"), "z",
+        "Should have the right property name for |z|.");
+
+      is(innerNodes[4].querySelector(".value").getAttribute("value"), "[object Int8Array]",
+        "Should have the right property value for |z|.");
+
+
+      EventUtils.sendMouseEvent({ type: "mousedown" }, innerNodes[3].querySelector(".arrow"), gDebugger);
+      EventUtils.sendMouseEvent({ type: "mousedown" }, innerNodes[4].querySelector(".arrow"), gDebugger);
+
+      gDebugger.addEventListener("Debugger:FetchedProperties", function test2() {
+        gDebugger.removeEventListener("Debugger:FetchedProperties", test2, false);
+        Services.tm.currentThread.dispatch({ run: function() {
+
+          let total = 10000;
+          let loaded = 0;
+          let paints = 0;
+
+          waitForProperties(total, {
+            onLoading: function(count) {
+              ok(count >= loaded, "Should have loaded more properties.");
+              info("Displayed " + count + " properties, not finished yet.");
+              info("Remaining " + (total - count) + " properties to display.");
+              loaded = count;
+              paints++;
+
+              loadScope.hidden = true;
+              globalScope.hidden = true;
+              scopes.parentNode.scrollTop = scopes.parentNode.scrollHeight;
+            },
+            onFinished: function(count) {
+              ok(count == total, "Displayed all the properties.");
+              isnot(paints, 0, "Debugger was unresponsive, sad panda.");
+
+              for (let i = 0; i < arrayNodes.length; i++) {
+                let node = arrayNodes[i];
+                let name = node.querySelector(".name").getAttribute("value");
+                is(name, i + "", "The array items aren't in the correct order.");
+              }
+
+              closeDebuggerAndFinish();
+            }
+          });
+        }}, 0);
+      }, false);
+    }}, 0);
+  }, false);
+
+  EventUtils.sendMouseEvent({ type: "click" },
+    content.document.querySelector("button"),
+    content.window);
+}
+
+function waitForProperties(total, callbacks)
+{
+  var scopes = gDebugger.DebuggerView.Variables._list,
+      innerScope = scopes.querySelectorAll(".scope")[0],
+      innerNodes = innerScope.querySelector(".details").childNodes,
+      arrayNodes = innerNodes[4].querySelector(".details").childNodes;
+
+  // Poll every few milliseconds until the properties are retrieved.
+  let count = 0;
+  let intervalID = window.setInterval(function() {
+    info("count: " + count + " ");
+    if (++count > total) {
+      ok(false, "Timed out while polling for the properties.");
+      window.clearInterval(intervalID);
+      return closeDebuggerAndFinish();
+    }
+    // Still need to wait for a few more properties to be fetched.
+    if (arrayNodes.length < total) {
+      callbacks.onLoading(arrayNodes.length);
+      return;
+    }
+    // We got all the properties, it's safe to callback.
+    window.clearInterval(intervalID);
+    callbacks.onFinished(arrayNodes.length);
+  }, 100);
+}
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-data.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-data.js
@@ -65,25 +65,40 @@ function testVariablesView()
 
   testHierarchy();
   testHeader();
   testFirstLevelContents();
   testSecondLevelContents();
   testThirdLevelContents();
   testIntegrity(arr, obj);
 
+  gVariablesView.eval = function() {};
+  gVariablesView.switch = function() {};
+  gVariablesView.delete = function() {};
+
+  let fooScope = gVariablesView.addScope("foo");
+  let anonymousVar = fooScope.addVar();
+
+  let anonymousScope = gVariablesView.addScope();
+  let barVar = anonymousScope.addVar("bar");
+  let bazProperty = barVar.addProperty("baz");
+
+  testAnonymousHeaders(fooScope, anonymousVar, anonymousScope, barVar, bazProperty);
+  testPropertyInheritance(fooScope, anonymousVar, anonymousScope, barVar, bazProperty);
+  testClearHierarchy();
+
   closeDebuggerAndFinish();
 }
 
 function testHierarchy() {
   is(gVariablesView._currHierarchy.size, 13,
     "There should be 1 scope, 1 var, 1 proto, 8 props, 1 getter and 1 setter.");
 
   gScope = gVariablesView._currHierarchy.get("");
-  gVariable = gVariablesView._currHierarchy.get(".");
+  gVariable = gVariablesView._currHierarchy.get("[\"\"]");
 
   is(gVariablesView._store.size, 1,
     "There should be only one scope in the view");
   is(gScope._store.size, 1,
     "There should be only one variable in the scope");
   is(gVariable._store.size, 9,
     "There should be 1 __proto__ and 8 properties in the variable");
 }
@@ -92,28 +107,28 @@ function testHeader() {
   is(gScope.header, false,
     "The scope title header should be hidden");
   is(gVariable.header, false,
     "The variable title header should be hidden");
 
   gScope.showHeader();
   gVariable.showHeader();
 
-  is(gScope.header, true,
-    "The scope title header should now be visible");
-  is(gVariable.header, true,
-    "The variable title header should now be visible");
+  is(gScope.header, false,
+    "The scope title header should still not be visible");
+  is(gVariable.header, false,
+    "The variable title header should still not be visible");
 
   gScope.hideHeader();
   gVariable.hideHeader();
 
   is(gScope.header, false,
-    "The scope title header should now be hidden");
+    "The scope title header should now still be hidden");
   is(gVariable.header, false,
-    "The variable title header should now be hidden");
+    "The variable title header should now still be hidden");
 }
 
 function testFirstLevelContents() {
   let someProp0 = gVariable.get("someProp0");
   let someProp1 = gVariable.get("someProp1");
   let someProp2 = gVariable.get("someProp2");
   let someProp3 = gVariable.get("someProp3");
   let someProp4 = gVariable.get("someProp4");
@@ -469,16 +484,117 @@ function testIntegrity(arr, obj) {
   is(obj.p5[0], 3, "The sixth object property should not have changed");
   is(obj.p5[1], 4, "The sixth object property should not have changed");
   is(obj.p5[2], 5, "The sixth object property should not have changed");
   ok(obj.p6 instanceof Object, "The seventh object property should be an Object");
   is(obj.p6.prop1, 7, "The seventh object property should not have changed");
   is(obj.p6.prop2, 6, "The seventh object property should not have changed");
 }
 
+function testAnonymousHeaders(fooScope, anonymousVar, anonymousScope, barVar, bazProperty) {
+  is(fooScope.header, true,
+    "A named scope should have a header visible.");
+  is(fooScope.target.hasAttribute("non-header"), false,
+    "The non-header attribute should not be applied to scopes with headers.");
+
+  is(anonymousScope.header, false,
+    "An anonymous scope should have a header visible.");
+  is(anonymousScope.target.hasAttribute("non-header"), true,
+    "The non-header attribute should not be applied to scopes without headers.");
+
+  is(barVar.header, true,
+    "A named variable should have a header visible.");
+  is(barVar.target.hasAttribute("non-header"), false,
+    "The non-header attribute should not be applied to variables with headers.");
+
+  is(anonymousVar.header, false,
+    "An anonymous variable should have a header visible.");
+  is(anonymousVar.target.hasAttribute("non-header"), true,
+    "The non-header attribute should not be applied to variables without headers.");
+}
+
+function testPropertyInheritance(fooScope, anonymousVar, anonymousScope, barVar, bazProperty) {
+  is(fooScope.editableValueTooltip, gVariablesView.editableValueTooltip,
+    "The editableValueTooltip property should persist from the view to all scopes.");
+  is(fooScope.editableNameTooltip, gVariablesView.editableNameTooltip,
+    "The editableNameTooltip property should persist from the view to all scopes.");
+  is(fooScope.deleteButtonTooltip, gVariablesView.deleteButtonTooltip,
+    "The deleteButtonTooltip property should persist from the view to all scopes.");
+  is(fooScope.descriptorTooltip, gVariablesView.descriptorTooltip,
+    "The descriptorTooltip property should persist from the view to all scopes.");
+  is(fooScope.contextMenuId, gVariablesView.contextMenuId,
+    "The contextMenuId property should persist from the view to all scopes.");
+  is(fooScope.separatorStr, gVariablesView.separatorStr,
+    "The separatorStr property should persist from the view to all scopes.");
+  is(fooScope.eval, gVariablesView.eval,
+    "The eval property should persist from the view to all scopes.");
+  is(fooScope.switch, gVariablesView.switch,
+    "The switch property should persist from the view to all scopes.");
+  is(fooScope.delete, gVariablesView.delete,
+    "The delete property should persist from the view to all scopes.");
+  isnot(fooScope.eval, fooScope.switch,
+    "The eval and switch functions got mixed up in the scope.");
+  isnot(fooScope.switch, fooScope.delete,
+    "The eval and switch functions got mixed up in the scope.");
+
+  is(barVar.editableValueTooltip, gVariablesView.editableValueTooltip,
+    "The editableValueTooltip property should persist from the view to all variables.");
+  is(barVar.editableNameTooltip, gVariablesView.editableNameTooltip,
+    "The editableNameTooltip property should persist from the view to all variables.");
+  is(barVar.deleteButtonTooltip, gVariablesView.deleteButtonTooltip,
+    "The deleteButtonTooltip property should persist from the view to all variables.");
+  is(barVar.descriptorTooltip, gVariablesView.descriptorTooltip,
+    "The descriptorTooltip property should persist from the view to all variables.");
+  is(barVar.contextMenuId, gVariablesView.contextMenuId,
+    "The contextMenuId property should persist from the view to all variables.");
+  is(barVar.separatorStr, gVariablesView.separatorStr,
+    "The separatorStr property should persist from the view to all variables.");
+  is(barVar.eval, gVariablesView.eval,
+    "The eval property should persist from the view to all variables.");
+  is(barVar.switch, gVariablesView.switch,
+    "The switch property should persist from the view to all variables.");
+  is(barVar.delete, gVariablesView.delete,
+    "The delete property should persist from the view to all variables.");
+  isnot(barVar.eval, barVar.switch,
+    "The eval and switch functions got mixed up in the variable.");
+  isnot(barVar.switch, barVar.delete,
+    "The eval and switch functions got mixed up in the variable.");
+
+  is(bazProperty.editableValueTooltip, gVariablesView.editableValueTooltip,
+    "The editableValueTooltip property should persist from the view to all properties.");
+  is(bazProperty.editableNameTooltip, gVariablesView.editableNameTooltip,
+    "The editableNameTooltip property should persist from the view to all properties.");
+  is(bazProperty.deleteButtonTooltip, gVariablesView.deleteButtonTooltip,
+    "The deleteButtonTooltip property should persist from the view to all properties.");
+  is(bazProperty.descriptorTooltip, gVariablesView.descriptorTooltip,
+    "The descriptorTooltip property should persist from the view to all properties.");
+  is(bazProperty.contextMenuId, gVariablesView.contextMenuId,
+    "The contextMenuId property should persist from the view to all properties.");
+  is(bazProperty.separatorStr, gVariablesView.separatorStr,
+    "The separatorStr property should persist from the view to all properties.");
+  is(bazProperty.eval, gVariablesView.eval,
+    "The eval property should persist from the view to all properties.");
+  is(bazProperty.switch, gVariablesView.switch,
+    "The switch property should persist from the view to all properties.");
+  is(bazProperty.delete, gVariablesView.delete,
+    "The delete property should persist from the view to all properties.");
+  isnot(bazProperty.eval, bazProperty.switch,
+    "The eval and switch functions got mixed up in the property.");
+  isnot(bazProperty.switch, bazProperty.delete,
+    "The eval and switch functions got mixed up in the property.");
+}
+
+function testClearHierarchy() {
+  gVariablesView.clearHierarchy();
+  is (gVariablesView._prevHierarchy.size, 0,
+    "The previous hierarchy should have been cleared.");
+  is (gVariablesView._currHierarchy.size, 0,
+    "The current hierarchy should have been cleared.");
+}
+
 registerCleanupFunction(function() {
   removeTab(gTab);
   gPane = null;
   gTab = null;
   gDebugger = null;
   gVariablesView = null;
   gScope = null;
   gVariable = null;
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-edit-watch.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-edit-watch.js
@@ -65,59 +65,59 @@ function testFrameEval() {
       let label = gDebugger.L10N.getStr("watchExpressionsScopeLabel");
       let scope = gVars._currHierarchy.get(label);
 
       ok(scope, "There should be a wach expressions scope in the variables view");
       is(scope._store.size, 5, "There should be 5 evaluations availalble");
 
       is(scope.get("this")._isShown, true,
         "Should have the right visibility state for 'this'.");
-      is(scope.get("this").target.querySelectorAll(".dbg-variables-delete").length, 1,
+      is(scope.get("this").target.querySelectorAll(".dbg-variable-delete").length, 1,
         "Should have the one close button visible for 'this'.");
       is(scope.get("this").name, "this",
         "Should have the right name for 'this'.");
       is(scope.get("this").value.type, "object",
         "Should have the right value type for 'this'.");
       is(scope.get("this").value.class, "Proxy",
         "Should have the right value type for 'this'.");
 
       is(scope.get("ermahgerd")._isShown, true,
         "Should have the right visibility state for 'ermahgerd'.");
-      is(scope.get("ermahgerd").target.querySelectorAll(".dbg-variables-delete").length, 1,
+      is(scope.get("ermahgerd").target.querySelectorAll(".dbg-variable-delete").length, 1,
         "Should have the one close button visible for 'ermahgerd'.");
       is(scope.get("ermahgerd").name, "ermahgerd",
         "Should have the right name for 'ermahgerd'.");
       is(scope.get("ermahgerd").value.type, "object",
         "Should have the right value type for 'ermahgerd'.");
       is(scope.get("ermahgerd").value.class, "Function",
         "Should have the right value type for 'ermahgerd'.");
 
       is(scope.get("aArg")._isShown, true,
         "Should have the right visibility state for 'aArg'.");
-      is(scope.get("aArg").target.querySelectorAll(".dbg-variables-delete").length, 1,
+      is(scope.get("aArg").target.querySelectorAll(".dbg-variable-delete").length, 1,
         "Should have the one close button visible for 'aArg'.");
       is(scope.get("aArg").name, "aArg",
         "Should have the right name for 'aArg'.");
       is(scope.get("aArg").value, undefined,
         "Should have the right value for 'aArg'.");
 
       is(scope.get("document.title")._isShown, true,
         "Should have the right visibility state for 'document.title'.");
-      is(scope.get("document.title").target.querySelectorAll(".dbg-variables-delete").length, 1,
+      is(scope.get("document.title").target.querySelectorAll(".dbg-variable-delete").length, 1,
         "Should have the one close button visible for 'document.title'.");
       is(scope.get("document.title").name, "document.title",
         "Should have the right name for 'document.title'.");
       is(scope.get("document.title").value, "42",
         "Should have the right value for 'document.title'.");
       is(typeof scope.get("document.title").value, "string",
         "Should have the right value type for 'document.title'.");
 
       is(scope.get("document.title = 42")._isShown, true,
         "Should have the right visibility state for 'document.title = 42'.");
-      is(scope.get("document.title = 42").target.querySelectorAll(".dbg-variables-delete").length, 1,
+      is(scope.get("document.title = 42").target.querySelectorAll(".dbg-variable-delete").length, 1,
         "Should have the one close button visible for 'document.title = 42'.");
       is(scope.get("document.title = 42").name, "document.title = 42",
         "Should have the right name for 'document.title = 42'.");
       is(scope.get("document.title = 42").value, 42,
         "Should have the right value for 'document.title = 42'.");
       is(typeof scope.get("document.title = 42").value, "number",
         "Should have the right value type for 'document.title = 42'.");
 
@@ -320,17 +320,17 @@ function testExprDeletion(aVar, aTest, a
   }
 
   if (aRemoveAllFlag) {
     gWatch._onCmdRemoveAllExpressions();
     return;
   }
 
   EventUtils.sendMouseEvent({ type: "click" },
-    aVar.querySelector(".dbg-variables-delete"),
+    aVar.querySelector(".dbg-variable-delete"),
     gDebugger);
 }
 
 function test1(scope) {
   is(gWatch._container._parent.querySelectorAll(".dbg-expression[hidden=true]").length, 5,
     "There should be 5 hidden nodes in the watch expressions container");
   is(gWatch._container._parent.querySelectorAll(".dbg-expression:not([hidden=true])").length, 0,
     "There should be 0 visible nodes in the watch expressions container");
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-filter-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-filter-01.js
@@ -33,53 +33,57 @@ function test()
 
 function testSearchbox()
 {
   ok(!gDebugger.DebuggerView.Variables._searchboxNode,
     "There should not initially be a searchbox available in the variables view.");
   ok(!gDebugger.DebuggerView.Variables._parent.parentNode.querySelector(".variables-searchinput.devtools-searchinput"),
     "The searchbox element should not be found.");
 
-  gDebugger.DebuggerView.Variables.enableSearch();
+  gDebugger.DebuggerView.Variables._enableSearch();
   ok(gDebugger.DebuggerView.Variables._searchboxNode,
     "There should be a searchbox available after enabling.");
   ok(gDebugger.DebuggerView.Variables._searchboxContainer.hidden,
     "The searchbox container should be hidden at this point.");
   ok(gDebugger.DebuggerView.Variables._parent.parentNode.querySelector(".variables-searchinput.devtools-searchinput"),
     "The searchbox element should be found.");
 
 
-  gDebugger.DebuggerView.Variables.disableSearch();
+  gDebugger.DebuggerView.Variables._disableSearch();
   ok(!gDebugger.DebuggerView.Variables._searchboxNode,
     "There shouldn't be a searchbox available after disabling.");
   ok(!gDebugger.DebuggerView.Variables._parent.parentNode.querySelector(".variables-searchinput.devtools-searchinput"),
     "The searchbox element should not be found.");
 
-  gDebugger.DebuggerView.Variables.enableSearch();
+  gDebugger.DebuggerView.Variables._enableSearch();
   ok(gDebugger.DebuggerView.Variables._searchboxNode,
     "There should be a searchbox available after enabling.");
   ok(gDebugger.DebuggerView.Variables._searchboxContainer.hidden,
     "The searchbox container should be hidden at this point.");
   ok(gDebugger.DebuggerView.Variables._parent.parentNode.querySelector(".variables-searchinput.devtools-searchinput"),
     "The searchbox element should be found.");
 
+
   let placeholder = "freshly squeezed mango juice";
 
   gDebugger.DebuggerView.Variables.searchPlaceholder = placeholder;
+  is(gDebugger.DebuggerView.Variables.searchPlaceholder, placeholder,
+    "The placeholder getter didn't return the expected string");
+
   ok(gDebugger.DebuggerView.Variables._searchboxNode.getAttribute("placeholder"),
     placeholder, "There correct placeholder should be applied to the searchbox.");
 
 
-  gDebugger.DebuggerView.Variables.disableSearch();
+  gDebugger.DebuggerView.Variables._disableSearch();
   ok(!gDebugger.DebuggerView.Variables._searchboxNode,
     "There shouldn't be a searchbox available after disabling again.");
   ok(!gDebugger.DebuggerView.Variables._parent.parentNode.querySelector(".variables-searchinput.devtools-searchinput"),
     "The searchbox element should not be found.");
 
-  gDebugger.DebuggerView.Variables.enableSearch();
+  gDebugger.DebuggerView.Variables._enableSearch();
   ok(gDebugger.DebuggerView.Variables._searchboxNode,
     "There should be a searchbox available after enabling again.");
   ok(gDebugger.DebuggerView.Variables._searchboxContainer.hidden,
     "The searchbox container should be hidden at this point.");
   ok(gDebugger.DebuggerView.Variables._parent.parentNode.querySelector(".variables-searchinput.devtools-searchinput"),
     "The searchbox element should be found.");
 
   ok(gDebugger.DebuggerView.Variables._searchboxNode.getAttribute("placeholder"),
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-filter-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-filter-02.js
@@ -33,17 +33,17 @@ function test()
 
 function testSearchbox()
 {
   ok(!gDebugger.DebuggerView.Variables._searchboxNode,
     "There should not initially be a searchbox available in the variables view.");
   ok(!gDebugger.DebuggerView.Variables._parent.parentNode.querySelector(".variables-searchinput.devtools-searchinput"),
     "The searchbox element should not be found.");
 
-  gDebugger.DebuggerView.Variables.enableSearch();
+  gDebugger.DebuggerView.Variables._enableSearch();
   ok(gDebugger.DebuggerView.Variables._searchboxNode,
     "There should be a searchbox available after enabling.");
   ok(gDebugger.DebuggerView.Variables._parent.parentNode.querySelector(".variables-searchinput.devtools-searchinput"),
     "The searchbox element should be found.");
   ok(gDebugger.DebuggerView.Variables._searchboxContainer.hidden,
     "The searchbox container should be hidden at this point.");
 }
 
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -178,16 +178,17 @@ function debug_tab_pane(aURL, aOnDebuggi
     let target = TargetFactory.forTab(gBrowser.selectedTab);
 
     gDevTools.showToolbox(target, "jsdebugger").then(function(toolbox) {
       let dbg = toolbox.getCurrentPanel();
       dbg.once("connected", function() {
         // Wait for the initial resume...
         dbg.panelWin.gClient.addOneTimeListener("resumed", function() {
           dbg._view.Variables.lazyEmpty = false;
+          dbg._view.Variables.lazyAppend = false;
           aOnDebugging(tab, debuggee, dbg);
         });
       });
     });
   });
 }
 
 function debug_remote(aURL, aOnDebugging, aBeforeTabAdded) {
@@ -200,16 +201,17 @@ function debug_remote(aURL, aOnDebugging
 
     let win = DebuggerUI.toggleRemoteDebugger();
     win._dbgwin.addEventListener("Debugger:Connected", function dbgConnected() {
       win._dbgwin.removeEventListener("Debugger:Connected", dbgConnected, true);
 
       // Wait for the initial resume...
       win.panelWin.gClient.addOneTimeListener("resumed", function() {
         win._dbgwin.DebuggerView.Variables.lazyEmpty = false;
+        win._dbgwin.DebuggerView.Variables.lazyAppend = false;
         aOnDebugging(tab, debuggee, win);
       });
     }, true);
   });
 }
 
 function debug_chrome(aURL, aOnClosing, aOnDebugging) {
   let tab = addTab(aURL, function() {
--- a/browser/devtools/framework/Toolbox.jsm
+++ b/browser/devtools/framework/Toolbox.jsm
@@ -314,16 +314,23 @@ Toolbox.prototype = {
     while (dockBox.firstChild) {
       dockBox.removeChild(dockBox.firstChild);
     }
 
     if (!this._target.isLocalTab) {
       return;
     }
 
+    let closeButton = this.doc.getElementById("toolbox-close");
+    if (this.hostType === this.HostType.WINDOW) {
+      closeButton.setAttribute("hidden", "true");
+    } else {
+      closeButton.removeAttribute("hidden");
+    }
+
     let sideEnabled = Services.prefs.getBoolPref(this._prefs.SIDE_ENABLED);
 
     for each (let position in this.HostType) {
       if (position == this.hostType ||
          (!sideEnabled && position == this.HostType.SIDE)) {
         continue;
       }
 
--- a/browser/devtools/framework/gDevTools.jsm
+++ b/browser/devtools/framework/gDevTools.jsm
@@ -337,16 +337,38 @@ let gDevToolsBrowser = {
     gDevToolsBrowser._addAllToolsToMenu(win.document);
 
     let tabContainer = win.document.getElementById("tabbrowser-tabs")
     tabContainer.addEventListener("TabSelect",
                                   gDevToolsBrowser._updateMenuCheckbox, false);
   },
 
   /**
+   * Add a <key> to <keyset id="devtoolsKeyset">.
+   * Appending a <key> element is not always enough. The <keyset> needs
+   * to be detached and reattached to make sure the <key> is taken into
+   * account (see bug 832984).
+   *
+   * @param {XULDocument} doc
+   *        The document to which keys are to be added
+   * @param {XULElement} or {DocumentFragment} keys
+   *        Keys to add
+   */
+  attachKeybindingsToBrowser: function DT_attachKeybindingsToBrowser(doc, keys) {
+    let devtoolsKeyset = doc.getElementById("devtoolsKeyset");
+    if (!devtoolsKeyset) {
+      devtoolsKeyset = doc.createElement("keyset");
+      devtoolsKeyset.setAttribute("id", "devtoolsKeyset");
+    }
+    devtoolsKeyset.appendChild(keys);
+    let mainKeyset = doc.getElementById("mainKeyset");
+    mainKeyset.parentNode.insertBefore(devtoolsKeyset, mainKeyset);
+  },
+
+  /**
    * Add the menuitem for a tool to all open browser windows.
    *
    * @param {object} toolDefinition
    *        properties of the tool to add
    */
   _addToolToWindows: function DT_addToolToWindows(toolDefinition) {
     // We need to insert the new tool in the right place, which means knowing
     // the tool that comes before the tool that we're trying to add
@@ -361,17 +383,17 @@ let gDevToolsBrowser = {
 
     for (let win of gDevToolsBrowser._trackedBrowserWindows) {
       let doc = win.document;
       let elements = gDevToolsBrowser._createToolMenuElements(toolDefinition, doc);
 
       doc.getElementById("mainCommandSet").appendChild(elements.cmd);
 
       if (elements.key) {
-        doc.getElementById("mainKeyset").appendChild(elements.key);
+        this.attachKeybindingsToBrowser(doc, elements.keys);
       }
 
       doc.getElementById("mainBroadcasterSet").appendChild(elements.bc);
 
       let amp = doc.getElementById("appmenu_webDeveloper_popup");
       if (amp) {
         let ref = (prevDef != null) ?
             doc.getElementById("appmenuitem_" + prevDef.id).nextSibling :
@@ -415,18 +437,17 @@ let gDevToolsBrowser = {
       fragBroadcasters.appendChild(elements.bc);
       fragAppMenuItems.appendChild(elements.appmenuitem);
       fragMenuItems.appendChild(elements.menuitem);
     }
 
     let mcs = doc.getElementById("mainCommandSet");
     mcs.appendChild(fragCommands);
 
-    let mks = doc.getElementById("mainKeyset");
-    mks.appendChild(fragKeys);
+    this.attachKeybindingsToBrowser(doc, fragKeys);
 
     let mbs = doc.getElementById("mainBroadcasterSet");
     mbs.appendChild(fragBroadcasters);
 
     let amp = doc.getElementById("appmenu_webDeveloper_popup");
     if (amp) {
       let amps = doc.getElementById("appmenu_devtools_separator");
       amp.insertBefore(fragAppMenuItems, amps);
--- a/browser/devtools/inspector/Highlighter.jsm
+++ b/browser/devtools/inspector/Highlighter.jsm
@@ -91,17 +91,17 @@ this.Highlighter = function Highlighter(
 
 Highlighter.prototype = {
   get selection() {
     return this.inspector.selection;
   },
 
   _init: function Highlighter__init()
   {
-    this.unlock = this.unlock.bind(this);
+    this.unlockAndFocus = this.unlockAndFocus.bind(this);
     this.updateInfobar = this.updateInfobar.bind(this);
     this.highlight = this.highlight.bind(this);
 
     let stack = this.browser.parentNode;
     this.win = this.browser.contentWindow;
     this._highlighting = false;
 
     this.highlighterContainer = this.chromeDoc.createElement("stack");
@@ -124,17 +124,17 @@ Highlighter.prototype = {
     // Insert the highlighter right after the browser
     stack.insertBefore(this.highlighterContainer, stack.childNodes[1]);
 
     this.buildInfobar(controlsBox);
 
     this.transitionDisabler = null;
     this.pageEventsMuter = null;
 
-    this.unlock();
+    this.unlockAndFocus();
 
     this.selection.on("new-node", this.highlight);
     this.selection.on("new-node", this.updateInfobar);
     this.selection.on("pseudoclass", this.updateInfobar);
     this.selection.on("attribute-changed", this.updateInfobar);
 
     this.onToolSelected = function(event, id) {
       if (id != "inspector") {
@@ -154,17 +154,17 @@ Highlighter.prototype = {
     this.highlight();
   },
 
   /**
    * Destroy the nodes. Remove listeners.
    */
   destroy: function Highlighter_destroy()
   {
-    this.inspectButton.removeEventListener("command", this.unlock);
+    this.inspectButton.removeEventListener("command", this.unlockAndFocus);
     this.inspectButton = null;
 
     this.toolbox.off("select", this.onToolSelected);
     this.toolbox = null;
 
     this.selection.off("new-node", this.highlight);
     this.selection.off("new-node", this.updateInfobar);
     this.selection.off("pseudoclass", this.updateInfobar);
@@ -292,25 +292,33 @@ Highlighter.prototype = {
    * Unlock the current node (if any), and select any node being hovered.
    */
   unlock: function() {
     if (this.locked === false) return;
     this.outline.removeAttribute("locked");
     this.nodeInfo.container.removeAttribute("locked");
     this.attachMouseListeners();
     this.locked = false;
-    this.chromeWin.focus();
     if (this.selection.isElementNode() &&
         this.selection.isConnected()) {
       this.showOutline();
     }
     this.emit("unlocked");
   },
 
   /**
+   * Focus the browser before unlocking.
+   */
+  unlockAndFocus: function Highlighter_unlockAndFocus() {
+    if (this.locked === false) return;
+    this.chromeWin.focus();
+    this.unlock();
+  },
+
+  /**
    * Hide the infobar
    */
    hideInfobar: function Highlighter_hideInfobar() {
      this.nodeInfo.container.setAttribute("force-transitions", "true");
      this.nodeInfo.container.setAttribute("hidden", "true");
    },
 
   /**
@@ -390,17 +398,17 @@ Highlighter.prototype = {
     pseudoClassesBox.textContent = "&nbsp;";
 
     // Create buttons
 
     this.inspectButton = this.chromeDoc.createElement("toolbarbutton");
     this.inspectButton.className = "highlighter-nodeinfobar-button highlighter-nodeinfobar-inspectbutton"
     let toolbarInspectButton = this.inspector.panelDoc.getElementById("inspector-inspect-toolbutton");
     this.inspectButton.setAttribute("tooltiptext", toolbarInspectButton.getAttribute("tooltiptext"));
-    this.inspectButton.addEventListener("command", this.unlock);
+    this.inspectButton.addEventListener("command", this.unlockAndFocus);
 
     let nodemenu = this.chromeDoc.createElement("toolbarbutton");
     nodemenu.setAttribute("type", "menu");
     nodemenu.className = "highlighter-nodeinfobar-button highlighter-nodeinfobar-menu"
     nodemenu.setAttribute("tooltiptext",
                           this.strings.GetStringFromName("nodeMenu.tooltiptext"));
 
     nodemenu.onclick = function() {
@@ -738,21 +746,19 @@ Highlighter.prototype = {
    *
    * @param nsIDOMEvent aEvent
    *        The DOM event.
    */
   handleClick: function Highlighter_handleClick(aEvent)
   {
     // Stop inspection when the user clicks on a node.
     if (aEvent.button == 0) {
-      let win = aEvent.target.ownerDocument.defaultView;
       this.lock();
       let node = this.selection.node;
       this.selection.setNode(node, "highlighter-lock");
-      win.focus();
       aEvent.preventDefault();
       aEvent.stopPropagation();
     }
   },
 
   /**
    * Handle mousemoves in panel.
    *
--- a/browser/devtools/inspector/InspectorPanel.jsm
+++ b/browser/devtools/inspector/InspectorPanel.jsm
@@ -56,16 +56,26 @@ InspectorPanel.prototype = {
 
     this.nodemenu = this.panelDoc.getElementById("inspector-node-popup");
     this.lastNodemenuItem = this.nodemenu.lastChild;
     this._setupNodeMenu = this._setupNodeMenu.bind(this);
     this._resetNodeMenu = this._resetNodeMenu.bind(this);
     this.nodemenu.addEventListener("popupshowing", this._setupNodeMenu, true);
     this.nodemenu.addEventListener("popuphiding", this._resetNodeMenu, true);
 
+    // Initialize the search related items
+    this.searchBox = this.panelDoc.getElementById("inspector-searchbox");
+    this._lastSearched = null;
+    this._searchResults = null;
+    this._searchIndex = 0;
+    this._onHTMLSearch = this._onHTMLSearch.bind(this);
+    this._onSearchKeypress = this._onSearchKeypress.bind(this);
+    this.searchBox.addEventListener("command", this._onHTMLSearch, true);
+    this.searchBox.addEventListener("keypress", this._onSearchKeypress, true);
+
     // Create an empty selection
     this._selection = new Selection();
     this.onNewSelection = this.onNewSelection.bind(this);
     this.selection.on("new-node", this.onNewSelection);
     this.onBeforeNewSelection = this.onBeforeNewSelection.bind(this);
     this.selection.on("before-new-node", this.onBeforeNewSelection);
     this.onDetached = this.onDetached.bind(this);
     this.selection.on("detached", this.onDetached);
@@ -75,25 +85,26 @@ InspectorPanel.prototype = {
     if (this.target.isLocalTab) {
       this.browser = this.target.tab.linkedBrowser;
       this.scheduleLayoutChange = this.scheduleLayoutChange.bind(this);
       this.browser.addEventListener("resize", this.scheduleLayoutChange, true);
 
       this.highlighter = new Highlighter(this.target, this, this._toolbox);
       let button = this.panelDoc.getElementById("inspector-inspect-toolbutton");
       button.hidden = false;
-      this.updateInspectorButton = function() {
+      this.onLockStateChanged = function() {
         if (this.highlighter.locked) {
           button.removeAttribute("checked");
+          this._toolbox.raise();
         } else {
           button.setAttribute("checked", "true");
         }
       }.bind(this);
-      this.highlighter.on("locked", this.updateInspectorButton);
-      this.highlighter.on("unlocked", this.updateInspectorButton);
+      this.highlighter.on("locked", this.onLockStateChanged);
+      this.highlighter.on("unlocked", this.onLockStateChanged);
 
       // Show a warning when the debugger is paused.
       // We show the warning only when the inspector
       // is selected.
       this.updateDebuggerPausedWarning = function() {
         let notificationBox = this._toolbox.getNotificationBox();
         let notification = notificationBox.getNotificationWithValue("inspector-script-paused");
         if (!notification && this._toolbox.currentToolId == "inspector" &&
@@ -356,53 +367,123 @@ InspectorPanel.prototype = {
       this.browser.removeEventListener("resize", this.scheduleLayoutChange, true);
       this.browser = null;
     }
 
     this.target.off("will-navigate", this.preventNavigateAway);
     this.target.off("navigate", this.onNavigatedAway);
 
     if (this.highlighter) {
-      this.highlighter.off("locked", this.updateInspectorButton);
-      this.highlighter.off("unlocked", this.updateInspectorButton);
+      this.highlighter.off("locked", this.onLockStateChanged);
+      this.highlighter.off("unlocked", this.onLockStateChanged);
       this.highlighter.destroy();
     }
 
     this.target.off("thread-paused", this.updateDebuggerPausedWarning);
     this.target.off("thread-resumed", this.updateDebuggerPausedWarning);
     this._toolbox.off("select", this.updateDebuggerPausedWarning);
 
     this._toolbox = null;
 
     this.sidebar.off("select", this._setDefaultSidebar);
     this.sidebar.destroy();
     this.sidebar = null;
 
     this.nodemenu.removeEventListener("popupshowing", this._setupNodeMenu, true);
     this.nodemenu.removeEventListener("popuphiding", this._resetNodeMenu, true);
+    this.searchBox.removeEventListener("command", this._onHTMLSearch, true);
+    this.searchBox.removeEventListener("keypress", this._onSearchKeypress, true);
     this.breadcrumbs.destroy();
     this.selection.off("new-node", this.onNewSelection);
     this.selection.off("before-new-node", this.onBeforeNewSelection);
     this.selection.off("detached", this.onDetached);
     this._destroyMarkup();
     this._selection.destroy();
     this._selection = null;
     this.panelWin.inspector = null;
     this.target = null;
     this.panelDoc = null;
     this.panelWin = null;
     this.breadcrumbs = null;
     this.lastNodemenuItem = null;
     this.nodemenu = null;
+    this.searchBox = null;
     this.highlighter = null;
 
     return Promise.resolve(null);
   },
 
   /**
+   * The command callback for the HTML search box. This function is
+   * automatically invoked as the user is typing.
+   */
+  _onHTMLSearch: function InspectorPanel__onHTMLSearch() {
+    let query = this.searchBox.value;
+    if (query == this._lastSearched) {
+      return;
+    }
+    this._lastSearched = query;
+    this._searchIndex = 0;
+
+    if (query.length == 0) {
+      this.searchBox.removeAttribute("filled");
+      this.searchBox.classList.remove("devtools-no-search-result");
+      return;
+    }
+
+    this.searchBox.setAttribute("filled", true);
+    this._searchResults = this.browser.contentDocument.querySelectorAll(query);
+    if (this._searchResults.length > 0) {
+      this.searchBox.classList.remove("devtools-no-search-result");
+      this.cancelLayoutChange();
+      this.selection.setNode(this._searchResults[0]);
+    } else {
+      this.searchBox.classList.add("devtools-no-search-result");
+    }
+  },
+
+  /**
+   * Search for the search box value as a query selector.
+   */
+  _onSearchKeypress: function InspectorPanel__onSearchKeypress(aEvent) {
+    let query = this.searchBox.value;
+    switch(aEvent.keyCode) {
+      case aEvent.DOM_VK_ENTER:
+      case aEvent.DOM_VK_RETURN:
+        if (query == this._lastSearched) {
+          this._searchIndex = (this._searchIndex + 1) % this._searchResults.length;
+        } else {
+          this._onHTMLSearch();
+          return;
+        }
+        break;
+
+      case aEvent.DOM_VK_UP:
+        if (--this._searchIndex < 0) {
+          this._searchIndex = this._searchResults.length - 1;
+        }
+        break;
+
+      case aEvent.DOM_VK_DOWN:
+        this._searchIndex = (this._searchIndex + 1) % this._searchResults.length;
+        break;
+
+      default:
+        return;
+    }
+
+    aEvent.preventDefault();
+    aEvent.stopPropagation();
+    this.cancelLayoutChange();
+    if (this._searchResults.length > 0) {
+      this.selection.setNode(this._searchResults[this._searchIndex]);
+    }
+  },
+
+  /**
    * Show the node menu.
    */
   showNodeMenu: function InspectorPanel_showNodeMenu(aButton, aPosition, aExtraItems) {
     if (aExtraItems) {
       for (let item of aExtraItems) {
         this.nodemenu.appendChild(item);
       }
     }
--- a/browser/devtools/inspector/inspector.xul
+++ b/browser/devtools/inspector/inspector.xul
@@ -11,16 +11,28 @@
  %inspectorDTD;
 ]>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <script type="application/javascript"
           src="chrome://global/content/viewSourceUtils.js"/>
 
+  <commandset>
+    <command id="nodeSearchCommand"
+      oncommand="inspector.searchBox.focus()"/>
+  </commandset>
+
+  <keyset>
+    <key id="nodeSearchKey"
+      key="&inspectorSearchHTML.key;"
+      modifiers="accel"
+      command="nodeSearchCommand"/>
+  </keyset>
+
   <popupset id="inspectorPopupSet">
     <!-- Used by the Markup Panel, the Highlighter and the Breadcrumbs -->
     <menupopup id="inspector-node-popup">
       <menuitem id="node-menu-copyinner"
         label="&inspectorHTMLCopyInner.label;"
         accesskey="&inspectorHTMLCopyInner.accesskey;"
         oncommand="inspector.copyInnerHTML()"/>
       <menuitem id="node-menu-copyouter"
@@ -49,20 +61,25 @@
     <vbox flex="1">
       <toolbar id="inspector-toolbar"
         class="devtools-toolbar"
         nowindowdrag="true">
         <toolbarbutton id="inspector-inspect-toolbutton"
           tooltiptext="&inspector.selectButton.tooltip;"
           class="devtools-toolbarbutton"
           hidden="true"
-          oncommand="inspector.highlighter.unlock()"/>
+          oncommand="inspector.highlighter.unlockAndFocus()"/>
         <arrowscrollbox id="inspector-breadcrumbs"
           flex="1" orient="horizontal"
           clicktoscroll="true"/>
+        <textbox id="inspector-searchbox"
+          type="search"
+          timeout="50"
+          class="devtools-searchinput"
+          placeholder="&inspectorSearchHTML.label;"/>
       </toolbar>
       <vbox flex="1" id="markup-box">
       </vbox>
     </vbox>
     <splitter class="devtools-side-splitter"/>
     <tabbox id="inspector-sidebar" class="devtools-sidebar-tabs" hidden="true">
       <tabs/>
       <tabpanels flex="1"/>
--- a/browser/devtools/inspector/test/Makefile.in
+++ b/browser/devtools/inspector/test/Makefile.in
@@ -31,14 +31,16 @@ include $(topsrcdir)/config/rules.mk
 		$(filter disabled-temporarily--bug-816990, browser_inspector_sidebarstate.js) \
 		browser_inspector_pseudoclass_lock.js \
 		browser_inspector_cmd_inspect.js \
 		browser_inspector_cmd_inspect.html \
 		browser_inspector_highlighter_autohide.js \
 		browser_inspector_changes.js \
 		browser_inspector_bug_674871.js \
 		browser_inspector_bug_817558_delete_node.js \
+		browser_inspector_bug_650804_search.js \
+		browser_inspector_bug_650804_search.html \
 		head.js \
 		helpers.js \
 		$(NULL)
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/inspector/test/browser_inspector_bug_650804_search.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <title>Inspector Search Box Test</title>
+</head>
+<body>
+
+  <!-- This is a list of 0 h1 elements -->
+
+  <!-- This is a list of 2 div elements -->
+  <div id="d1">Hello, I'm a div</div>
+  <div id="d2" class="c1">Hello, I'm another div</div>
+
+  <!-- This is a list of 2 span elements -->
+  <span id="s1">Hello, I'm a span</span>
+  <span class="c1" id="s2">And me</span>
+
+  <!-- This is a collection of various things that match only once -->
+  <p class="c1" id="p1">.someclass</p>
+  <p id="p2">#someid</p>
+  <button id="b1" disabled>button[disabled]</button>
+  <p id="p3" class="c2"><strong>p&gt;strong</strong></p>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/inspector/test/browser_inspector_bug_650804_search.js
@@ -0,0 +1,115 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test()
+{
+  waitForExplicitFinish();
+
+  let inspector, searchBox, state;
+  let keypressStates = [3,4,8,18,19,20,21,22];
+
+  // The various states of the inspector: [key, id, isValid]
+  // [
+  //  what key to press,
+  //  what id should be selected after the keypress,
+  //  is the searched text valid selector
+  // ]
+  let keyStates = [
+    ["d", "b1", false],
+    ["i", "b1", false],
+    ["v", "d1", true],
+    ["VK_DOWN", "d2", true],
+    ["VK_ENTER", "d1", true],
+    [".", "d1", true],
+    ["c", "d1", false],
+    ["1", "d2", true],
+    ["VK_DOWN", "d2", true],
+    ["VK_BACK_SPACE", "d2", false],
+    ["VK_BACK_SPACE", "d2", false],
+    ["VK_BACK_SPACE", "d1", true],
+    ["VK_BACK_SPACE", "d1", false],
+    ["VK_BACK_SPACE", "d1", false],
+    ["VK_BACK_SPACE", "d1", true],
+    [".", "d1", true],
+    ["c", "d1", false],
+    ["1", "d2", true],
+    ["VK_DOWN", "s2", true],
+    ["VK_DOWN", "p1", true],
+    ["VK_UP", "s2", true],
+    ["VK_UP", "d2", true],
+    ["VK_UP", "p1", true],
+    ["VK_BACK_SPACE", "p1", false],
+    ["2", "p3", true],
+    ["VK_BACK_SPACE", "p3", false],
+    ["VK_BACK_SPACE", "p3", false],
+    ["VK_BACK_SPACE", "p3", true],
+    ["r", "p3", false],
+  ];
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onload() {
+    gBrowser.selectedBrowser.removeEventListener("load", onload, true);
+    waitForFocus(setupTest, content);
+  }, true);
+
+  content.location = "http://mochi.test:8888/browser/browser/devtools/inspector/test/browser_inspector_bug_650804_search.html";
+
+  function $(id) {
+    if (id == null) return null;
+    return content.document.getElementById(id);
+  }
+
+  function setupTest()
+  {
+    openInspector(startTest);
+  }
+
+  function startTest(aInspector)
+  {
+    inspector = aInspector;
+    inspector.selection.setNode($("b1"));
+    searchBox =
+      inspector.panelWin.document.getElementById("inspector-searchbox");
+
+    focusSearchBoxUsingShortcut(inspector.panelWin, function() {
+      searchBox.addEventListener("command", checkState, true);
+      searchBox.addEventListener("keypress", checkState, true);
+      checkStateAndMoveOn(0);
+    });
+  }
+
+  function checkStateAndMoveOn(index) {
+    if (index == keyStates.length) {
+      finishUp();
+      return;
+    }
+
+    let [key, id, isValid] = keyStates[index];
+    state = index;
+
+    info("pressing key " + key + " to get id " + id);
+    EventUtils.synthesizeKey(key, {}, inspector.panelWin);
+  }
+
+  function checkState(event) {
+    if (event.type == "keypress" && keypressStates.indexOf(state) == -1) {
+      return;
+    }
+    executeSoon(function() {
+      let [key, id, isValid] = keyStates[state];
+      info(inspector.selection.node.id + " is selected with text " +
+           inspector.searchBox.value);
+      is(inspector.selection.node, $(id),
+         "Correct node is selected for state " + state);
+      is(!searchBox.classList.contains("devtools-no-search-result"), isValid,
+         "Correct searchbox result state for state " + state);
+      checkStateAndMoveOn(state + 1);
+    });
+  }
+
+  function finishUp() {
+    searchBox = null;
+    gBrowser.removeCurrentTab();
+    finish();
+  }
+}
--- a/browser/devtools/inspector/test/browser_inspector_highlighter.js
+++ b/browser/devtools/inspector/test/browser_inspector_highlighter.js
@@ -52,17 +52,17 @@ function createDocument()
 }
 
 function setupHighlighterTests()
 {
   h1 = doc.querySelector("h1");
   ok(h1, "we have the header");
 
   let i = getActiveInspector();
-  i.highlighter.unlock();
+  i.highlighter.unlockAndFocus();
   i.highlighter.outline.setAttribute("disable-transitions", "true");
 
   executeSoon(function() {
     i.selection.once("new-node", performTestComparisons);
     EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content);
   });
 }
 
--- a/browser/devtools/inspector/test/head.js
+++ b/browser/devtools/inspector/test/head.js
@@ -1,13 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Cu = Components.utils;
+const Ci = Components.interfaces;
+const Cc = Components.classes;
 let tempScope = {};
 Cu.import("resource:///modules/devtools/LayoutHelpers.jsm", tempScope);
 let LayoutHelpers = tempScope.LayoutHelpers;
 Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
 let TargetFactory = tempScope.TargetFactory;
 Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope);
 let console = tempScope.console;
 
@@ -106,8 +108,41 @@ function synthesizeKeyFromKeyTag(aKeyId)
     ctrlKey: modifiersAttr.match("ctrl"),
     altKey: modifiersAttr.match("alt"),
     metaKey: modifiersAttr.match("meta"),
     accelKey: modifiersAttr.match("accel")
   }
 
   EventUtils.synthesizeKey(name, modifiers);
 }
+
+function focusSearchBoxUsingShortcut(panelWin, callback) {
+  panelWin.focus();
+  let key = panelWin.document.getElementById("nodeSearchKey");
+  isnot(key, null, "Successfully retrieved the <key> node");
+
+  let modifiersAttr = key.getAttribute("modifiers");
+
+  let name = null;
+
+  if (key.getAttribute("keycode")) {
+    name = key.getAttribute("keycode");
+  } else if (key.getAttribute("key")) {
+    name = key.getAttribute("key");
+  }
+
+  isnot(name, null, "Successfully retrieved keycode/key");
+
+  let modifiers = {
+    shiftKey: modifiersAttr.match("shift"),
+    ctrlKey: modifiersAttr.match("ctrl"),
+    altKey: modifiersAttr.match("alt"),
+    metaKey: modifiersAttr.match("meta"),
+    accelKey: modifiersAttr.match("accel")
+  }
+
+  let searchBox = panelWin.document.getElementById("inspector-searchbox");
+  searchBox.addEventListener("focus", function onFocus() {
+    searchBox.removeEventListener("focus", onFocus, false);
+    callback && callback();
+  }, false);
+  EventUtils.synthesizeKey(name, modifiers);
+}
--- a/browser/devtools/layoutview/view.js
+++ b/browser/devtools/layoutview/view.js
@@ -117,20 +117,20 @@ LayoutView.prototype = {
    */
   onNewNode: function LV_onNewNode() {
     if (this.isActive() &&
         this.inspector.selection.isConnected() &&
         this.inspector.selection.isElementNode() &&
         this.inspector.selection.reason != "highlighter") {
       this.cssLogic.highlight(this.inspector.selection.node);
       this.undim();
-      this.update();
     } else {
       this.dim();
     }
+    this.update();
   },
 
   /**
    * Highlighter 'locked' event handler
    */
   onHighlighterLocked: function LV_onHighlighterLocked() {
     this.cssLogic.highlight(this.inspector.selection.node);
     this.undim();
--- a/browser/devtools/markupview/MarkupView.jsm
+++ b/browser/devtools/markupview/MarkupView.jsm
@@ -779,16 +779,19 @@ function MarkupContainer(aMarkupView, aN
 
   this.codeBox.insertBefore(this.editor.elt, this.children);
 
   this.editor.elt.addEventListener("mousedown", function(evt) {
     this.markup.navigate(this);
   }.bind(this), false);
 
   if (this.editor.closeElt) {
+    this.editor.closeElt.addEventListener("mousedown", function(evt) {
+      this.markup.navigate(this);
+    }.bind(this), false);
     this.codeBox.appendChild(this.editor.closeElt);
   }
 
 }
 
 MarkupContainer.prototype = {
   /**
    * True if the current node has children.  The MarkupView
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -694,18 +694,19 @@ var Scratchpad = {
         converter.charset = "UTF-8";
         content = NetUtil.readInputStreamToString(aInputStream,
                                                   aInputStream.available());
         content = converter.ConvertToUnicode(content);
 
         // Check to see if the first line is a mode-line comment.
         let line = content.split("\n")[0];
         let modeline = self._scanModeLine(line);
+        let chrome = Services.prefs.getBoolPref(DEVTOOLS_CHROME_ENABLED);
 
-        if (modeline["-sp-context"] === "browser") {
+        if (chrome && modeline["-sp-context"] === "browser") {
           self.setBrowserContext();
         }
 
         self.setText(content);
         self.editor.resetUndo();
       }
       else if (!aSilentError) {
         window.alert(self.strings.GetStringFromName("openFile.failed"));
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug_644413_modeline.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug_644413_modeline.js
@@ -3,28 +3,24 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let tempScope = {};
 Cu.import("resource://gre/modules/NetUtil.jsm", tempScope);
 Cu.import("resource://gre/modules/FileUtils.jsm", tempScope);
 let NetUtil = tempScope.NetUtil;
 let FileUtils = tempScope.FileUtils;
 
-// Reference to the Scratchpad object.
-let gScratchpad;
 
-// Reference to the temporary nsIFile we will work with.
-let gFile;
+let gScratchpad; // Reference to the Scratchpad object.
+let gFile; // Reference to the temporary nsIFile we will work with.
+let DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
 
 // The temporary file content.
 let gFileContent = "function main() { return 0; }";
 
-const SCRATCHPAD_CONTEXT_CONTENT = 1;
-const SCRATCHPAD_CONTEXT_BROWSER = 2;
-
 function test() {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
     gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
     openScratchpad(runTests);
   }, true);
@@ -67,16 +63,30 @@ function runTests() {
 
     gFile = aFile;
     gScratchpad.importFromFile(gFile.QueryInterface(Ci.nsILocalFile), true, fileImported);
   });
 }
 
 function fileImported(status, content) {
   ok(Components.isSuccessCode(status), "File was imported successfully");
-  is(gScratchpad.executionContext, SCRATCHPAD_CONTEXT_BROWSER);
+
+  // Since devtools.chrome.enabled is off, Scratchpad should still be in
+  // the content context.
+  is(gScratchpad.executionContext, gScratchpadWindow.SCRATCHPAD_CONTEXT_CONTENT);
+
+  // Set the pref and try again.
+  Services.prefs.setBoolPref(DEVTOOLS_CHROME_ENABLED, true);
 
-  gFile.remove(false);
-  gFile = null;
-  gScratchpad = null;
-  finish();
+  gScratchpad.importFromFile(gFile.QueryInterface(Ci.nsILocalFile), true, function(status, content) {
+    ok(Components.isSuccessCode(status), "File was imported successfully");
+    is(gScratchpad.executionContext, gScratchpadWindow.SCRATCHPAD_CONTEXT_BROWSER);
+
+    gFile.remove(false);
+    gFile = null;
+    gScratchpad = null;
+    finish();
+  });
 }
 
+registerCleanupFunction(function () {
+  Services.prefs.clearUserPref(DEVTOOLS_CHROME_ENABLED);
+});
\ No newline at end of file
--- a/browser/devtools/shared/DeveloperToolbar.jsm
+++ b/browser/devtools/shared/DeveloperToolbar.jsm
@@ -29,16 +29,20 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 XPCOMUtils.defineLazyGetter(this, "prefBranch", function() {
   let prefService = Components.classes["@mozilla.org/preferences-service;1"]
           .getService(Components.interfaces.nsIPrefService);
   return prefService.getBranch(null)
           .QueryInterface(Components.interfaces.nsIPrefBranch2);
 });
 
+XPCOMUtils.defineLazyGetter(this, "toolboxStrings", function () {
+  return Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties");
+});
+
 /**
  * A collection of utilities to help working with commands
  */
 this.CommandUtils = {
   /**
    * Read a toolbarSpec from preferences
    * @param aPref The name of the preference to read
    */
@@ -158,19 +162,22 @@ this.DeveloperToolbar = function Develop
   this._element = aToolbarElement;
   this._element.hidden = true;
   this._doc = this._element.ownerDocument;
 
   this._lastState = NOTIFICATIONS.HIDE;
   this._pendingShowCallback = undefined;
   this._pendingHide = false;
   this._errorsCount = {};
+  this._warningsCount = {};
   this._errorListeners = {};
   this._errorCounterButton = this._doc
                              .getElementById("developer-toolbar-toolbox-button");
+  this._errorCounterButton._defaultTooltipText =
+    this._errorCounterButton.getAttribute("tooltiptext");
 
   try {
     CmdCommands.refreshAutoCommands(aChromeWindow);
   }
   catch (ex) {
     console.error(ex);
   }
 }
@@ -392,16 +399,17 @@ DeveloperToolbar.prototype._initErrorsCo
   let window = aTab.linkedBrowser.contentWindow;
   let listener = new PageErrorListener(window, {
     onPageError: this._onPageError.bind(this, tabId),
   });
   listener.init();
 
   this._errorListeners[tabId] = listener;
   this._errorsCount[tabId] = 0;
+  this._warningsCount[tabId] = 0;
 
   let messages = listener.getCachedMessages();
   messages.forEach(this._onPageError.bind(this, tabId));
 
   this._updateErrorsCount();
 };
 
 /**
@@ -410,24 +418,25 @@ DeveloperToolbar.prototype._initErrorsCo
  *
  * @private
  * @param nsIDOMNode aTab the xul:tab for which you want to stop tracking the
  * number of errors.
  */
 DeveloperToolbar.prototype._stopErrorsCount = function DT__stopErrorsCount(aTab)
 {
   let tabId = aTab.linkedPanel;
-  if (!(tabId in this._errorsCount)) {
+  if (!(tabId in this._errorsCount) || !(tabId in this._warningsCount)) {
     this._updateErrorsCount();
     return;
   }
 
   this._errorListeners[tabId].destroy();
   delete this._errorListeners[tabId];
   delete this._errorsCount[tabId];
+  delete this._warningsCount[tabId];
 
   this._updateErrorsCount();
 };
 
 /**
  * Hide the developer toolbar.
  */
 DeveloperToolbar.prototype.hide = function DT_hide()
@@ -545,23 +554,25 @@ DeveloperToolbar.prototype.handleEvent =
  * @param string aTabId the ID of the tab from where the page error comes.
  * @param object aPageError the page error object received from the
  * PageErrorListener.
  */
 DeveloperToolbar.prototype._onPageError =
 function DT__onPageError(aTabId, aPageError)
 {
   if (aPageError.category == "CSS Parser" ||
-      aPageError.category == "CSS Loader" ||
-      (aPageError.flags & aPageError.warningFlag) ||
+      aPageError.category == "CSS Loader") {
+    return;
+  }
+  if ((aPageError.flags & aPageError.warningFlag) ||
       (aPageError.flags & aPageError.strictFlag)) {
-    return; // just a CSS or JS warning
+    this._warningsCount[aTabId]++;
+  } else {
+    this._errorsCount[aTabId]++;
   }
-
-  this._errorsCount[aTabId]++;
   this._updateErrorsCount(aTabId);
 };
 
 /**
  * The |beforeunload| event handler. This function resets the errors count when
  * a different page starts loading.
  *
  * @private
@@ -574,18 +585,19 @@ function DT__onPageBeforeUnload(aEvent)
   if (window.top !== window) {
     return;
   }
 
   let tabs = this._chromeWindow.getBrowser().tabs;
   Array.prototype.some.call(tabs, function(aTab) {
     if (aTab.linkedBrowser.contentWindow === window) {
       let tabId = aTab.linkedPanel;
-      if (tabId in this._errorsCount) {
+      if (tabId in this._errorsCount || tabId in this._warningsCount) {
         this._errorsCount[tabId] = 0;
+        this._warningsCount[tabId] = 0;
         this._updateErrorsCount(tabId);
       }
       return true;
     }
     return false;
   }, this);
 };
 
@@ -602,36 +614,41 @@ DeveloperToolbar.prototype._updateErrors
 function DT__updateErrorsCount(aChangedTabId)
 {
   let tabId = this._chromeWindow.getBrowser().selectedTab.linkedPanel;
   if (aChangedTabId && tabId != aChangedTabId) {
     return;
   }
 
   let errors = this._errorsCount[tabId];
-
+  let warnings = this._warningsCount[tabId];
+  let btn = this._errorCounterButton;
   if (errors) {
-    this._errorCounterButton.setAttribute("error-count", errors);
+    let tooltiptext = toolboxStrings.formatStringFromName("toolboxDockButtons.errorsCount.tooltip", [errors, warnings], 2);
+    btn.setAttribute("error-count", errors);
+    btn.setAttribute("tooltiptext", tooltiptext);
   } else {
-    this._errorCounterButton.removeAttribute("error-count");
+    btn.removeAttribute("error-count");
+    btn.setAttribute("tooltiptext", btn._defaultTooltipText);
   }
 };
 
 /**
  * Reset the errors counter for the given tab.
  *
  * @param nsIDOMElement aTab The xul:tab for which you want to reset the page
  * errors counters.
  */
 DeveloperToolbar.prototype.resetErrorsCount =
 function DT_resetErrorsCount(aTab)
 {
   let tabId = aTab.linkedPanel;
-  if (tabId in this._errorsCount) {
+  if (tabId in this._errorsCount || tabId in this._warningsCount) {
     this._errorsCount[tabId] = 0;
+    this._warningsCount[tabId] = 0;
     this._updateErrorsCount(tabId);
   }
 };
 
 /**
  * Panel to handle command line output.
  *
  * There is a tooltip bug on Windows and OSX that prevents tooltips from being
--- a/browser/devtools/shared/VariablesView.jsm
+++ b/browser/devtools/shared/VariablesView.jsm
@@ -2,50 +2,62 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
 const LAZY_EMPTY_DELAY = 150; // ms
+const LAZY_EXPAND_DELAY = 50; // ms
+const LAZY_APPEND_DELAY = 100; // ms
+const LAZY_APPEND_BATCH = 100; // nodes
 const SEARCH_ACTION_MAX_DELAY = 1000; // ms
 
-Components.utils.import('resource://gre/modules/Services.jsm');
+Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this,
+  "WebConsoleUtils", "resource://gre/modules/devtools/WebConsoleUtils.jsm");
+
 this.EXPORTED_SYMBOLS = ["VariablesView", "create"];
 
 /**
+ * Debugger localization strings.
+ */
+const STR = Services.strings.createBundle(DBG_STRINGS_URI);
+
+/**
  * A tree view for inspecting scopes, objects and properties.
  * Iterable via "for (let [id, scope] in instance) { }".
  * Requires the devtools common.css and debugger.css skin stylesheets.
  *
  * To allow replacing variable or property values in this view, provide an
- * "eval" function property. To allow replacing variable or property values,
+ * "eval" function property. To allow replacing variable or property names,
  * provide a "switch" function. To handle deleting variables or properties,
  * provide a "delete" function.
  *
  * @param nsIDOMNode aParentNode
  *        The parent node to hold this view.
  */
 this.VariablesView = function VariablesView(aParentNode) {
   this._store = new Map();
   this._prevHierarchy = new Map();
   this._currHierarchy = new Map();
+
   this._parent = aParentNode;
   this._appendEmptyNotice();
 
   this._onSearchboxInput = this._onSearchboxInput.bind(this);
   this._onSearchboxKeyPress = this._onSearchboxKeyPress.bind(this);
 
   // Create an internal list container.
   this._list = this.document.createElement("vbox");
   this._parent.appendChild(this._list);
-}
+};
 
 VariablesView.prototype = {
   /**
    * Helper setter for populating this container with a raw object.
    *
    * @param object aData
    *        The raw object to display. You can only provide this object
    *        if you want the variables view to work in sync mode.
@@ -60,17 +72,17 @@ VariablesView.prototype = {
    *
    * @param string aName
    *        The scope's name (e.g. "Local", "Global" etc.).
    * @return Scope
    *         The newly created Scope instance.
    */
   addScope: function VV_addScope(aName = "") {
     this._removeEmptyNotice();
-    this._toggleSearch(true);
+    this._toggleSearchVisibility(true);
 
     let scope = new Scope(this, aName);
     this._store.set(scope.id, scope);
     this._currHierarchy.set(aName, scope);
     scope.header = !!aName;
     return scope;
   },
 
@@ -96,17 +108,17 @@ VariablesView.prototype = {
     let firstChild;
 
     while (firstChild = list.firstChild) {
       list.removeChild(firstChild);
     }
 
     this._store = new Map();
     this._appendEmptyNotice();
-    this._toggleSearch(false);
+    this._toggleSearchVisibility(false);
   },
 
   /**
    * Emptying this container and rebuilding it immediately afterwards would
    * result in a brief redraw flicker, because the previously expanded nodes
    * may get asynchronously re-expanded, after fetching the prototype and
    * properties from a server.
    *
@@ -115,58 +127,146 @@ VariablesView.prototype = {
    * is kept around for a short period of time, hopefully accounting for the
    * data fetching delay. In the meantime, any operations can be executed
    * normally.
    *
    * @see VariablesView.empty
    * @see VariablesView.commitHierarchy
    */
   _emptySoon: function VV__emptySoon(aTimeout) {
-    let window = this.window;
-    let document = this.document;
-
     let prevList = this._list;
     let currList = this._list = this.document.createElement("vbox");
     this._store = new Map();
 
-    this._emptyTimeout = window.setTimeout(function() {
+    this._emptyTimeout = this.window.setTimeout(function() {
       this._emptyTimeout = null;
 
       this._parent.removeChild(prevList);
       this._parent.appendChild(currList);
 
       if (!this._store.size) {
         this._appendEmptyNotice();
-        this._toggleSearch(false);
+        this._toggleSearchVisibility(false);
       }
     }.bind(this), aTimeout);
   },
 
   /**
+   * Specifies if this view may be emptied lazily.
+   * @see VariablesView.prototype.empty
+   */
+  lazyEmpty: false,
+
+  /**
+   * Specifies if nodes in this view may be added lazily.
+   * @see Scope.prototype._lazyAppend
+   */
+  lazyAppend: true,
+
+  /**
+   * Function called each time a variable or property's value is changed via
+   * user interaction. If null, then value changes are disabled.
+   *
+   * This property is applied recursively onto each scope in this view and
+   * affects only the child nodes when they're created.
+   */
+  eval: null,
+
+  /**
+   * Function called each time a variable or property's name is changed via
+   * user interaction. If null, then name changes are disabled.
+   *
+   * This property is applied recursively onto each scope in this view and
+   * affects only the child nodes when they're created.
+   */
+  switch: null,
+
+  /**
+   * Function called each time a variable or property is deleted via
+   * user interaction. If null, then deletions are disabled.
+   *
+   * This property is applied recursively onto each scope in this view and
+   * affects only the child nodes when they're created.
+   */
+  delete: null,
+
+  /**
+   * The tooltip text shown on a variable or property's value if an |eval|
+   * function is provided, in order to change the variable or property's value.
+   *
+   * This flag is applied recursively onto each scope in this view and
+   * affects only the child nodes when they're created.
+   */
+  editableValueTooltip: STR.GetStringFromName("variablesEditableValueTooltip"),
+
+  /**
+   * The tooltip text shown on a variable or property's name if a |switch|
+   * function is provided, in order to change the variable or property's name.
+   *
+   * This flag is applied recursively onto each scope in this view and
+   * affects only the child nodes when they're created.
+   */
+  editableNameTooltip: STR.GetStringFromName("variablesEditableNameTooltip"),
+
+  /**
+   * The tooltip text shown on a variable or property's delete button if a
+   * |delete| function is provided, in order to delete the variable or property.
+   *
+   * This flag is applied recursively onto each scope in this view and
+   * affects only the child nodes when they're created.
+   */
+  deleteButtonTooltip: STR.GetStringFromName("variablesCloseButtonTooltip"),
+
+  /**
+   * Specifies if the configurable, enumerable or writable tooltip should be
+   * shown whenever a variable or property descriptor is available.
+   *
+   * This flag is applied recursively onto each scope in this view and
+   * affects only the child nodes when they're created.
+   */
+  descriptorTooltip: true,
+
+  /**
+   * Specifies the context menu attribute set on variables and properties.
+   *
+   * This flag is applied recursively onto each scope in this view and
+   * affects only the child nodes when they're created.
+   */
+  contextMenuId: "",
+
+  /**
+   * The separator label between the variables or properties name and value.
+   *
+   * This flag is applied recursively onto each scope in this view and
+   * affects only the child nodes when they're created.
+   */
+  separatorStr: STR.GetStringFromName("variablesSeparatorLabel"),
+
+  /**
    * Specifies if enumerable properties and variables should be displayed.
    * These variables and properties are visible by default.
    * @param boolean aFlag
    */
   set enumVisible(aFlag) {
     this._enumVisible = aFlag;
 
-    for (let [, scope] in this) {
+    for (let [, scope] of this._store) {
       scope._enumVisible = aFlag;
     }
   },
 
   /**
    * Specifies if non-enumerable properties and variables should be displayed.
    * These variables and properties are visible by default.
    * @param boolean aFlag
    */
   set nonEnumVisible(aFlag) {
     this._nonEnumVisible = aFlag;
 
-    for (let [, scope] in this) {
+    for (let [, scope] of this._store) {
       scope._nonEnumVisible = aFlag;
     }
   },
 
   /**
    * Specifies if only enumerable properties and variables should be displayed.
    * Both types of these variables and properties are visible by default.
    * @param boolean aFlag
@@ -177,81 +277,129 @@ VariablesView.prototype = {
       this.nonEnumVisible = false;
     } else {
       this.enumVisible = true;
       this.nonEnumVisible = true;
     }
   },
 
   /**
-   * Enables variable and property searching in this view.
+   * Sets if the variable and property searching is enabled.
+   * @param boolean aFlag
+   */
+  set searchEnabled(aFlag) aFlag ? this._enableSearch() : this._disableSearch(),
+
+  /**
+   * Gets if the variable and property searching is enabled.
+   * @return boolean
+   */
+  get searchEnabled() !!this._searchboxContainer,
+
+  /**
+   * Sets the text displayed for the searchbox in this container.
+   * @param string aValue
    */
-  enableSearch: function VV_enableSearch() {
+  set searchPlaceholder(aValue) {
+    if (this._searchboxNode) {
+      this._searchboxNode.setAttribute("placeholder", aValue);
+    }
+    this._searchboxPlaceholder = aValue;
+  },
+
+  /**
+   * Gets the text displayed for the searchbox in this container.
+   * @return string
+   */
+  get searchPlaceholder() this._searchboxPlaceholder,
+
+  /**
+   * Enables variable and property searching in this view.
+   * Use the "searchEnabled" setter to enable searching.
+   */
+  _enableSearch: function VV__enableSearch() {
     // If searching was already enabled, no need to re-enable it again.
     if (this._searchboxContainer) {
       return;
     }
     let document = this.document;
     let ownerView = this._parent.parentNode;
 
     let container = this._searchboxContainer = document.createElement("hbox");
     container.className = "devtools-toolbar";
+
+    // Hide the variables searchbox container if there are no variables or
+    // properties to display.
     container.hidden = !this._store.size;
 
     let searchbox = this._searchboxNode = document.createElement("textbox");
     searchbox.className = "variables-searchinput devtools-searchinput";
     searchbox.setAttribute("placeholder", this._searchboxPlaceholder);
     searchbox.setAttribute("type", "search");
     searchbox.setAttribute("flex", "1");
     searchbox.addEventListener("input", this._onSearchboxInput, false);
     searchbox.addEventListener("keypress", this._onSearchboxKeyPress, false);
 
     container.appendChild(searchbox);
     ownerView.insertBefore(container, this._parent);
   },
 
   /**
    * Disables variable and property searching in this view.
+   * Use the "searchEnabled" setter to disable searching.
    */
-  disableSearch: function VV_disableSearch() {
+  _disableSearch: function VV__disableSearch() {
     // If searching was already disabled, no need to re-disable it again.
     if (!this._searchboxContainer) {
       return;
     }
     this._searchboxContainer.parentNode.removeChild(this._searchboxContainer);
     this._searchboxNode.addEventListener("input", this._onSearchboxInput, false);
     this._searchboxNode.addEventListener("keypress", this._onSearchboxKeyPress, false);
 
     this._searchboxContainer = null;
     this._searchboxNode = null;
   },
 
   /**
-   * Sets the variables searchbox hidden or visible. It's hidden by default.
+   * Sets the variables searchbox container hidden or visible.
+   * It's hidden by default.
    *
    * @param boolean aVisibleFlag
    *        Specifies the intended visibility.
    */
-  _toggleSearch: function VV__toggleSearch(aVisibleFlag) {
+  _toggleSearchVisibility: function VV__toggleSearchVisibility(aVisibleFlag) {
     // If searching was already disabled, there's no need to hide it.
     if (!this._searchboxContainer) {
       return;
     }
     this._searchboxContainer.hidden = !aVisibleFlag;
   },
 
   /**
-   * Sets if the variable and property searching is enabled.
+   * Listener handling the searchbox input event.
    */
-  set searchEnabled(aFlag) aFlag ? this.enableSearch() : this.disableSearch(),
+  _onSearchboxInput: function VV__onSearchboxInput() {
+    this.performSearch(this._searchboxNode.value);
+  },
 
   /**
-   * Gets if the variable and property searching is enabled.
+   * Listener handling the searchbox key press event.
    */
-  get searchEnabled() !!this._searchboxContainer,
+  _onSearchboxKeyPress: function VV__onSearchboxKeyPress(e) {
+    switch(e.keyCode) {
+      case e.DOM_VK_RETURN:
+      case e.DOM_VK_ENTER:
+        this._onSearchboxInput();
+        return;
+      case e.DOM_VK_ESCAPE:
+        this._searchboxNode.value = "";
+        this._onSearchboxInput();
+        return;
+    }
+  },
 
   /**
    * Allows searches to be scheduled and delayed to avoid redundant calls.
    */
   delayedSearch: true,
 
   /**
    * Schedules searching for variables or properties matching the query.
@@ -282,81 +430,92 @@ VariablesView.prototype = {
     this._searchFunction = null;
     this._startSearch(aQuery);
   },
 
   /**
    * Performs a case insensitive search for variables or properties matching
    * the query, and hides non-matched items.
    *
+   * If aQuery is empty string, then all the scopes are unhidden and expanded,
+   * while the available variables and properties inside those scopes are
+   * just unhidden.
+   *
+   * If aQuery is null or undefined, then all the scopes are just unhidden,
+   * and the available variables and properties inside those scopes are also
+   * just unhidden.
+   *
    * @param string aQuery
    *        The variable or property to search for.
    */
   _startSearch: function VV__startSearch(aQuery) {
-    for (let [, scope] in this) {
+    for (let [, scope] of this._store) {
       switch (aQuery) {
         case "":
           scope.expand();
           // fall through
         case null:
+        case undefined:
           scope._performSearch("");
           break;
         default:
           scope._performSearch(aQuery.toLowerCase());
           break;
       }
     }
   },
 
   /**
    * Expands the first search results in this container.
    */
   expandFirstSearchResults: function VV_expandFirstSearchResults() {
-    for (let [, scope] in this) {
-      for (let [, variable] in scope) {
+    for (let [, scope] of this._store) {
+      for (let [, variable] of scope._store) {
         if (variable._isMatch) {
           variable.expand();
           break;
         }
       }
     }
   },
 
   /**
-   * Sets the text displayed for the searchbox in this container.
-   * @param string aValue
+   * Searches for the scope in this container displayed by the specified node.
+   *
+   * @param nsIDOMNode aNode
+   *        The node to search for.
+   * @return Scope
+   *         The matched scope, or null if nothing is found.
    */
-  set searchPlaceholder(aValue) {
-    if (this._searchboxNode) {
-      this._searchboxNode.setAttribute("placeholder", aValue);
+  getScopeForNode: function VV_getScopeForNode(aNode) {
+    for (let [, scope] of this._store) {
+      if (scope._target == aNode) {
+        return scope;
+      }
     }
-    this._searchboxPlaceholder = aValue;
+    return null;
   },
 
   /**
-   * Listener handling the searchbox input event.
-   */
-  _onSearchboxInput: function VV__onSearchboxInput() {
-    this.performSearch(this._searchboxNode.value);
-  },
-
-  /**
-   * Listener handling the searchbox key press event.
+   * Recursively searches all the scopes for the variable or property
+   * displayed by the specified node.
+   *
+   * @param nsIDOMNode aNode
+   *        The node to search for.
+   * @return Variable | Property
+   *         The matched variable or property, or null if nothing is found.
    */
-  _onSearchboxKeyPress: function VV__onSearchboxKeyPress(e) {
-    switch(e.keyCode) {
-      case e.DOM_VK_RETURN:
-      case e.DOM_VK_ENTER:
-        this._onSearchboxInput();
-        return;
-      case e.DOM_VK_ESCAPE:
-        this._searchboxNode.value = "";
-        this._onSearchboxInput();
-        return;
+  getVariableOrPropertyForNode: function VV_getVariableOrPropertyForNode(aNode) {
+    for (let [, scope] of this._store) {
+      let match = scope.find(aNode);
+      if (match) {
+        return match;
+      }
     }
+    return null;
   },
 
   /**
    * Sets the text displayed in this container when there are no available items.
    * @param string aValue
    */
   set emptyText(aValue) {
     if (this._emptyTextNode) {
@@ -398,34 +557,35 @@ VariablesView.prototype = {
    * @return nsIDOMNode
    */
   get parentNode() this._parent,
 
   /**
    * Gets the owner document holding this view.
    * @return nsIHTMLDocument
    */
-  get document() this._parent.ownerDocument,
+  get document() this._document || (this._document = this._parent.ownerDocument),
 
   /**
    * Gets the default window holding this view.
    * @return nsIDOMWindow
    */
-  get window() this.document.defaultView,
+  get window() this._window || (this._window = this.document.defaultView),
 
-  eval: null,
-  lazyEmpty: false,
+  _document: null,
+  _window: null,
+
   _store: null,
   _prevHierarchy: null,
   _currHierarchy: null,
+  _enumVisible: true,
+  _nonEnumVisible: true,
   _emptyTimeout: null,
   _searchTimeout: null,
   _searchFunction: null,
-  _enumVisible: true,
-  _nonEnumVisible: true,
   _parent: null,
   _list: null,
   _searchboxNode: null,
   _searchboxContainer: null,
   _searchboxPlaceholder: "",
   _emptyTextNode: null,
   _emptyTextValue: ""
 };
@@ -437,28 +597,36 @@ VariablesView.prototype = {
  * @param VariablesView aView
  *        The view to contain this scope.
  * @param string aName
  *        The scope's name.
  * @param object aFlags [optional]
  *        Additional options or flags for this scope.
  */
 function Scope(aView, aName, aFlags = {}) {
-  this.show = this.show.bind(this);
-  this.hide = this.hide.bind(this);
+  this.ownerView = aView;
+
   this.expand = this.expand.bind(this);
-  this.collapse = this.collapse.bind(this);
   this.toggle = this.toggle.bind(this);
   this._openEnum = this._openEnum.bind(this);
   this._openNonEnum = this._openNonEnum.bind(this);
+  this._batchAppend = this._batchAppend.bind(this);
+  this._batchItems = [];
 
-  this.ownerView = aView;
+  // Inherit properties and flags from the parent view. You can override
+  // each of these directly onto any scope, variable or property instance.
   this.eval = aView.eval;
   this.switch = aView.switch;
   this.delete = aView.delete;
+  this.editableValueTooltip = aView.editableValueTooltip;
+  this.editableNameTooltip = aView.editableNameTooltip;
+  this.deleteButtonTooltip = aView.deleteButtonTooltip;
+  this.descriptorTooltip = aView.descriptorTooltip;
+  this.contextMenuId = aView.contextMenuId;
+  this.separatorStr = aView.separatorStr;
 
   this._store = new Map();
   this._init(aName.trim(), aFlags);
 }
 
 Scope.prototype = {
   /**
    * Adds a variable to contain any inspected properties.
@@ -490,24 +658,50 @@ Scope.prototype = {
     this._variablesView._currHierarchy.set(variable._absoluteName, variable);
     variable.header = !!aName;
     return variable;
   },
 
   /**
    * Gets the variable in this container having the specified name.
    *
+   * @param string aName
+   *        The name of the variable to get.
    * @return Variable
    *         The matched variable, or null if nothing is found.
    */
   get: function S_get(aName) {
     return this._store.get(aName);
   },
 
   /**
+   * Recursively searches for the variable or property in this container
+   * displayed by the specified node.
+   *
+   * @param nsIDOMNode aNode
+   *        The node to search for.
+   * @return Variable | Property
+   *         The matched variable or property, or null if nothing is found.
+   */
+  find: function S_find(aNode) {
+    for (let [, variable] of this._store) {
+      let match;
+      if (variable._target == aNode) {
+        match = variable;
+      } else {
+        match = variable.find(aNode);
+      }
+      if (match) {
+        return match;
+      }
+    }
+    return null;
+  },
+
+  /**
    * Shows the scope.
    */
   show: function S_show() {
     this._target.hidden = false;
     this._isShown = true;
 
     if (this.onshow) {
       this.onshow(this);
@@ -528,22 +722,38 @@ Scope.prototype = {
 
   /**
    * Expands the scope, showing all the added details.
    */
   expand: function S_expand() {
     if (this._isExpanded || this._locked) {
       return;
     }
+    // If there's a large number of enumerable or non-enumerable items
+    // contained in this scope, painting them may take several seconds,
+    // even if they were already displayed before. In this case, show a throbber
+    // to suggest that this scope is expanding.
+    if (!this._isExpanding &&
+         this._variablesView.lazyAppend && this._store.size > LAZY_APPEND_BATCH) {
+      this._isExpanding = true;
+
+      // Start spinning a throbber in this scope's title and allow a few
+      // milliseconds for it to be painted.
+      this._startThrobber();
+      this.window.setTimeout(this.expand, LAZY_EXPAND_DELAY);
+      return;
+    }
+
     if (this._variablesView._enumVisible) {
       this._openEnum();
     }
     if (this._variablesView._nonEnumVisible) {
       Services.tm.currentThread.dispatch({ run: this._openNonEnum }, 0);
     }
+    this._isExpanding = false;
     this._isExpanded = true;
 
     if (this.onexpand) {
       this.onexpand(this);
     }
   },
 
   /**
@@ -570,30 +780,30 @@ Scope.prototype = {
     if (e && e.button != 0) {
       // Only allow left-click to trigger this event.
       return;
     }
     this._wasToggled = true;
     this.expanded ^= 1;
 
     // Make sure the scope and its contents are visibile.
-    for (let [, variable] in this) {
+    for (let [, variable] of this._store) {
       variable.header = true;
       variable._match = true;
     }
     if (this.ontoggle) {
       this.ontoggle(this);
     }
   },
 
   /**
    * Shows the scope's title header.
    */
   showHeader: function S_showHeader() {
-    if (this._isHeaderVisible) {
+    if (this._isHeaderVisible || !this._nameString) {
       return;
     }
     this._target.removeAttribute("non-header");
     this._isHeaderVisible = true;
   },
 
   /**
    * Hides the scope's title header.
@@ -674,44 +884,46 @@ Scope.prototype = {
 
   /**
    * Sets the twisty visibility state.
    * @param boolean aFlag
    */
   set twisty(aFlag) aFlag ? this.showArrow() : this.hideArrow(),
 
   /**
-   * Specifies if the configurable/enumerable/writable tooltip should be shown
-   * whenever a variable or property descriptor is available.
-   * This flag applies non-recursively to the current scope.
+   * Gets the expand lock state.
+   * @return boolean
    */
-  showDescriptorTooltip: true,
+  get locked() this._locked,
 
   /**
-   * Specifies if editing variable or property names is allowed.
-   * This flag applies non-recursively to the current scope.
+   * Sets the expand lock state.
+   * @param boolean aFlag
    */
-  allowNameInput: false,
+  set locked(aFlag) this._locked = aFlag,
 
   /**
-   * Specifies if editing variable or property values is allowed.
-   * This flag applies non-recursively to the current scope.
+   * Adds an event listener for a certain event on this scope's title.
+   * @param string aName
+   * @param function aCallback
+   * @param boolean aCapture
    */
-  allowValueInput: true,
+  addEventListener: function S_addEventListener(aName, aCallback, aCapture) {
+    this._title.addEventListener(aName, aCallback, aCapture);
+  },
 
   /**
-   * Specifies if removing variables or properties values is allowed.
-   * This flag applies non-recursively to the current scope.
+   * Removes an event listener for a certain event on this scope's title.
+   * @param string aName
+   * @param function aCallback
+   * @param boolean aCapture
    */
-  allowDeletion: false,
-
-  /**
-   * Specifies the context menu attribute set on variables and properties.
-   */
-  contextMenu: "",
+  removeEventListener: function S_removeEventListener(aName, aCallback, aCapture) {
+    this._title.removeEventListener(aName, aCallback, aCapture);
+  },
 
   /**
    * Gets the id associated with this item.
    * @return string
    */
   get id() this._idString,
 
   /**
@@ -733,41 +945,41 @@ Scope.prototype = {
    *        The scope's name.
    * @param object aFlags [optional]
    *        Additional options or flags for this scope.
    * @param string aClassName [optional]
    *        A custom class name for this scope.
    */
   _init: function S__init(aName, aFlags = {}, aClassName = "scope") {
     this._idString = generateId(this._nameString = aName);
-    this._createScope(aName, aClassName);
+    this._displayScope(aName, aClassName);
     this._addEventListeners();
     this.parentNode.appendChild(this._target);
   },
 
   /**
    * Creates the necessary nodes for this scope.
    *
    * @param string aName
    *        The scope's name.
    * @param string aClassName
    *        A custom class name for this scope.
    */
-  _createScope: function S__createScope(aName, aClassName) {
+  _displayScope: function S__createScope(aName, aClassName) {
     let document = this.document;
 
     let element = this._target = document.createElement("vbox");
     element.id = this._idString;
     element.className = aClassName;
 
     let arrow = this._arrow = document.createElement("hbox");
     arrow.className = "arrow";
 
     let name = this._name = document.createElement("label");
-    name.className = "name plain";
+    name.className = "plain name";
     name.setAttribute("value", aName);
 
     let title = this._title = document.createElement("hbox");
     title.className = "title" + (aClassName == "scope" ? " devtools-toolbar" : "");
     title.setAttribute("align", "center");
 
     let enumerable = this._enum = document.createElement("vbox");
     let nonenum = this._nonenum = document.createElement("vbox");
@@ -785,66 +997,145 @@ Scope.prototype = {
   /**
    * Adds the necessary event listeners for this scope.
    */
   _addEventListeners: function S__addEventListeners() {
     this._title.addEventListener("mousedown", this.toggle, false);
   },
 
   /**
-   * Adds an event listener for the mouse over event on the title element.
-   * @param function aCallback
+   * Lazily appends a node to this scope's enumerable or non-enumerable
+   * container. Once a certain number of nodes have been batched, they
+   * will be appended.
+   *
+   * @param boolean aImmediateFlag
+   *        Set to false if append calls should be dispatched synchronously
+   *        on the current thread, to allow for a paint flush.
+   * @param boolean aEnumerableFlag
+   *        Specifies if the node to append is enumerable or non-enumerable.
+   * @param nsIDOMNode aChild
+   *        The child node to append.
    */
-  set onmouseover(aCallback) {
-    this._title.addEventListener("mouseover", aCallback, false);
+  _lazyAppend: function S__lazyAppend(aImmediateFlag, aEnumerableFlag, aChild) {
+    // Append immediately, don't stage items and don't allow for a paint flush.
+    if (aImmediateFlag || !this._variablesView.lazyAppend) {
+      if (aEnumerableFlag) {
+        this._enum.appendChild(aChild);
+      } else {
+        this._nonenum.appendChild(aChild);
+      }
+      return;
+    }
+
+    let window = this.window;
+    let batchItems = this._batchItems;
+
+    window.clearTimeout(this._batchTimeout);
+    batchItems.push({ enumerableFlag: aEnumerableFlag, child: aChild });
+
+    // If a certain number of nodes have been batched, append all the
+    // staged items now.
+    if (batchItems.length > LAZY_APPEND_BATCH) {
+      // Allow for a paint flush.
+      Services.tm.currentThread.dispatch({ run: this._batchAppend }, 1);
+      return;
+    }
+    // Postpone appending the staged items for later, to allow batching
+    // more nodes.
+    this._batchTimeout = window.setTimeout(this._batchAppend, LAZY_APPEND_DELAY);
+  },
+
+  /**
+   * Appends all the batched nodes to this scope's enumerable and non-enumerable
+   * containers.
+   */
+  _batchAppend: function S__batchAppend() {
+    let document = this.document;
+    let batchItems = this._batchItems;
+
+    // Create two document fragments, one for enumerable nodes, and one
+    // for non-enumerable nodes.
+    let frags = [document.createDocumentFragment(), document.createDocumentFragment()];
+
+    for (let item of batchItems) {
+      frags[~~item.enumerableFlag].appendChild(item.child);
+    }
+    batchItems.length = 0;
+    this._enum.appendChild(frags[1]);
+    this._nonenum.appendChild(frags[0]);
+  },
+
+  /**
+   * Starts spinning a throbber in this scope's title.
+   */
+  _startThrobber: function S__startThrobber() {
+    if (this._throbber) {
+      this._throbber.hidden = false;
+      return;
+    }
+    let throbber = this._throbber = this.document.createElement("hbox");
+    throbber.className = "dbg-variable-throbber";
+    this._title.appendChild(throbber);
+  },
+
+  /**
+   * Stops spinning the throbber in this scope's title.
+   */
+  _stopThrobber: function S__stopThrobber() {
+    if (!this._throbber) {
+      return;
+    }
+    this._throbber.hidden = true;
   },
 
   /**
    * Opens the enumerable items container.
    */
   _openEnum: function S__openEnum() {
     this._arrow.setAttribute("open", "");
     this._enum.setAttribute("open", "");
+    this._stopThrobber();
   },
 
   /**
    * Opens the non-enumerable items container.
    */
   _openNonEnum: function S__openNonEnum() {
     this._nonenum.setAttribute("open", "");
+    this._stopThrobber();
   },
 
   /**
    * Specifies if enumerable properties and variables should be displayed.
    * @param boolean aFlag
    */
   set _enumVisible(aFlag) {
-    for (let [, variable] in this) {
+    for (let [, variable] of this._store) {
       variable._enumVisible = aFlag;
 
-      if (!this.expanded) {
+      if (!this._isExpanded) {
         continue;
       }
       if (aFlag) {
         this._enum.setAttribute("open", "");
       } else {
         this._enum.removeAttribute("open");
       }
     }
   },
 
   /**
    * Specifies if non-enumerable properties and variables should be displayed.
    * @param boolean aFlag
    */
   set _nonEnumVisible(aFlag) {
-    for (let [, variable] in this) {
+    for (let [, variable] of this._store) {
       variable._nonEnumVisible = aFlag;
 
-      if (!this.expanded) {
+      if (!this._isExpanded) {
         continue;
       }
       if (aFlag) {
         this._nonenum.setAttribute("open", "");
       } else {
         this._nonenum.removeAttribute("open");
       }
     }
@@ -853,17 +1144,17 @@ Scope.prototype = {
   /**
    * Performs a case insensitive search for variables or properties matching
    * the query, and hides non-matched items.
    *
    * @param string aLowerCaseQuery
    *        The lowercased name of the variable or property to search for.
    */
   _performSearch: function S__performSearch(aLowerCaseQuery) {
-    for (let [, variable] in this) {
+    for (let [, variable] of this._store) {
       let currentObject = variable;
       let lowerCaseName = variable._nameString.toLowerCase();
       let lowerCaseValue = variable._valueString.toLowerCase();
 
       // Non-matched variables or properties require a corresponding attribute.
       if (!lowerCaseName.contains(aLowerCaseQuery) &&
           !lowerCaseValue.contains(aLowerCaseQuery)) {
         variable._match = false;
@@ -923,92 +1214,108 @@ Scope.prototype = {
       this.target.setAttribute("non-match", "");
     }
   },
 
   /**
    * Gets top level variables view instance.
    * @return VariablesView
    */
-  get _variablesView() {
-    let parentView = this.ownerView;
+  get _variablesView() this._topView || (this._topView = (function(self) {
+    let parentView = self.ownerView;
     let topView;
 
     while (topView = parentView.ownerView) {
       parentView = topView;
     }
     return parentView;
-  },
+  })(this)),
 
   /**
    * Gets the parent node holding this scope.
    * @return nsIDOMNode
    */
   get parentNode() this.ownerView._list,
 
   /**
    * Gets the owner document holding this scope.
    * @return nsIHTMLDocument
    */
-  get document() this.ownerView.document,
+  get document() this._document || (this._document = this.ownerView.document),
 
   /**
    * Gets the default window holding this scope.
    * @return nsIDOMWindow
    */
-  get window() this.ownerView.window,
+  get window() this._window || (this._window = this.ownerView.window),
+
+  _topView: null,
+  _document: null,
+  _window: null,
 
   ownerView: null,
   eval: null,
-  fetched: false,
+  switch: null,
+  delete: null,
+  editableValueTooltip: "",
+  editableNameTooltip: "",
+  deleteButtonTooltip: "",
+  descriptorTooltip: true,
+  contextMenuId: "",
+  separatorStr: "",
+
+  _store: null,
+  _fetched: false,
+  _retrieved: false,
   _committed: false,
+  _batchItems: null,
+  _batchTimeout: null,
   _locked: false,
   _isShown: true,
+  _isExpanding: false,
   _isExpanded: false,
   _wasToggled: false,
   _isHeaderVisible: true,
   _isArrowVisible: true,
   _isMatch: true,
-  _store: null,
   _idString: "",
   _nameString: "",
   _target: null,
   _arrow: null,
   _name: null,
   _title: null,
   _enum: null,
-  _nonenum: null
+  _nonenum: null,
+  _throbber: null
 };
 
 /**
  * A Variable is a Scope holding Property instances.
  * Iterable via "for (let [name, property] in instance) { }".
  *
  * @param Scope aScope
  *        The scope to contain this varialbe.
  * @param string aName
  *        The variable's name.
  * @param object aDescriptor
  *        The variable's descriptor.
  */
 function Variable(aScope, aName, aDescriptor) {
-  this._onClose = this._onClose.bind(this);
   this._displayTooltip = this._displayTooltip.bind(this);
   this._activateNameInput = this._activateNameInput.bind(this);
   this._activateValueInput = this._activateValueInput.bind(this);
   this._deactivateNameInput = this._deactivateNameInput.bind(this);
   this._deactivateValueInput = this._deactivateValueInput.bind(this);
   this._onNameInputKeyPress = this._onNameInputKeyPress.bind(this);
   this._onValueInputKeyPress = this._onValueInputKeyPress.bind(this);
 
-  Scope.call(this, aScope, aName, aDescriptor);
-  this._setGrip(aDescriptor.value);
+  Scope.call(this, aScope, aName, this._initialDescriptor = aDescriptor);
+  this.setGrip(aDescriptor.value);
   this._symbolicName = aName;
-  this._absoluteName = aScope.name + "." + aName;
-  this._initialDescriptor = aDescriptor;
+  this._absoluteName = aScope.name + "[\"" + aName + "\"]";
 }
 
 create({ constructor: Variable, proto: Scope.prototype }, {
   /**
    * Adds a property for this variable.
    *
    * @param string aName
    *        The property's name.
@@ -1047,48 +1354,61 @@ create({ constructor: Variable, proto: S
    *        specifying the value and/or type & class of the variable,
    *        or 'get' & 'set' accessor properties. If the type is implicit,
    *        it will be inferred from the value.
    *        e.g. - { someProp0: { value: 42 },
    *                 someProp1: { value: true },
    *                 someProp2: { value: "nasu" },
    *                 someProp3: { value: { type: "undefined" } },
    *                 someProp4: { value: { type: "null" } },
-   *                 someProp5: { valu§e: { type: "object", class: "Object" } },
+   *                 someProp5: { value: { type: "object", class: "Object" } },
    *                 someProp6: { get: { type: "object", class: "Function" },
    *                              set: { type: "undefined" } }
+   * @param object aOptions [optional]
+   *        Additional options for adding the properties. Supported options:
+   *        - sorted: true to sort all the properties before adding them
    */
-  addProperties: function V_addProperties(aProperties) {
-    // Sort all of the properties before adding them.
-    let sortedPropertyNames = Object.keys(aProperties).sort();
+  addProperties: function V_addProperties(aProperties, aOptions = {}) {
+    let propertyNames = Object.keys(aProperties);
 
-    for (let name of sortedPropertyNames) {
+    // Sort all of the properties before adding them, if preferred.
+    if (aOptions.sorted) {
+      propertyNames.sort();
+    }
+    // Add the properties to the current scope.
+    for (let name of propertyNames) {
       this.addProperty(name, aProperties[name]);
     }
   },
 
   /**
    * Populates this variable to contain all the properties of an object.
    *
    * @param object aObject
    *        The raw object you want to display.
+   * @param object aOptions [optional]
+   *        Additional options for adding the properties. Supported options:
+   *        - sorted: true to sort all the properties before adding them
    */
-  populate: function V_populate(aObject) {
+  populate: function V_populate(aObject, aOptions = {}) {
     // Retrieve the properties only once.
-    if (this.fetched) {
+    if (this._fetched) {
       return;
     }
-    this.fetched = true;
+    this._fetched = true;
 
-    // Sort all of the properties before adding them.
-    let sortedPropertyNames = Object.getOwnPropertyNames(aObject).sort();
+    let propertyNames = Object.getOwnPropertyNames(aObject);
     let prototype = Object.getPrototypeOf(aObject);
 
+    // Sort all of the properties before adding them, if preferred.
+    if (aOptions.sorted) {
+      propertyNames.sort();
+    }
     // Add all the variable properties.
-    for (let name of sortedPropertyNames) {
+    for (let name of propertyNames) {
       let descriptor = Object.getOwnPropertyDescriptor(aObject, name);
       if (descriptor.get || descriptor.set) {
         this._addRawNonValueProperty(name, descriptor);
       } else {
         this._addRawValueProperty(name, descriptor, aObject[name]);
       }
     }
     // Add the variable's __proto__.
@@ -1114,18 +1434,16 @@ create({ constructor: Variable, proto: S
 
     let propertyItem = this.addProperty(aName, descriptor);
 
     // Add an 'onexpand' callback for the property, lazily handling
     // the addition of new child properties.
     if (!VariablesView.isPrimitive(descriptor)) {
       propertyItem.onexpand = this.populate.bind(propertyItem, aValue);
     }
-
-    return propertyItem;
   },
 
   /**
    * Adds a property for this variable based on a getter/setter descriptor.
    *
    * @param string aName
    *        The property's name.
    * @param object aDescriptor
@@ -1137,63 +1455,70 @@ create({ constructor: Variable, proto: S
     descriptor.get = VariablesView.getGrip(aDescriptor.get);
     descriptor.set = VariablesView.getGrip(aDescriptor.set);
 
     let propertyItem = this.addProperty(aName, descriptor);
     return propertyItem;
   },
 
   /**
-   * Returns this variable's value from the descriptor if available,
+   * Gets this variable's path to the topmost scope.
+   * For example, a symbolic name may look like "arguments['0']['foo']['bar']".
+   * @return string
+   */
+  get symbolicName() this._symbolicName,
+
+  /**
+   * Returns this variable's value from the descriptor if available.
+   * @return any
    */
   get value() this._initialDescriptor.value,
 
   /**
-   * Returns this variable's getter from the descriptor if available,
+   * Returns this variable's getter from the descriptor if available.
+   * @return object
    */
   get getter() this._initialDescriptor.get,
 
   /**
-   * Returns this variable's getter from the descriptor if available,
+   * Returns this variable's getter from the descriptor if available.
+   * @return object
    */
   get setter() this._initialDescriptor.set,
 
   /**
-   * Sets the specific grip for this variable.
+   * Sets the specific grip for this variable (applies the text content and
+   * class name to the value label).
+   *
    * The grip should contain the value or the type & class, as defined in the
    * remote debugger protocol. For convenience, undefined and null are
    * both considered types.
    *
    * @param any aGrip
    *        Specifies the value and/or type & class of the variable.
    *        e.g. - 42
    *             - true
    *             - "nasu"
    *             - { type: "undefined" }
    *             - { type: "null" }
    *             - { type: "object", class: "Object" }
    */
-  _setGrip: function V__setGrip(aGrip) {
+  setGrip: function V_setGrip(aGrip) {
+    // Don't allow displaying grip information if there's no name available.
+    if (!this._nameString) {
+      return;
+    }
+
     if (aGrip === undefined) {
       aGrip = { type: "undefined" };
     }
     if (aGrip === null) {
       aGrip = { type: "null" };
     }
-    this._applyGrip(aGrip);
-  },
 
-  /**
-   * Applies the necessary text content and class name to a value node based
-   * on a grip.
-   *
-   * @param any aGrip
-   *        @see Variable._setGrip
-   */
-  _applyGrip: function V__applyGrip(aGrip) {
     let prevGrip = this._valueGrip;
     if (prevGrip) {
       this._valueLabel.classList.remove(VariablesView.getClass(prevGrip));
     }
     this._valueGrip = aGrip;
     this._valueString = VariablesView.getString(aGrip);
     this._valueClassName = VariablesView.getClass(aGrip);
 
@@ -1206,172 +1531,181 @@ create({ constructor: Variable, proto: S
    *
    * @param string aName
    *        The variable's name.
    * @param object aDescriptor
    *        The variable's descriptor.
    */
   _init: function V__init(aName, aDescriptor) {
     this._idString = generateId(this._nameString = aName);
-    this._createScope(aName, "variable");
-    this._displayVariable(aDescriptor);
-    this._prepareTooltip();
-    this._setAttributes(aName, aDescriptor);
-    this._addEventListeners();
+    this._displayScope(aName, "variable");
+
+    // Don't allow displaying variable information there's no name available.
+    if (this._nameString) {
+      this._displayVariable();
+      this._customizeVariable();
+      this._prepareTooltip();
+      this._setAttributes();
+      this._addEventListeners();
+    }
+
+    this._onInit(this.ownerView._store.size < LAZY_APPEND_BATCH);
+  },
 
-    if (aDescriptor.enumerable || aName == "this" || aName == "<exception>") {
-      this.ownerView._enum.appendChild(this._target);
+  /**
+   * Called when this variable has finished initializing, and is ready to
+   * be attached to the owner view.
+   *
+   * @param boolean aImmediateFlag
+   *        @see Scope.prototype._lazyAppend
+   */
+  _onInit: function V__onInit(aImmediateFlag) {
+    if (this._initialDescriptor.enumerable ||
+        this._nameString == "this" ||
+        this._nameString == "<exception>") {
+      this.ownerView._lazyAppend(aImmediateFlag, true, this._target);
     } else {
-      this.ownerView._nonenum.appendChild(this._target);
+      this.ownerView._lazyAppend(aImmediateFlag, false, this._target);
     }
   },
 
   /**
    * Creates the necessary nodes for this variable.
-   *
-   * @param object aDescriptor
-   *        The property's descriptor.
    */
-  _displayVariable: function V__displayVariable(aDescriptor) {
+  _displayVariable: function V__createVariable() {
     let document = this.document;
+    let descriptor = this._initialDescriptor;
 
     let separatorLabel = this._separatorLabel = document.createElement("label");
     separatorLabel.className = "plain";
-    separatorLabel.setAttribute("value", this.ownerView.separator);
+    separatorLabel.setAttribute("value", this.ownerView.separatorStr);
 
     let valueLabel = this._valueLabel = document.createElement("label");
-    valueLabel.className = "value plain";
+    valueLabel.className = "plain value";
+    valueLabel.setAttribute("crop", "center");
 
     this._title.appendChild(separatorLabel);
     this._title.appendChild(valueLabel);
 
-    let isPrimitive = VariablesView.isPrimitive(aDescriptor);
-    let isUndefined = VariablesView.isUndefined(aDescriptor);
+    let isPrimitive = VariablesView.isPrimitive(descriptor);
+    let isUndefined = VariablesView.isUndefined(descriptor);
 
     if (isPrimitive || isUndefined) {
       this.hideArrow();
     }
-    if (!isUndefined && (aDescriptor.get || aDescriptor.set)) {
-      this.addProperty("get", { value: aDescriptor.get });
-      this.addProperty("set", { value: aDescriptor.set });
+    if (!isUndefined && (descriptor.get || descriptor.set)) {
+      // FIXME: editing getters and setters is not allowed yet. Bug 831794.
+      this.eval = null;
+      this.addProperty("get", { value: descriptor.get });
+      this.addProperty("set", { value: descriptor.set });
       this.expand();
       separatorLabel.hidden = true;
       valueLabel.hidden = true;
     }
-    if (this.ownerView.allowDeletion) {
-      let closeNode = this._closeNode = document.createElement("toolbarbutton");
-      closeNode.className = "dbg-variables-delete plain devtools-closebutton";
-      closeNode.addEventListener("click", this._onClose, false);
-      this._title.appendChild(closeNode);
+  },
+
+  /**
+   * Adds specific nodes for this variable based on custom flags.
+   */
+  _customizeVariable: function V__customizeVariable() {
+    if (this.ownerView.delete) {
+      let deleteNode = this._deleteNode = this.document.createElement("toolbarbutton");
+      deleteNode.className = "plain dbg-variable-delete devtools-closebutton";
+      deleteNode.addEventListener("click", this._onDelete.bind(this), false);
+      this._title.appendChild(deleteNode);
     }
-    if (this.ownerView.contextMenu) {
-      this._title.setAttribute("context", this.ownerView.contextMenu);
+    if (this.ownerView.contextMenuId) {
+      this._title.setAttribute("context", this.ownerView.contextMenuId);
     }
   },
 
   /**
    * Prepares a tooltip for this variable.
    */
   _prepareTooltip: function V__prepareTooltip() {
     this._target.addEventListener("mouseover", this._displayTooltip, false);
   },
 
   /**
    * Creates a tooltip for this variable.
    */
   _displayTooltip: function V__displayTooltip() {
     this._target.removeEventListener("mouseover", this._displayTooltip, false);
-    let document = this.document;
 
-    if (this.ownerView.showDescriptorTooltip) {
+    if (this.ownerView.descriptorTooltip) {
+      let document = this.document;
+
       let tooltip = document.createElement("tooltip");
-      tooltip.id = "tooltip-" + this.id;
+      tooltip.id = "tooltip-" + this._idString;
 
       let configurableLabel = document.createElement("label");
+      let enumerableLabel = document.createElement("label");
+      let writableLabel = document.createElement("label");
       configurableLabel.setAttribute("value", "configurable");
-
-      let enumerableLabel = document.createElement("label");
       enumerableLabel.setAttribute("value", "enumerable");
-
-      let writableLabel = document.createElement("label");
       writableLabel.setAttribute("value", "writable");
 
-      tooltip.setAttribute("orient", "horizontal")
+      tooltip.setAttribute("orient", "horizontal");
       tooltip.appendChild(configurableLabel);
       tooltip.appendChild(enumerableLabel);
       tooltip.appendChild(writableLabel);
 
       this._target.appendChild(tooltip);
       this._target.setAttribute("tooltip", tooltip.id);
     }
-    if (this.ownerView.allowNameInput) {
-      this._name.setAttribute("tooltiptext", L10N.getStr("variablesEditableNameTooltip"));
+    if (this.ownerView.eval) {
+      this._valueLabel.setAttribute("tooltiptext", this.ownerView.editableValueTooltip);
     }
-    if (this.ownerView.allowValueInput) {
-      this._valueLabel.setAttribute("tooltiptext", L10N.getStr("variablesEditableValueTooltip"));
+    if (this.ownerView.switch) {
+      this._name.setAttribute("tooltiptext", this.ownerView.editableNameTooltip);
     }
-    if (this.ownerView.allowDeletion) {
-      this._closeNode.setAttribute("tooltiptext", L10N.getStr("variablesCloseButtonTooltip"));
+    if (this.ownerView.delete) {
+      this._deleteNode.setAttribute("tooltiptext", this.ownerView.deleteButtonTooltip);
     }
   },
 
   /**
    * Sets a variable's configurable, enumerable and writable attributes,
    * and specifies if it's a 'this', '<exception>' or '__proto__' reference.
-   *
-   * @param object aName
-   *        The varialbe name.
-   * @param object aDescriptor
-   *        The variable's descriptor.
    */
-  _setAttributes: function V__setAttributes(aName, aDescriptor) {
-    if (aDescriptor) {
-      if (!aDescriptor.configurable) {
-        this._target.setAttribute("non-configurable", "");
-      }
-      if (!aDescriptor.enumerable) {
-        this._target.setAttribute("non-enumerable", "");
-      }
-      if (!aDescriptor.writable) {
-        this._target.setAttribute("non-writable", "");
-      }
+  _setAttributes: function V__setAttributes() {
+    let descriptor = this._initialDescriptor;
+    let name = this._nameString;
+
+    if (!descriptor.configurable) {
+      this._target.setAttribute("non-configurable", "");
     }
-    if (aName == "this") {
+    if (!descriptor.enumerable) {
+      this._target.setAttribute("non-enumerable", "");
+    }
+    if (!descriptor.writable) {
+      this._target.setAttribute("non-writable", "");
+    }
+    if (name == "this") {
       this._target.setAttribute("self", "");
     }
-    if (aName == "<exception>") {
+    else if (name == "<exception>") {
       this._target.setAttribute("exception", "");
     }
-    if (aName == "__proto__") {
+    else if (name == "__proto__") {
       this._target.setAttribute("proto", "");
     }
   },
 
   /**
    * Adds the necessary event listeners for this variable.
    */
   _addEventListeners: function V__addEventListeners() {
     this._arrow.addEventListener("mousedown", this.toggle, false);
     this._name.addEventListener("mousedown", this.toggle, false);
     this._name.addEventListener("dblclick", this._activateNameInput, false);
     this._valueLabel.addEventListener("click", this._activateValueInput, false);
   },
 
   /**
-   * The click listener for the close button.
-   */
-  _onClose: function V__onClose() {
-    this.hide();
-
-    if (this.delete) {
-      this.delete(this);
-    }
-  },
-
-  /**
    * Creates a textbox node in place of a label.
    *
    * @param nsIDOMNode aLabel
    *        The label to be replaced with a textbox.
    * @param string aClassName
    *        The class to be applied to the textbox.
    * @param object aCallbacks
    *        An object containing the onKeypress and onBlur callbacks.
@@ -1400,16 +1734,18 @@ create({ constructor: Variable, proto: S
     input.addEventListener("keypress", aCallbacks.onKeypress, false);
     input.addEventListener("blur", aCallbacks.onBlur, false);
 
     this._prevExpandable = this.twisty;
     this._prevExpanded = this.expanded;
     this.collapse();
     this.hideArrow();
     this._locked = true;
+
+    this._stopThrobber();
   },
 
   /**
    * Removes the textbox node in place of a label.
    *
    * @param nsIDOMNode aLabel
    *        The label which was replaced with a textbox.
    * @param object aCallbacks
@@ -1418,27 +1754,29 @@ create({ constructor: Variable, proto: S
   _deactivateInput: function V__deactivateInput(aLabel, aInput, aCallbacks) {
     aInput.parentNode.replaceChild(aLabel, aInput);
     aInput.removeEventListener("keypress", aCallbacks.onKeypress, false);
     aInput.removeEventListener("blur", aCallbacks.onBlur, false);
 
     this._locked = false;
     this.twisty = this._prevExpandable;
     this.expanded = this._prevExpanded;
+
+    this._stopThrobber();
   },
 
   /**
    * Makes this variable's name editable.
    */
   _activateNameInput: function V__activateNameInput(e) {
     if (e && e.button != 0) {
       // Only allow left-click to trigger this event.
       return;
     }
-    if (!this.ownerView.allowNameInput || !this.switch) {
+    if (!this.ownerView.switch) {
       return;
     }
     this._activateInput(this._name, "element-name-input", {
       onKeypress: this._onNameInputKeyPress,
       onBlur: this._deactivateNameInput
     });
     this._separatorLabel.hidden = true;
     this._valueLabel.hidden = true;
@@ -1459,17 +1797,17 @@ create({ constructor: Variable, proto: S
   /**
    * Makes this variable's value editable.
    */
   _activateValueInput: function V__activateValueInput(e) {
     if (e && e.button != 0) {
       // Only allow left-click to trigger this event.
       return;
     }
-    if (!this.ownerView.allowValueInput || !this.eval) {
+    if (!this.ownerView.eval) {
       return;
     }
     this._activateInput(this._valueLabel, "element-value-input", {
       onKeypress: this._onValueInputKeyPress,
       onBlur: this._deactivateValueInput
     });
   },
 
@@ -1501,32 +1839,32 @@ create({ constructor: Variable, proto: S
     let input = e.target;
     let initialString = this._name.getAttribute("value");
     let currentString = input.value.trim();
     this._deactivateNameInput(e);
 
     if (initialString != currentString) {
       this._disable();
       this._name.value = currentString;
-      this.switch(this, currentString);
+      this.ownerView.switch(this, currentString);
     }
   },
 
   /**
    * Deactivates this variable's editable mode and evaluates the new value.
    */
   _saveValueInput: function V__saveValueInput(e) {
     let input = e.target;
     let initialString = this._valueLabel.getAttribute("value");
     let currentString = input.value.trim();
     this._deactivateValueInput(e);
 
     if (initialString != currentString) {
       this._disable();
-      this.eval(this._symbolicName + "=" + currentString);
+      this.ownerView.eval(this._symbolicName + "=" + currentString);
     }
   },
 
   /**
    * The key press listener for this variable's editable name textbox.
    */
   _onNameInputKeyPress: function V__onNameInputKeyPress(e) {
     switch(e.keyCode) {
@@ -1550,22 +1888,33 @@ create({ constructor: Variable, proto: S
         this._saveValueInput(e);
         return;
       case e.DOM_VK_ESCAPE:
         this._deactivateValueInput(e);
         return;
     }
   },
 
+  /**
+   * The click listener for the delete button.
+   */
+  _onDelete: function V__onDelete() {
+    this.hide();
+
+    if (this.ownerView.delete) {
+      this.ownerView.delete(this);
+    }
+  },
+
   _symbolicName: "",
   _absoluteName: "",
   _initialDescriptor: null,
   _separatorLabel: null,
   _valueLabel: null,
-  _closeNode: null,
+  _deleteNode: null,
   _tooltip: null,
   _valueGrip: null,
   _valueString: "",
   _valueClassName: "",
   _prevExpandable: false,
   _prevExpanded: false
 });
 
@@ -1578,41 +1927,56 @@ create({ constructor: Variable, proto: S
  * @param string aName
  *        The property's name.
  * @param object aDescriptor
  *        The property's descriptor.
  */
 function Property(aVar, aName, aDescriptor) {
   Variable.call(this, aVar, aName, aDescriptor);
   this._symbolicName = aVar._symbolicName + "[\"" + aName + "\"]";
-  this._absoluteName = aVar._absoluteName + "." + aName;
-  this._initialDescriptor = aDescriptor;
+  this._absoluteName = aVar._absoluteName + "[\"" + aName + "\"]";
 }
 
 create({ constructor: Property, proto: Variable.prototype }, {
   /**
    * Initializes this property's id, view and binds event listeners.
    *
    * @param string aName
    *        The property's name.
    * @param object aDescriptor
    *        The property's descriptor.
    */
   _init: function P__init(aName, aDescriptor) {
     this._idString = generateId(this._nameString = aName);
-    this._createScope(aName, "property");
-    this._displayVariable(aDescriptor);
-    this._prepareTooltip();
-    this._setAttributes(aName, aDescriptor);
-    this._addEventListeners();
+    this._displayScope(aName, "property");
+
+    // Don't allow displaying property information there's no name available.
+    if (this._nameString) {
+      this._displayVariable();
+      this._customizeVariable();
+      this._prepareTooltip();
+      this._setAttributes();
+      this._addEventListeners();
+    }
 
-    if (aDescriptor.enumerable) {
-      this.ownerView._enum.appendChild(this._target);
+    this._onInit(this.ownerView._store.size < LAZY_APPEND_BATCH);
+  },
+
+  /**
+   * Called when this property has finished initializing, and is ready to
+   * be attached to the owner view.
+   *
+   * @param boolean aImmediateFlag
+   *        @see Scope.prototype._lazyAppend
+   */
+  _onInit: function P__onInit(aImmediateFlag) {
+    if (this._initialDescriptor.enumerable) {
+      this.ownerView._lazyAppend(aImmediateFlag, true, this._target);
     } else {
-      this.ownerView._nonenum.appendChild(this._target);
+      this.ownerView._lazyAppend(aImmediateFlag, false, this._target);
     }
   }
 });
 
 /**
  * A generator-iterator over the VariablesView, Scopes, Variables and Properties.
  */
 VariablesView.prototype.__iterator__ =
@@ -1620,16 +1984,25 @@ Scope.prototype.__iterator__ =
 Variable.prototype.__iterator__ =
 Property.prototype.__iterator__ = function VV_iterator() {
   for (let item of this._store) {
     yield item;
   }
 };
 
 /**
+ * Forget everything recorded about added scopes, variables or properties.
+ * @see VariablesView.createHierarchy
+ */
+VariablesView.prototype.clearHierarchy = function VV_clearHierarchy() {
+  this._prevHierarchy = new Map();
+  this._currHierarchy = new Map();
+};
+
+/**
  * Start recording a hierarchy of any added scopes, variables or properties.
  * @see VariablesView.commitHierarchy
  */
 VariablesView.prototype.createHierarchy = function VV_createHierarchy() {
   this._prevHierarchy = this._currHierarchy;
   this._currHierarchy = new Map();
 };
 
@@ -1702,38 +2075,34 @@ VariablesView.prototype.commitHierarchyI
 /**
  * Returns true if the descriptor represents an undefined, null or
  * primitive value.
  *
  * @param object aDescriptor
  *        The variable's descriptor.
  */
 VariablesView.isPrimitive = function VV_isPrimitive(aDescriptor) {
-  if (!aDescriptor || typeof aDescriptor != "object") {
-    return true;
-  }
-
   // For accessor property descriptors, the getter and setter need to be
   // contained in 'get' and 'set' properties.
   let getter = aDescriptor.get;
   let setter = aDescriptor.set;
   if (getter || setter) {
     return false;
   }
 
   // As described in the remote debugger protocol, the value grip
   // must be contained in a 'value' property.
   let grip = aDescriptor.value;
   if (!grip || typeof grip != "object") {
     return true;
   }
 
-  // For convenience, undefined and null are both considered types.
+  // For convenience, undefined, null and long strings are considered primitives.
   let type = grip.type;
-  if (type == "undefined" || type == "null") {
+  if (type == "undefined" || type == "null" || type == "longString") {
     return true;
   }
 
   return false;
 };
 
 /**
  * Returns true if the descriptor represents an undefined value.
@@ -1764,20 +2133,16 @@ VariablesView.isUndefined = function VV_
 
 /**
  * Returns true if the descriptor represents a falsy value.
  *
  * @param object aDescriptor
  *        The variable's descriptor.
  */
 VariablesView.isFalsy = function VV_isFalsy(aDescriptor) {
-  if (!aDescriptor || typeof aDescriptor != "object") {
-    return true;
-  }
-
   // As described in the remote debugger protocol, the value grip
   // must be contained in a 'value' property.
   let grip = aDescriptor.value;
   if (typeof grip != "object") {
     return !grip;
   }
 
   // For convenience, undefined and null are both considered types.
@@ -1800,42 +2165,40 @@ VariablesView.isFalsy = function VV_isFa
 VariablesView.getGrip = function VV_getGrip(aValue) {
   if (aValue === undefined) {
     return { type: "undefined" };
   }
   if (aValue === null) {
     return { type: "null" };
   }
   if (typeof aValue == "object" || typeof aValue == "function") {
-    if (aValue.constructor) {
-      return { type: "object", class: aValue.constructor.name };
-    } else {
-      return { type: "object", class: "Object" };
-    }
+    return { type: "object", class: WebConsoleUtils.getObjectClassName(aValue) };
   }
   return aValue;
 };
 
 /**
  * Returns a custom formatted property string for a grip.
  *
  * @param any aGrip
- *        @see Variable._setGrip
+ *        @see Variable.setGrip
  * @param boolean aConciseFlag
  *        Return a concisely formatted property string.
  * @return string
  *         The formatted property string.
  */
 VariablesView.getString = function VV_getString(aGrip, aConciseFlag) {
   if (aGrip && typeof aGrip == "object") {
     switch (aGrip.type) {
       case "undefined":
         return "undefined";
       case "null":
         return "null";
+      case "longString":
+        return "\"" + aGrip.initial + "\"";
       default:
         if (!aConciseFlag) {
           return "[" + aGrip.type + " " + aGrip.class + "]";
         } else {
           return aGrip.class;
         }
     }
   } else {
@@ -1848,67 +2211,44 @@ VariablesView.getString = function VV_ge
   }
   return aGrip + "";
 };
 
 /**
  * Returns a custom class style for a grip.
  *
  * @param any aGrip
- *        @see Variable._setGrip
+ *        @see Variable.setGrip
  * @return string
  *         The custom class style.
  */
 VariablesView.getClass = function VV_getClass(aGrip) {
   if (aGrip && typeof aGrip == "object") {
     switch (aGrip.type) {
       case "undefined":
         return "token-undefined";
       case "null":
         return "token-null";
+      case "longString":
+        return "token-string";
     }
   } else {
     switch (typeof aGrip) {
       case "string":
         return "token-string";
       case "boolean":
         return "token-boolean";
       case "number":
         return "token-number";
     }
   }
   return "token-other";
 };
 
 /**
- * Localization convenience methods.
- */
-let L10N = {
-  /**
-   * L10N shortcut function.
-   *
-   * @param string aName
-   * @return string
-   */
-  getStr: function L10N_getStr(aName) {
-    return this.stringBundle.GetStringFromName(aName);
-  }
-};
-
-XPCOMUtils.defineLazyGetter(L10N, "stringBundle", function() {
-  return Services.strings.createBundle(DBG_STRINGS_URI);
-});
-
-/**
- * The separator label between the variables or properties name and value.
- * This property applies non-recursively to the current scope.
- */
-Scope.prototype.separator = L10N.getStr("variablesSeparatorLabel");
-
-/**
  * A monotonically-increasing counter, that guarantees the uniqueness of scope,
  * variables and properties ids.
  *
  * @param string aName
  *        An optional string to prefix the id with.
  * @return number
  *         A unique id.
  */
--- a/browser/devtools/shared/test/browser_toolbar_webconsole_errors_count.html
+++ b/browser/devtools/shared/test/browser_toolbar_webconsole_errors_count.html
@@ -15,15 +15,18 @@
   <script type="text/javascript">
     window.foobarBug762996b();
   </script>
 </head>
 <body>
   <p>Hello world! Test for errors count in the Web Console button (developer
   toolbar).</p>
   <p style="color: foobarBug762996css"><button>click me</button></p>
-  <script type="text/javascript">
+  <script type="text/javascript;version=1.8">
+    "use strict";
+    let testObj = {};
     document.querySelector("button").onclick = function() {
+      let test = testObj.fooBug788445 + "warning";
       window.foobarBug762996click();
     };
   </script>
 </body>
 </html>
--- a/browser/devtools/shared/test/browser_toolbar_webconsole_errors_count.js
+++ b/browser/devtools/shared/test/browser_toolbar_webconsole_errors_count.js
@@ -16,16 +16,18 @@ function test() {
 
   Components.utils.import("resource:///modules/devtools/Target.jsm", imported);
   let TargetFactory = imported.TargetFactory;
 
   let webconsole = document.getElementById("developer-toolbar-toolbox-button");
   let toolbar = document.getElementById("Tools:DevToolbar");
   let tab1, tab2;
 
+  Services.prefs.setBoolPref("javascript.options.strict", true);
+
   function openToolbar(browser, tab) {
     tab1 = tab;
     ignoreAllUncaughtExceptions(false);
 
     ok(!DeveloperToolbar.visible, "DeveloperToolbar is not visible");
 
     expectUncaughtException();
     oneTimeObserve(DeveloperToolbar.NOTIFICATIONS.SHOW, onOpenToolbar);
@@ -35,69 +37,104 @@ function test() {
   ignoreAllUncaughtExceptions();
   addTab(TEST_URI, openToolbar);
 
   function getErrorsCount() {
     let count = webconsole.getAttribute("error-count");
     return count ? count : "0";
   }
 
+  function getTooltipValues() {
+    let matches = webconsole.getAttribute("tooltiptext")
+                  .match(/(\d+) errors, (\d+) warnings/);
+    return matches ? [matches[1], matches[2]] : [0, 0];
+  }
+
+  function waitForButtonUpdate(aOptions)
+  {
+    aOptions.validator = function() {
+      let errors = getErrorsCount();
+      let tooltip = getTooltipValues();
+      let result = errors == aOptions.errors &&
+                   tooltip[1] == aOptions.warnings;
+      if (result) {
+        is(errors, tooltip[0], "button error-count is the same as in the tooltip");
+      }
+      return result;
+    };
+
+    let originalFailure = aOptions.failure;
+    aOptions.failure = function() {
+      let tooltip = getTooltipValues();
+
+      info("expected " + aOptions.errors + " errors, " +
+           aOptions.warnings + " warnings");
+      info("got " + tooltip[0] + " errors, " + tooltip[1] + " warnings");
+
+      originalFailure();
+    };
+
+    waitForValue(aOptions);
+  }
+
   function onOpenToolbar() {
     ok(DeveloperToolbar.visible, "DeveloperToolbar is visible");
 
-    waitForValue({
+    waitForButtonUpdate({
       name: "web console button shows page errors",
-      validator: getErrorsCount,
-      value: 3,
+      errors: 3,
+      warnings: 0,
       success: addErrors,
       failure: finish,
     });
   }
 
   function addErrors() {
     expectUncaughtException();
 
     waitForFocus(function() {
       let button = content.document.querySelector("button");
-      EventUtils.synthesizeMouse(button, 2, 2, {}, content);
+      executeSoon(function() {
+        EventUtils.synthesizeMouse(button, 3, 2, {}, content);
+      });
     }, content);
 
-    waitForValue({
+    waitForButtonUpdate({
       name: "button shows one more error after click in page",
-      validator: getErrorsCount,
-      value: 4,
+      errors: 4,
+      warnings: 1,
       success: function() {
         ignoreAllUncaughtExceptions();
         addTab(TEST_URI, onOpenSecondTab);
       },
       failure: finish,
     });
   }
 
   function onOpenSecondTab(browser, tab) {
     tab2 = tab;
 
     ignoreAllUncaughtExceptions(false);
     expectUncaughtException();
 
-    waitForValue({
+    waitForButtonUpdate({
       name: "button shows correct number of errors after new tab is open",
-      validator: getErrorsCount,
-      value: 3,
+      errors: 3,
+      warnings: 0,
       success: switchToTab1,
       failure: finish,
     });
   }
 
   function switchToTab1() {
     gBrowser.selectedTab = tab1;
-    waitForValue({
+    waitForButtonUpdate({
       name: "button shows the page errors from tab 1",
-      validator: getErrorsCount,
-      value: 4,
+      errors: 4,
+      warnings: 1,
       success: function() {
         openWebConsole(tab1, onWebConsoleOpen);
       },
       failure: finish,
     });
   }
 
   function openWebConsole(tab, callback)
@@ -123,37 +160,37 @@ function test() {
       },
       value: 4,
       success: checkConsoleOutput.bind(null, hud),
       failure: finish,
     });
   }
 
   function checkConsoleOutput(hud) {
-    let errors = ["foobarBug762996a", "foobarBug762996b", "foobarBug762996load",
-                  "foobarBug762996click", "foobarBug762996consoleLog",
-                  "foobarBug762996css"];
-    errors.forEach(function(error) {
-      isnot(hud.outputNode.textContent.indexOf(error), -1,
-            error + " found in the Web Console output");
+    let msgs = ["foobarBug762996a", "foobarBug762996b", "foobarBug762996load",
+                "foobarBug762996click", "foobarBug762996consoleLog",
+                "foobarBug762996css", "fooBug788445"];
+    msgs.forEach(function(msg) {
+      isnot(hud.outputNode.textContent.indexOf(msg), -1,
+            msg + " found in the Web Console output");
     });
 
     hud.jsterm.clearOutput();
 
     is(hud.outputNode.textContent.indexOf("foobarBug762996color"), -1,
        "clearOutput() worked");
 
     expectUncaughtException();
     let button = content.document.querySelector("button");
     EventUtils.synthesizeMouse(button, 2, 2, {}, content);
 
-    waitForValue({
+    waitForButtonUpdate({
       name: "button shows one more error after another click in page",
-      validator: getErrorsCount,
-      value: 5,
+      errors: 5,
+      warnings: 1, // warnings are not repeated by the js engine
       success: function() {
         waitForValue(waitForNewError);
       },
       failure: finish,
     });
 
     let waitForNewError = {
       name: "the Web Console displays the new error",
@@ -168,34 +205,36 @@ function test() {
   function doClearConsoleButton(hud) {
     let clearButton = hud.ui.rootElement
                       .querySelector(".webconsole-clear-console-button");
     EventUtils.synthesizeMouse(clearButton, 2, 2, {}, hud.iframeWindow);
 
     is(hud.outputNode.textContent.indexOf("foobarBug762996click"), -1,
        "clear console button worked");
     is(getErrorsCount(), 0, "page errors counter has been reset");
+    let tooltip = getTooltipValues();
+    is(tooltip[1], 0, "page warnings counter has been reset");
 
     doPageReload(hud);
   }
 
   function doPageReload(hud) {
     tab1.linkedBrowser.addEventListener("load", function _onReload() {
       tab1.linkedBrowser.removeEventListener("load", _onReload, true);
       ignoreAllUncaughtExceptions(false);
       expectUncaughtException();
     }, true);
 
     ignoreAllUncaughtExceptions();
     content.location.reload();
 
-    waitForValue({
+    waitForButtonUpdate({
       name: "the Web Console button count has been reset after page reload",
-      validator: getErrorsCount,
-      value: 3,
+      errors: 3,
+      warnings: 0,
       success: function() {
         waitForValue(waitForConsoleOutputAfterReload);
       },
       failure: finish,
     });
 
     let waitForConsoleOutputAfterReload = {
       name: "the Web Console displays the correct number of errors after reload",
@@ -213,16 +252,17 @@ function test() {
   }
 
   function testEnd() {
     document.getElementById("developer-toolbar-closebutton").doCommand();
     let target1 = TargetFactory.forTab(tab1);
     gDevTools.closeToolbox(target1);
     gBrowser.removeTab(tab1);
     gBrowser.removeTab(tab2);
+    Services.prefs.clearUserPref("javascript.options.strict");
     finish();
   }
 
   function oneTimeObserve(name, callback) {
     function _onObserve(aSubject, aTopic, aData) {
       Services.obs.removeObserver(_onObserve, name);
       callback(aSubject, aTopic, aData);
     };
--- a/browser/devtools/tilt/TiltVisualizer.jsm
+++ b/browser/devtools/tilt/TiltVisualizer.jsm
@@ -189,17 +189,19 @@ TiltVisualizer.prototype = {
   /**
    * Unregister inspector event listeners.
    */
   unbindInspector: function TV_unbindInspector()
   {
     this._browserTab = null;
 
     if (this.inspector) {
-      this.inspector.selection.off("new-node", this.onNewNodeFromInspector);
+      if (this.inspector.selection) {
+        this.inspector.selection.off("new-node", this.onNewNodeFromInspector);
+      }
       this.inspector = null;
     }
 
     gDevTools.off("inspector-ready", this.onInspectorReady);
     gDevTools.off("toolbox-destroyed", this.onToolboxDestroyed);
 
     Services.obs.removeObserver(this.onNewNodeFromTilt,
                                 this.presenter.NOTIFICATIONS.HIGHLIGHTING);
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_653531_highlighter_console_helper.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_653531_highlighter_console_helper.js
@@ -45,17 +45,17 @@ function setupHighlighterTests()
   let h1 = content.document.querySelector("h1");
   ok(h1, "we have the header node");
 
   openInspector(runSelectionTests);
 }
 
 function runSelectionTests(aInspector)
 {
-  aInspector.highlighter.unlock();
+  aInspector.highlighter.unlockAndFocus();
   aInspector.highlighter.outline.setAttribute("disable-transitions", "true");
 
   executeSoon(function() {
     aInspector.selection.once("new-node", performTestComparisons);
     let h1 = content.document.querySelector("h1");
     EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content);
   });
 }
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,4 +1,4 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 0.7.28
+Current extension version is: 0.7.82
 
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -11,24 +11,23 @@
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 var PDFJS = {};
+PDFJS.version = '0.7.82';
+PDFJS.build = 'd467790';
 
 (function pdfjsWrapper() {
   // Use strict in our context only - users might not want it
   'use strict';
 
-  PDFJS.build =
-'e22ee54';
-
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 /* Copyright 2012 Mozilla Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
@@ -1029,17 +1028,24 @@ function isName(v) {
   return v instanceof Name;
 }
 
 function isCmd(v, cmd) {
   return v instanceof Cmd && (!cmd || v.cmd == cmd);
 }
 
 function isDict(v, type) {
-  return v instanceof Dict && (!type || v.get('Type').name == type);
+  if (!(v instanceof Dict)) {
+    return false;
+  }
+  if (!type) {
+    return true;
+  }
+  var dictType = v.get('Type');
+  return isName(dictType) && dictType.name == type;
 }
 
 function isArray(v) {
   return v instanceof Array;
 }
 
 function isStream(v) {
   return typeof v == 'object' && v != null && ('getChar' in v);
@@ -2124,17 +2130,17 @@ function addContextCurrentTransform(ctx)
 
 var CanvasExtraState = (function CanvasExtraStateClosure() {
   function CanvasExtraState(old) {
     // Are soft masks and alpha values shapes or opacities?
     this.alphaIsShape = false;
     this.fontSize = 0;
     this.fontSizeScale = 1;
     this.textMatrix = IDENTITY_MATRIX;
-    this.fontMatrix = IDENTITY_MATRIX;
+    this.fontMatrix = FONT_IDENTITY_MATRIX;
     this.leading = 0;
     // Current point (in user coordinates)
     this.x = 0;
     this.y = 0;
     // Start of text line (in text coordinates)
     this.lineX = 0;
     this.lineY = 0;
     // Character and word spacing
@@ -2210,67 +2216,119 @@ var CanvasGraphics = (function CanvasGra
           buffer[bufferPos] = 0;
         }
         bufferPos += 4;
         mask >>= 1;
       }
     }
   }
 
-  function rescaleImage(pixels, width, height, widthScale, heightScale) {
-    var scaledWidth = Math.ceil(width / widthScale);
-    var scaledHeight = Math.ceil(height / heightScale);
-
-    var itemsSum = new Float32Array(scaledWidth * scaledHeight * 3);
-    var itemsCount = new Float32Array(scaledWidth * scaledHeight);
-    var maxAlphas = new Uint8Array(scaledWidth * scaledHeight);
-    for (var i = 0, position = 0; i < height; i++) {
-      var lineOffset = (0 | (i / heightScale)) * scaledWidth;
-      for (var j = 0; j < width; j++) {
-        var countOffset = lineOffset + (0 | (j / widthScale));
-        var sumOffset = countOffset * 3;
-        var maxAlpha = maxAlphas[countOffset];
-        var currentAlpha = pixels[position + 3];
-        if (maxAlpha < currentAlpha) {
-          // lowering total alpha
-          var scale = 1 - (currentAlpha - maxAlpha) / 255;
-          itemsSum[sumOffset] *= scale;
-          itemsSum[sumOffset + 1] *= scale;
-          itemsSum[sumOffset + 2] *= scale;
-          maxAlphas[countOffset] = maxAlpha = currentAlpha;
-        }
-        if (maxAlpha > currentAlpha) {
-          var scale = 1 - (maxAlpha - currentAlpha) / 255;
-          itemsSum[sumOffset] += pixels[position] * scale;
-          itemsSum[sumOffset + 1] += pixels[position + 1] * scale;
-          itemsSum[sumOffset + 2] += pixels[position + 2] * scale;
-          itemsCount[countOffset] += scale;
-        } else {
-          itemsSum[sumOffset] += pixels[position];
-          itemsSum[sumOffset + 1] += pixels[position + 1];
-          itemsSum[sumOffset + 2] += pixels[position + 2];
-          itemsCount[countOffset]++;
-        }
-        position += 4;
-      }
-    }
-    var tmpCanvas = createScratchCanvas(scaledWidth, scaledHeight);
+  function putBinaryImageData(ctx, data, w, h) {
+    var tmpImgData = 'createImageData' in ctx ? ctx.createImageData(w, h) :
+      ctx.getImageData(0, 0, w, h);
+
+    var tmpImgDataPixels = tmpImgData.data;
+    if ('set' in tmpImgDataPixels)
+      tmpImgDataPixels.set(data);
+    else {
+      // Copy over the imageData pixel by pixel.
+      for (var i = 0, ii = tmpImgDataPixels.length; i < ii; i++)
+        tmpImgDataPixels[i] = data[i];
+    }
+
+    ctx.putImageData(tmpImgData, 0, 0);
+  }
+
+  function prescaleImage(pixels, width, height, widthScale, heightScale) {
+    pixels = new Uint8Array(pixels); // creating a copy
+    while (widthScale > 2 || heightScale > 2) {
+      if (heightScale > 2) {
+        // scaling image twice vertically
+        var rowSize = width * 4;
+        var k = 0, l = 0;
+        for (var i = 0; i < height - 1; i += 2) {
+          for (var j = 0; j < width; j++) {
+            var alpha1 = pixels[k + 3], alpha2 = pixels[k + 3 + rowSize];
+            if (alpha1 === alpha2) {
+              pixels[l] = (pixels[k] + pixels[k + rowSize]) >> 1;
+              pixels[l + 1] = (pixels[k + 1] + pixels[k + 1 + rowSize]) >> 1;
+              pixels[l + 2] = (pixels[k + 2] + pixels[k + 2 + rowSize]) >> 1;
+              pixels[l + 3] = alpha1;
+            } else if (alpha1 < alpha2) {
+              var d = 256 - alpha2 + alpha1;
+              pixels[l] = (pixels[k] * d + (pixels[k + rowSize] << 8)) >> 9;
+              pixels[l + 1] = (pixels[k + 1] * d +
+                              (pixels[k + 1 + rowSize] << 8)) >> 9;
+              pixels[l + 2] = (pixels[k + 2] * d +
+                              (pixels[k + 2 + rowSize] << 8)) >> 9;
+              pixels[l + 3] = alpha2;
+            } else {
+              var d = 256 - alpha1 + alpha2;
+              pixels[l] = ((pixels[k] << 8) + pixels[k + rowSize] * d) >> 9;
+              pixels[l + 1] = ((pixels[k + 1] << 8) +
+                              pixels[k + 1 + rowSize] * d) >> 9;
+              pixels[l + 2] = ((pixels[k + 2] << 8) +
+                              pixels[k + 2 + rowSize] * d) >> 9;
+              pixels[l + 3] = alpha1;
+            }
+            k += 4; l += 4;
+          }
+          k += rowSize;
+        }
+        if (height & 1) {
+          for (var i = 0; i < rowSize; i++) {
+            pixels[l++] = pixels[k++];
+          }
+        }
+        height = (height + 1) >> 1;
+        heightScale /= 2;
+      }
+      if (widthScale > 2) {
+        // scaling image twice horizontally
+        var k = 0, l = 0;
+        for (var i = 0; i < height; i++) {
+          for (var j = 0; j < width - 1; j += 2) {
+            var alpha1 = pixels[k + 3], alpha2 = pixels[k + 7];
+            if (alpha1 === alpha2) {
+              pixels[l] = (pixels[k] + pixels[k + 4]) >> 1;
+              pixels[l + 1] = (pixels[k + 1] + pixels[k + 5]) >> 1;
+              pixels[l + 2] = (pixels[k + 2] + pixels[k + 6]) >> 1;
+              pixels[l + 3] = alpha1;
+            } else if (alpha1 < alpha2) {
+              var d = 256 - alpha2 + alpha1;
+              pixels[l] = (pixels[k] * d + (pixels[k + 4] << 8)) >> 9;
+              pixels[l + 1] = (pixels[k + 1] * d + (pixels[k + 5] << 8)) >> 9;
+              pixels[l + 2] = (pixels[k + 2] * d + (pixels[k + 6] << 8)) >> 9;
+              pixels[l + 3] = alpha2;
+            } else {
+              var d = 256 - alpha1 + alpha2;
+              pixels[l] = ((pixels[k] << 8) + pixels[k + 4] * d) >> 9;
+              pixels[l + 1] = ((pixels[k + 1] << 8) + pixels[k + 5] * d) >> 9;
+              pixels[l + 2] = ((pixels[k + 2] << 8) + pixels[k + 6] * d) >> 9;
+              pixels[l + 3] = alpha1;
+            }
+            k += 8; l += 4;
+          }
+          if (width & 1) {
+            pixels[l++] = pixels[k++];
+            pixels[l++] = pixels[k++];
+            pixels[l++] = pixels[k++];
+            pixels[l++] = pixels[k++];
+          }
+        }
+        width = (width + 1) >> 1;
+        widthScale /= 2;
+      }
+    }
+
+    var tmpCanvas = createScratchCanvas(width, height);
     var tmpCtx = tmpCanvas.getContext('2d');
-    var imgData = tmpCtx.getImageData(0, 0, scaledWidth, scaledHeight);
-    pixels = imgData.data;
-    var j = 0, q = 0;
-    for (var i = 0, ii = scaledWidth * scaledHeight; i < ii; i++) {
-      var count = itemsCount[i];
-      pixels[j] = itemsSum[q++] / count;
-      pixels[j + 1] = itemsSum[q++] / count;
-      pixels[j + 2] = itemsSum[q++] / count;
-      pixels[j + 3] = maxAlphas[i];
-      j += 4;
-    }
-    tmpCtx.putImageData(imgData, 0, 0);
+    putBinaryImageData(tmpCtx, pixels.subarray(0, width * height * 4),
+                               width, height);
+
     return tmpCanvas;
   }
 
   var LINE_CAP_STYLES = ['butt', 'round', 'square'];
   var LINE_JOIN_STYLES = ['miter', 'round', 'bevel'];
   var NORMAL_CLIP = {};
   var EO_CLIP = {};
 
@@ -2681,36 +2739,33 @@ var CanvasGraphics = (function CanvasGra
     },
     setFont: function CanvasGraphics_setFont(fontRefName, size) {
       var fontObj = this.commonObjs.get(fontRefName);
       var current = this.current;
 
       if (!fontObj)
         error('Can\'t find font for ' + fontRefName);
 
-      // Slice-clone matrix so we can manipulate it without affecting original
-      if (fontObj.fontMatrix)
-        current.fontMatrix = fontObj.fontMatrix.slice(0);
-      else
-        current.fontMatrix = IDENTITY_MATRIX.slice(0);
+      current.fontMatrix = fontObj.fontMatrix ? fontObj.fontMatrix :
+                                                FONT_IDENTITY_MATRIX;
 
       // A valid matrix needs all main diagonal elements to be non-zero
       // This also ensures we bypass FF bugzilla bug #719844.
       if (current.fontMatrix[0] === 0 ||
           current.fontMatrix[3] === 0) {
         warn('Invalid font matrix for font ' + fontRefName);
       }
 
       // The spec for Tf (setFont) says that 'size' specifies the font 'scale',
       // and in some docs this can be negative (inverted x-y axes).
-      // We implement this condition with fontMatrix.
       if (size < 0) {
         size = -size;
-        current.fontMatrix[0] *= -1;
-        current.fontMatrix[3] *= -1;
+        current.fontDirection = -1;
+      } else {
+        current.fontDirection = 1;
       }
 
       this.current.font = fontObj;
       this.current.fontSize = size;
 
       if (fontObj.coded)
         return; // we don't need ctx.font for Type3 fonts
 
@@ -2753,24 +2808,23 @@ var CanvasGraphics = (function CanvasGra
       this.current.y = this.current.lineY = 0;
     },
     nextLine: function CanvasGraphics_nextLine() {
       this.moveText(0, this.current.leading);
     },
     applyTextTransforms: function CanvasGraphics_applyTextTransforms() {
       var ctx = this.ctx;
       var current = this.current;
-      var textHScale = current.textHScale;
-      var fontMatrix = current.fontMatrix || IDENTITY_MATRIX;
-
       ctx.transform.apply(ctx, current.textMatrix);
-      ctx.scale(1, -1);
-      ctx.translate(current.x, -current.y - current.textRise);
-      ctx.transform.apply(ctx, fontMatrix);
-      ctx.scale(textHScale, 1);
+      ctx.translate(current.x, current.y + current.textRise);
+      if (current.fontDirection > 0) {
+        ctx.scale(current.textHScale, -1);
+      } else {
+        ctx.scale(-current.textHScale, 1);
+      }
     },
     createTextGeometry: function CanvasGraphics_createTextGeometry() {
       var geometry = {};
       var ctx = this.ctx;
       var font = this.current.font;
       var ctxMatrix = ctx.mozCurrentTransform;
       if (ctxMatrix) {
         var bl = Util.applyTransform([0, 0], ctxMatrix);
@@ -2791,19 +2845,18 @@ var CanvasGraphics = (function CanvasGra
       var ctx = this.ctx;
       var current = this.current;
       var font = current.font;
       var glyphs = font.charsToGlyphs(str);
       var fontSize = current.fontSize;
       var fontSizeScale = current.fontSizeScale;
       var charSpacing = current.charSpacing;
       var wordSpacing = current.wordSpacing;
-      var textHScale = current.textHScale;
-      var fontMatrix = current.fontMatrix || IDENTITY_MATRIX;
-      var textHScale2 = textHScale * fontMatrix[0];
+      var textHScale = current.textHScale * current.fontDirection;
+      var fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX;
       var glyphsLength = glyphs.length;
       var textLayer = this.textLayer;
       var geom;
       var textSelection = textLayer && !skipTextSelection ? true : false;
       var textRenderingMode = current.textRenderingMode;
       var canvasWidth = 0.0;
 
       // Type3 fonts - each glyph is a "mini-PDF"
@@ -2832,33 +2885,32 @@ var CanvasGraphics = (function CanvasGra
 
           this.save();
           ctx.scale(fontSize, fontSize);
           ctx.transform.apply(ctx, fontMatrix);
           this.executeOperatorList(glyph.operatorList);
           this.restore();
 
           var transformed = Util.applyTransform([glyph.width, 0], fontMatrix);
-          var width = transformed[0] * fontSize +
-              Util.sign(current.fontMatrix[0]) * charSpacing;
+          var width = (transformed[0] * fontSize + charSpacing) *
+                      current.fontDirection;
 
           ctx.translate(width, 0);
           current.x += width * textHScale;
 
           canvasWidth += width;
         }
         ctx.restore();
       } else {
         ctx.save();
         this.applyTextTransforms();
 
         var lineWidth = current.lineWidth;
         var a1 = current.textMatrix[0], b1 = current.textMatrix[1];
-        var a2 = fontMatrix[0], b2 = fontMatrix[1];
-        var scale = Math.sqrt((a1 * a1 + b1 * b1) * (a2 * a2 + b2 * b2));
+        var scale = Math.sqrt(a1 * a1 + b1 * b1);
         if (scale == 0 || lineWidth == 0)
           lineWidth = this.getSinglePixelWidth();
         else
           lineWidth /= scale;
 
         if (textSelection)
           geom = this.createTextGeometry();
 
@@ -2869,23 +2921,23 @@ var CanvasGraphics = (function CanvasGra
 
         ctx.lineWidth = lineWidth;
 
         var x = 0;
         for (var i = 0; i < glyphsLength; ++i) {
           var glyph = glyphs[i];
           if (glyph === null) {
             // word break
-            x += Util.sign(current.fontMatrix[0]) * wordSpacing;
+            x += current.fontDirection * wordSpacing;
             continue;
           }
 
           var character = glyph.fontChar;
-          var charWidth = glyph.width * fontSize * 0.001 +
-              Util.sign(current.fontMatrix[0]) * charSpacing;
+          var charWidth = glyph.width * fontSize * current.fontMatrix[0] +
+                          charSpacing * current.fontDirection;
 
           if (!glyph.disabled) {
             var scaledX = x / fontSizeScale;
             switch (textRenderingMode) {
               default: // other unsupported rendering modes
               case TextRenderingMode.FILL:
               case TextRenderingMode.FILL_ADD_TO_PATH:
                 ctx.fillText(character, scaledX, 0);
@@ -2908,59 +2960,52 @@ var CanvasGraphics = (function CanvasGra
               clipCtx.fillText(character, scaledX, 0);
             }
           }
 
           x += charWidth;
 
           canvasWidth += charWidth;
         }
-        current.x += x * textHScale2;
+        current.x += x * textHScale;
         ctx.restore();
       }
 
       if (textSelection) {
         geom.canvasWidth = canvasWidth;
         this.textLayer.appendText(geom);
       }
 
       return canvasWidth;
     },
     showSpacedText: function CanvasGraphics_showSpacedText(arr) {
       var ctx = this.ctx;
       var current = this.current;
       var font = current.font;
       var fontSize = current.fontSize;
-      var textHScale = current.textHScale;
-      if (!font.coded)
-        textHScale *= (current.fontMatrix || IDENTITY_MATRIX)[0];
+      var textHScale = current.textHScale * (current.fontMatrix && !font.coded ?
+        current.fontMatrix[0] : FONT_IDENTITY_MATRIX[0]) *
+        current.fontDirection;
       var arrLength = arr.length;
       var textLayer = this.textLayer;
       var geom;
       var canvasWidth = 0.0;
       var textSelection = textLayer ? true : false;
 
       if (textSelection) {
         ctx.save();
-        // Type3 fonts - each glyph is a "mini-PDF" (see also showText)
-        if (font.coded) {
-          ctx.transform.apply(ctx, current.textMatrix);
-          ctx.scale(1, -1);
-          ctx.translate(current.x, -1 * current.y);
-          ctx.scale(textHScale, 1);
-        } else
-          this.applyTextTransforms();
+        this.applyTextTransforms();
         geom = this.createTextGeometry();
         ctx.restore();
       }
 
       for (var i = 0; i < arrLength; ++i) {
         var e = arr[i];
         if (isNum(e)) {
-          var spacingLength = -e * 0.001 * fontSize * textHScale;
+          var spacingLength = -e * fontSize * textHScale;
           current.x += spacingLength;
 
           if (textSelection)
             canvasWidth += spacingLength;
         } else if (isString(e)) {
           var shownCanvasWidth = this.showText(e, true);
 
           if (textSelection)
@@ -3279,75 +3324,56 @@ var CanvasGraphics = (function CanvasGra
       ctx.scale(1 / width, -1 / height);
 
       var currentTransform = ctx.mozCurrentTransformInverse;
       var widthScale = Math.max(Math.abs(currentTransform[0]), 1);
       var heightScale = Math.max(Math.abs(currentTransform[3]), 1);
       var tmpCanvas = createScratchCanvas(width, height);
       var tmpCtx = tmpCanvas.getContext('2d');
 
-      if (widthScale >= 2 || heightScale >= 2) {
+      if (widthScale > 2 || heightScale > 2) {
         // canvas does not resize well large images to small -- using simple
         // algorithm to perform pre-scaling
-        tmpCanvas = rescaleImage(imgData.data,
+        tmpCanvas = prescaleImage(imgData.data,
                                  width, height,
                                  widthScale, heightScale);
-        ctx.scale(widthScale, heightScale);
-        ctx.drawImage(tmpCanvas, 0, -height / heightScale);
+        ctx.drawImage(tmpCanvas, 0, 0, tmpCanvas.width, tmpCanvas.height,
+                                 0, -height, width, height);
       } else {
         if (typeof ImageData !== 'undefined' && imgData instanceof ImageData) {
           tmpCtx.putImageData(imgData, 0, 0);
         } else {
-          this.putBinaryImageData(tmpCtx, imgData);
+          putBinaryImageData(tmpCtx, imgData.data, width, height);
         }
         ctx.drawImage(tmpCanvas, 0, -height);
       }
       this.restore();
     },
 
     paintInlineImageXObjectGroup:
       function CanvasGraphics_paintInlineImageXObjectGroup(imgData, map) {
       var ctx = this.ctx;
       var w = imgData.width;
       var h = imgData.height;
 
       var tmpCanvas = createScratchCanvas(w, h);
       var tmpCtx = tmpCanvas.getContext('2d');
-      this.putBinaryImageData(tmpCtx, imgData);
+      putBinaryImageData(tmpCtx, imgData.data, w, h);
 
       for (var i = 0, ii = map.length; i < ii; i++) {
         var entry = map[i];
         ctx.save();
         ctx.transform.apply(ctx, entry.transform);
         ctx.scale(1, -1);
         ctx.drawImage(tmpCanvas, entry.x, entry.y, entry.w, entry.h,
                       0, -1, 1, 1);
         ctx.restore();
       }
     },
 
-    putBinaryImageData: function CanvasGraphics_putBinaryImageData(ctx,
-                                                                   imgData) {
-      var w = imgData.width, h = imgData.height;
-      var tmpImgData = 'createImageData' in ctx ? ctx.createImageData(w, h) :
-        ctx.getImageData(0, 0, w, h);
-
-      var tmpImgDataPixels = tmpImgData.data;
-      var data = imgData.data;
-      if ('set' in tmpImgDataPixels)
-        tmpImgDataPixels.set(data);
-      else {
-        // Copy over the imageData pixel by pixel.
-        for (var i = 0, ii = tmpImgDataPixels.length; i < ii; i++)
-          tmpImgDataPixels[i] = data[i];
-      }
-
-      ctx.putImageData(tmpImgData, 0, 0);
-    },
-
     // Marked content
 
     markPoint: function CanvasGraphics_markPoint(tag) {
       // TODO Marked content.
     },
     markPointProps: function CanvasGraphics_markPointProps(tag, properties) {
       // TODO Marked content.
     },
@@ -3732,18 +3758,19 @@ var XRef = (function XRefClosure() {
     var trailerDict = this.readXRef(startXRef);
     trailerDict.assignXref(this);
     this.trailer = trailerDict;
     // prepare the XRef cache
     this.cache = [];
 
     var encrypt = trailerDict.get('Encrypt');
     if (encrypt) {
-      var fileId = trailerDict.get('ID');
-      this.encrypt = new CipherTransformFactory(encrypt, fileId[0], password);
+      var ids = trailerDict.get('ID');
+      var fileId = (ids && ids.length) ? ids[0] : '';
+      this.encrypt = new CipherTransformFactory(encrypt, fileId, password);
     }
 
     // get the root dictionary (catalog) object
     if (!(this.root = trailerDict.get('Root')))
       error('Invalid root reference');
   }
 
   XRef.prototype = {
@@ -14174,111 +14201,114 @@ var PartialEvaluator = (function Partial
     this.xref = xref;
     this.handler = handler;
     this.pageIndex = pageIndex;
     this.uniquePrefix = uniquePrefix;
     this.objIdCounter = 0;
     this.fontIdCounter = 0;
   }
 
+  // Specifies properties for each command
+  //
+  // If variableArgs === true: [0, `numArgs`] expected
+  // If variableArgs === false: exactly `numArgs` expected
   var OP_MAP = {
-    // Graphics state
-    w: 'setLineWidth',
-    J: 'setLineCap',
-    j: 'setLineJoin',
-    M: 'setMiterLimit',
-    d: 'setDash',
-    ri: 'setRenderingIntent',
-    i: 'setFlatness',
-    gs: 'setGState',
-    q: 'save',
-    Q: 'restore',
-    cm: 'transform',
+    // Graphic state
+    w: { fnName: 'setLineWidth', numArgs: 1, variableArgs: false },
+    J: { fnName: 'setLineCap', numArgs: 1, variableArgs: false },
+    j: { fnName: 'setLineJoin', numArgs: 1, variableArgs: false },
+    M: { fnName: 'setMiterLimit', numArgs: 1, variableArgs: false },
+    d: { fnName: 'setDash', numArgs: 2, variableArgs: false },
+    ri: { fnName: 'setRenderingIntent', numArgs: 1, variableArgs: false },
+    i: { fnName: 'setFlatness', numArgs: 1, variableArgs: false },
+    gs: { fnName: 'setGState', numArgs: 1, variableArgs: false },
+    q: { fnName: 'save', numArgs: 0, variableArgs: false },
+    Q: { fnName: 'restore', numArgs: 0, variableArgs: false },
+    cm: { fnName: 'transform', numArgs: 6, variableArgs: false },
 
     // Path
-    m: 'moveTo',
-    l: 'lineTo',
-    c: 'curveTo',
-    v: 'curveTo2',
-    y: 'curveTo3',
-    h: 'closePath',
-    re: 'rectangle',
-    S: 'stroke',
-    s: 'closeStroke',
-    f: 'fill',
-    F: 'fill',
-    'f*': 'eoFill',
-    B: 'fillStroke',
-    'B*': 'eoFillStroke',
-    b: 'closeFillStroke',
-    'b*': 'closeEOFillStroke',
-    n: 'endPath',
+    m: { fnName: 'moveTo', numArgs: 2, variableArgs: false },
+    l: { fnName: 'lineTo', numArgs: 2, variableArgs: false },
+    c: { fnName: 'curveTo', numArgs: 6, variableArgs: false },
+    v: { fnName: 'curveTo2', numArgs: 4, variableArgs: false },
+    y: { fnName: 'curveTo3', numArgs: 4, variableArgs: false },
+    h: { fnName: 'closePath', numArgs: 0, variableArgs: false },
+    re: { fnName: 'rectangle', numArgs: 4, variableArgs: false },
+    S: { fnName: 'stroke', numArgs: 0, variableArgs: false },
+    s: { fnName: 'closeStroke', numArgs: 0, variableArgs: false },
+    f: { fnName: 'fill', numArgs: 0, variableArgs: false },
+    F: { fnName: 'fill', numArgs: 0, variableArgs: false },
+    'f*': { fnName: 'eoFill', numArgs: 0, variableArgs: false },
+    B: { fnName: 'fillStroke', numArgs: 0, variableArgs: false },
+    'B*': { fnName: 'eoFillStroke', numArgs: 0, variableArgs: false },
+    b: { fnName: 'closeFillStroke', numArgs: 0, variableArgs: false },
+    'b*': { fnName: 'closeEOFillStroke', numArgs: 0, variableArgs: false },
+    n: { fnName: 'endPath', numArgs: 0, variableArgs: false },
 
     // Clipping
-    W: 'clip',
-    'W*': 'eoClip',
+    W: { fnName: 'clip', numArgs: 0, variableArgs: false },
+    'W*': { fnName: 'eoClip', numArgs: 0, variableArgs: false },
 
     // Text
-    BT: 'beginText',
-    ET: 'endText',
-    Tc: 'setCharSpacing',
-    Tw: 'setWordSpacing',
-    Tz: 'setHScale',
-    TL: 'setLeading',
-    Tf: 'setFont',
-    Tr: 'setTextRenderingMode',
-    Ts: 'setTextRise',
-    Td: 'moveText',
-    TD: 'setLeadingMoveText',
-    Tm: 'setTextMatrix',
-    'T*': 'nextLine',
-    Tj: 'showText',
-    TJ: 'showSpacedText',
-    "'": 'nextLineShowText',
-    '"': 'nextLineSetSpacingShowText',
+    BT: { fnName: 'beginText', numArgs: 0, variableArgs: false },
+    ET: { fnName: 'endText', numArgs: 0, variableArgs: false },
+    Tc: { fnName: 'setCharSpacing', numArgs: 1, variableArgs: false },
+    Tw: { fnName: 'setWordSpacing', numArgs: 1, variableArgs: false },
+    Tz: { fnName: 'setHScale', numArgs: 1, variableArgs: false },
+    TL: { fnName: 'setLeading', numArgs: 1, variableArgs: false },
+    Tf: { fnName: 'setFont', numArgs: 2, variableArgs: false },
+    Tr: { fnName: 'setTextRenderingMode', numArgs: 1, variableArgs: false },
+    Ts: { fnName: 'setTextRise', numArgs: 1, variableArgs: false },
+    Td: { fnName: 'moveText', numArgs: 2, variableArgs: false },
+    TD: { fnName: 'setLeadingMoveText', numArgs: 2, variableArgs: false },
+    Tm: { fnName: 'setTextMatrix', numArgs: 6, variableArgs: false },
+    'T*': { fnName: 'nextLine', numArgs: 0, variableArgs: false },
+    Tj: { fnName: 'showText', numArgs: 1, variableArgs: false },
+    TJ: { fnName: 'showSpacedText', numArgs: 1, variableArgs: false },
+    '\'': { fnName: 'nextLineShowText', numArgs: 1, variableArgs: false },
+    '"': { fnName: 'nextLineSetSpacingShowText', numArgs: 3,
+      variableArgs: false },
 
     // Type3 fonts
-    d0: 'setCharWidth',
-    d1: 'setCharWidthAndBounds',
+    d0: { fnName: 'setCharWidth', numArgs: 2, variableArgs: false },
+    d1: { fnName: 'setCharWidthAndBounds', numArgs: 6, variableArgs: false },
 
     // Color
-    CS: 'setStrokeColorSpace',
-    cs: 'setFillColorSpace',
-    SC: 'setStrokeColor',
-    SCN: 'setStrokeColorN',
-    sc: 'setFillColor',
-    scn: 'setFillColorN',
-    G: 'setStrokeGray',
-    g: 'setFillGray',
-    RG: 'setStrokeRGBColor',
-    rg: 'setFillRGBColor',
-    K: 'setStrokeCMYKColor',
-    k: 'setFillCMYKColor',
+    CS: { fnName: 'setStrokeColorSpace', numArgs: 1, variableArgs: false },
+    cs: { fnName: 'setFillColorSpace', numArgs: 1, variableArgs: false },
+    SC: { fnName: 'setStrokeColor', numArgs: 4, variableArgs: true },
+    SCN: { fnName: 'setStrokeColorN', numArgs: 33, variableArgs: true },
+    sc: { fnName: 'setFillColor', numArgs: 4, variableArgs: true },
+    scn: { fnName: 'setFillColorN', numArgs: 33, variableArgs: true },
+    G: { fnName: 'setStrokeGray', numArgs: 1, variableArgs: false },
+    g: { fnName: 'setFillGray', numArgs: 1, variableArgs: false },
+    RG: { fnName: 'setStrokeRGBColor', numArgs: 3, variableArgs: false },
+    rg: { fnName: 'setFillRGBColor', numArgs: 3, variableArgs: false },
+    K: { fnName: 'setStrokeCMYKColor', numArgs: 4, variableArgs: false },
+    k: { fnName: 'setFillCMYKColor', numArgs: 4, variableArgs: false },
 
     // Shading
-    sh: 'shadingFill',
+    sh: { fnName: 'shadingFill', numArgs: 1, variableArgs: false },
 
     // Images
-    BI: 'beginInlineImage',
-    ID: 'beginImageData',
-    EI: 'endInlineImage',
+    BI: { fnName: 'beginInlineImage', numArgs: 0, variableArgs: false },
+    ID: { fnName: 'beginImageData', numArgs: 0, variableArgs: false },
+    EI: { fnName: 'endInlineImage', numArgs: 0, variableArgs: false },
 
     // XObjects
-    Do: 'paintXObject',
-
-    // Marked content
-    MP: 'markPoint',
-    DP: 'markPointProps',
-    BMC: 'beginMarkedContent',
-    BDC: 'beginMarkedContentProps',
-    EMC: 'endMarkedContent',
+    Do: { fnName: 'paintXObject', numArgs: 1, variableArgs: false },
+    MP: { fnName: 'markPoint', numArgs: 1, variableArgs: false },
+    DP: { fnName: 'markPointProps', numArgs: 2, variableArgs: false },
+    BMC: { fnName: 'beginMarkedContent', numArgs: 1, variableArgs: false },
+    BDC: { fnName: 'beginMarkedContentProps', numArgs: 2, variableArgs: false },
+    EMC: { fnName: 'endMarkedContent', numArgs: 0, variableArgs: false },
 
     // Compatibility
-    BX: 'beginCompat',
-    EX: 'endCompat',
+    BX: { fnName: 'beginCompat', numArgs: 0, variableArgs: false },
+    EX: { fnName: 'endCompat', numArgs: 0, variableArgs: false },
 
     // (reserved partial commands for the lexer)
     BM: null,
     BD: null,
     'true': null,
     fa: null,
     fal: null,
     fals: null,
@@ -14458,30 +14488,61 @@ var PartialEvaluator = (function Partial
       }
 
       var fnArray = queue.fnArray, argsArray = queue.argsArray;
       var dependencyArray = dependency || [];
 
       resources = resources || new Dict();
       var xobjs = resources.get('XObject') || new Dict();
       var patterns = resources.get('Pattern') || new Dict();
+      // TODO(mduan): pass array of knownCommands rather than OP_MAP
+      // dictionary
       var parser = new Parser(new Lexer(stream, OP_MAP), false, xref);
       var res = resources;
       var args = [], obj;
       var TILING_PATTERN = 1, SHADING_PATTERN = 2;
 
       while (true) {
         obj = parser.getObj();
-        if (isEOF(obj))
-          break;
+        if (isEOF(obj)) {
+          break;
+        }
 
         if (isCmd(obj)) {
           var cmd = obj.cmd;
-          var fn = OP_MAP[cmd];
-          assertWellFormed(fn, 'Unknown command "' + cmd + '"');
+
+          // Check that the command is valid
+          var opSpec = OP_MAP[cmd];
+          if (!opSpec) {
+            warn('Unknown command "' + cmd + '"');
+            continue;
+          }
+
+          var fn = opSpec.fnName;
+
+          // Validate the number of arguments for the command
+          if (opSpec.variableArgs) {
+            if (args.length > opSpec.numArgs) {
+              info('Command ' + fn + ': expected [0,' + opSpec.numArgs +
+                  '] args, but received ' + args.length + ' args');
+            }
+          } else {
+            if (args.length < opSpec.numArgs) {
+              // If we receive too few args, it's not possible to possible
+              // to execute the command, so skip the command
+              info('Command ' + fn + ': because expected ' + opSpec.numArgs +
+                  ' args, but received ' + args.length + ' args; skipping');
+              args = [];
+              continue;
+            } else if (args.length > opSpec.numArgs) {
+              info('Command ' + fn + ': expected ' + opSpec.numArgs +
+                  ' args, but received ' + args.length + ' args');
+            }
+          }
+
           // TODO figure out how to type-check vararg functions
 
           if ((cmd == 'SCN' || cmd == 'scn') && !args[args.length - 1].code) {
             // compile tiling patterns
             var patternName = args[args.length - 1];
             // SCN/scn applies patterns along with normal colors
             if (isName(patternName)) {
               var pattern = patterns.get(patternName.name);
@@ -14653,18 +14714,18 @@ var PartialEvaluator = (function Partial
               args = [gsStateObj];
               break;
           } // switch
 
           fnArray.push(fn);
           argsArray.push(args);
           args = [];
         } else if (obj != null) {
+          args.push(obj instanceof Dict ? obj.getAll() : obj);
           assertWellFormed(args.length <= 33, 'Too many arguments');
-          args.push(obj instanceof Dict ? obj.getAll() : obj);
         }
       }
 
       return queue;
     },
 
     optimizeQueue: function PartialEvaluator_optimizeQueue(queue) {
       var fnArray = queue.fnArray, argsArray = queue.argsArray;
@@ -14935,16 +14996,20 @@ var PartialEvaluator = (function Partial
         if (isDict(cidSystemInfo)) {
           properties.cidSystemInfo = {
             registry: cidSystemInfo.get('Registry'),
             ordering: cidSystemInfo.get('Ordering'),
             supplement: cidSystemInfo.get('Supplement')
           };
         }
 
+        var cidEncoding = baseDict.get('Encoding');
+        if (isName(cidEncoding))
+          properties.cidEncoding = cidEncoding.name;
+
         var cidToGidMap = dict.get('CIDToGIDMap');
         if (isStream(cidToGidMap))
           properties.cidToGidMap = this.readCidToGidMap(cidToGidMap);
       }
 
       var flags = properties.flags;
       var differences = [];
       var baseEncoding = Encodings.StandardEncoding;
@@ -15195,19 +15260,40 @@ var PartialEvaluator = (function Partial
       }
       if (isMonospace)
         properties.flags |= FontFlags.FixedPitch;
 
       properties.defaultWidth = defaultWidth;
       properties.widths = glyphsWidths;
     },
 
+    isSerifFont: function PartialEvaluator_isSerifFont(baseFontName) {
+
+      // Simulating descriptor flags attribute
+      var fontNameWoStyle = baseFontName.split('-')[0];
+      return (fontNameWoStyle in serifFonts) ||
+          (fontNameWoStyle.search(/serif/gi) !== -1);
+    },
+
     getBaseFontMetrics: function PartialEvaluator_getBaseFontMetrics(name) {
       var defaultWidth = 0, widths = [], monospace = false;
-      var glyphWidths = Metrics[stdFontMap[name] || name];
+
+      var lookupName = stdFontMap[name] || name;
+
+      if (!(lookupName in Metrics)) {
+        // Use default fonts for looking up font metrics if the passed
+        // font is not a base font
+        if (this.isSerifFont(name)) {
+          lookupName = 'Times-Roman';
+        } else {
+          lookupName = 'Helvetica';
+        }
+      }
+      var glyphWidths = Metrics[lookupName];
+
       if (isNum(glyphWidths)) {
         defaultWidth = glyphWidths;
         monospace = true;
       } else {
         widths = glyphWidths;
       }
 
       return {
@@ -15259,18 +15345,18 @@ var PartialEvaluator = (function Partial
             error('Base font is not specified');
 
           // Using base font name as a font name.
           baseFontName = baseFontName.name.replace(/[,_]/g, '-');
           var metrics = this.getBaseFontMetrics(baseFontName);
 
           // Simulating descriptor flags attribute
           var fontNameWoStyle = baseFontName.split('-')[0];
-          var flags = (serifFonts[fontNameWoStyle] ||
-            (fontNameWoStyle.search(/serif/gi) != -1) ? FontFlags.Serif : 0) |
+          var flags = (
+            this.isSerifFont(fontNameWoStyle) ? FontFlags.Serif : 0) |
             (metrics.monospace ? FontFlags.FixedPitch : 0) |
             (symbolsFonts[fontNameWoStyle] ? FontFlags.Symbolic :
             FontFlags.Nonsymbolic);
 
           var properties = {
             type: type.name,
             widths: metrics.widths,
             defaultWidth: metrics.defaultWidth,
@@ -15286,20 +15372,36 @@ var PartialEvaluator = (function Partial
 
       // According to the spec if 'FontDescriptor' is declared, 'FirstChar',
       // 'LastChar' and 'Widths' should exist too, but some PDF encoders seem
       // to ignore this rule when a variant of a standart font is used.
       // TODO Fill the width array depending on which of the base font this is
       // a variant.
       var firstChar = dict.get('FirstChar') || 0;
       var lastChar = dict.get('LastChar') || maxCharIndex;
+
       var fontName = descriptor.get('FontName');
+      var baseFont = baseDict.get('BaseFont');
       // Some bad pdf's have a string as the font name.
-      if (isString(fontName))
+      if (isString(fontName)) {
         fontName = new Name(fontName);
+      }
+      if (isString(baseFont)) {
+        baseFont = new Name(baseFont);
+      }
+
+      var fontNameStr = fontName && fontName.name;
+      var baseFontStr = baseFont && baseFont.name;
+      if (fontNameStr !== baseFontStr) {
+        warn('The FontDescriptor\'s FontName is "' + fontNameStr +
+            '" but should be the same as the Font\'s BaseFont "' +
+            baseFontStr + '"');
+      }
+      fontName = fontName || baseFont;
+
       assertWellFormed(isName(fontName), 'invalid font name');
 
       var fontFile = descriptor.get('FontFile', 'FontFile2', 'FontFile3');
       if (fontFile) {
         if (fontFile.dict) {
           var subtype = fontFile.dict.get('Subtype');
           if (subtype)
             subtype = subtype.name;
@@ -15315,17 +15417,17 @@ var PartialEvaluator = (function Partial
         subtype: subtype,
         file: fontFile,
         length1: length1,
         length2: length2,
         loadedName: baseDict.loadedName,
         composite: composite,
         wideChars: composite,
         fixedPitch: false,
-        fontMatrix: dict.get('FontMatrix') || IDENTITY_MATRIX,
+        fontMatrix: dict.get('FontMatrix') || FONT_IDENTITY_MATRIX,
         firstChar: firstChar || 0,
         lastChar: lastChar || maxCharIndex,
         bbox: descriptor.get('FontBBox'),
         ascent: descriptor.get('Ascent'),
         descent: descriptor.get('Descent'),
         xHeight: descriptor.get('XHeight'),
         capHeight: descriptor.get('CapHeight'),
         flags: descriptor.get('Flags'),
@@ -15378,16 +15480,18 @@ var SYMBOLIC_FONT_GLYPH_OFFSET = 0xF000;
 
 // PDF Glyph Space Units are one Thousandth of a TextSpace Unit
 // except for Type 3 fonts
 var PDF_GLYPH_SPACE_UNITS = 1000;
 
 // Until hinting is fully supported this constant can be used
 var HINTING_ENABLED = false;
 
+var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
+
 var FontFlags = {
   FixedPitch: 1,
   Serif: 2,
   Symbolic: 4,
   Script: 8,
   Nonsymbolic: 32,
   Italic: 64,
   AllCap: 65536,
@@ -15748,16 +15852,58 @@ var serifFonts = {
   'Versailles': true, 'Wanted': true, 'Weiss': true,
   'Wide Latin': true, 'Windsor': true, 'XITS': true
 };
 
 var symbolsFonts = {
   'Dingbats': true, 'Symbol': true, 'ZapfDingbats': true
 };
 
+var CMapConverterList = {
+  'H': jis7ToUnicode,
+  'V': jis7ToUnicode,
+  'EUC-H': eucjpToUnicode,
+  'EUC-V': eucjpToUnicode,
+  '90ms-RKSJ-H': sjisToUnicode,
+  '90ms-RKSJ-V': sjisToUnicode,
+  '90msp-RKSJ-H': sjisToUnicode,
+  '90msp-RKSJ-V': sjisToUnicode
+};
+
+var decodeBytes;
+if (typeof TextDecoder !== 'undefined') {
+  decodeBytes = function(bytes, encoding) {
+    return new TextDecoder(encoding).decode(bytes);
+  };
+} else if (typeof FileReaderSync !== 'undefined') {
+  decodeBytes = function(bytes, encoding) {
+    return new FileReaderSync().readAsText(new Blob([bytes]), encoding);
+  };
+} else {
+  // Clear the list so that decodeBytes will never be called.
+  CMapConverterList = {};
+}
+
+function jis7ToUnicode(str) {
+  var bytes = stringToBytes(str);
+  var length = bytes.length;
+  for (var i = 0; i < length; ++i) {
+    bytes[i] |= 0x80;
+  }
+  return decodeBytes(bytes, 'euc-jp');
+}
+
+function eucjpToUnicode(str) {
+  return decodeBytes(stringToBytes(str), 'euc-jp');
+}
+
+function sjisToUnicode(str) {
+  return decodeBytes(stringToBytes(str), 'shift_jis');
+}
+
 // Some characters, e.g. copyrightserif, mapped to the private use area and
 // might not be displayed using standard fonts. Mapping/hacking well-known chars
 // to the similar equivalents in the normal characters range.
 function mapPrivateUseChars(code) {
   switch (code) {
     case 0xF8E9: // copyrightsans
     case 0xF6D9: // copyrightserif
       return 0x00A9; // copyright
@@ -17377,16 +17523,29 @@ function fontCharsToUnicode(charCodes, f
     var glyphUnicode = glyph.unicode;
     if (glyphUnicode in NormalizedUnicodes)
       glyphUnicode = NormalizedUnicodes[glyphUnicode];
     result += reverseIfRtl(glyphUnicode);
   }
   return result;
 }
 
+function adjustWidths(properties) {
+  if (properties.fontMatrix[0] === FONT_IDENTITY_MATRIX[0]) {
+    return;
+  }
+  // adjusting width to fontMatrix scale
+  var scale = 0.001 / properties.fontMatrix[0];
+  var glyphsWidths = properties.widths;
+  for (var glyph in glyphsWidths) {
+    glyphsWidths[glyph] *= scale;
+  }
+  properties.defaultWidth *= scale;
+}
+
 /**
  * 'Font' is the class the outside world should use, it encapsulate all the font
  * decoding logics whatever type it is (assuming the font type is supported).
  *
  * For example to read a Type1 font and to attach it to the document:
  *   var type1Font = new Font("MyFontName", binaryFile, propertiesObject);
  *   type1Font.bind();
  */
@@ -17423,24 +17582,24 @@ var Font = (function FontClosure() {
     this.differences = properties.differences;
     this.widths = properties.widths;
     this.defaultWidth = properties.defaultWidth;
     this.composite = properties.composite;
     this.wideChars = properties.wideChars;
     this.hasEncoding = properties.hasEncoding;
 
     this.fontMatrix = properties.fontMatrix;
-    this.widthMultiplier = 1.0;
     if (properties.type == 'Type3') {
       this.encoding = properties.baseEncoding;
       return;
     }
 
     // Trying to fix encoding using glyph CIDSystemInfo.
     this.loadCidToUnicode(properties);
+    this.cidEncoding = properties.cidEncoding;
 
     if (properties.toUnicode)
       this.toUnicode = properties.toUnicode;
     else
       this.rebuildToUnicode(properties);
 
     this.toFontChar = this.buildToFontChar(this.toUnicode);
 
@@ -17481,16 +17640,18 @@ var Font = (function FontClosure() {
     switch (type) {
       case 'Type1':
       case 'CIDFontType0':
         this.mimetype = 'font/opentype';
 
         var cff = (subtype == 'Type1C' || subtype == 'CIDFontType0C') ?
           new CFFFont(file, properties) : new Type1Font(name, file, properties);
 
+        adjustWidths(properties);
+
         // Wrap the CFF data inside an OTF font file
         data = this.convert(name, cff, properties);
         break;
 
       case 'TrueType':
       case 'CIDFontType2':
         this.mimetype = 'font/opentype';
 
@@ -17500,20 +17661,23 @@ var Font = (function FontClosure() {
         break;
 
       default:
         warn('Font ' + type + ' is not supported');
         break;
     }
 
     this.data = data;
+
+    // Transfer some properties again that could change during font conversion
     this.fontMatrix = properties.fontMatrix;
-    this.widthMultiplier = !properties.fontMatrix ? 1.0 :
-      1.0 / properties.fontMatrix[0];
+    this.widths = properties.widths;
+    this.defaultWidth = properties.defaultWidth;
     this.encoding = properties.baseEncoding;
+
     this.loading = true;
   };
 
   var numFonts = 0;
   function getUniqueName() {
     return 'pdfFont' + numFonts++;
   }
 
@@ -17790,35 +17954,35 @@ var Font = (function FontClosure() {
         }
       }
     } else {
       // TODO
       firstCharIndex = 0;
       lastCharIndex = 255;
     }
 
-    var unitsPerEm = override.unitsPerEm || PDF_GLYPH_SPACE_UNITS;
-    var typoAscent = override.ascent || properties.ascent;
-    var typoDescent = override.descent || properties.descent;
+    var bbox = properties.bbox || [0, 0, 0, 0];
+    var unitsPerEm = override.unitsPerEm ||
+      1 / (properties.fontMatrix || FONT_IDENTITY_MATRIX)[0];
+
+    // if the font units differ to the PDF glyph space units
+    // then scale up the values
+    var scale = properties.ascentScaled ? 1.0 :
+      unitsPerEm / PDF_GLYPH_SPACE_UNITS;
+
+    var typoAscent = override.ascent || Math.round(scale *
+      (properties.ascent || bbox[3]));
+    var typoDescent = override.descent || Math.round(scale *
+      (properties.descent || bbox[1]));
+    if (typoDescent > 0 && properties.descent > 0 && bbox[1] < 0) {
+      typoDescent = -typoDescent; // fixing incorrect descent
+    }
     var winAscent = override.yMax || typoAscent;
     var winDescent = -override.yMin || -typoDescent;
 
-    // if there is a units per em value but no other override
-    // then scale the calculated ascent
-    if (unitsPerEm != PDF_GLYPH_SPACE_UNITS &&
-        'undefined' == typeof(override.ascent)) {
-      // if the font units differ to the PDF glyph space units
-      // then scale up the values
-      typoAscent = Math.round(typoAscent * unitsPerEm / PDF_GLYPH_SPACE_UNITS);
-      typoDescent = Math.round(typoDescent * unitsPerEm /
-        PDF_GLYPH_SPACE_UNITS);
-      winAscent = typoAscent;
-      winDescent = -typoDescent;
-    }
-
     return '\x00\x03' + // version
            '\x02\x24' + // xAvgCharWidth
            '\x01\xF4' + // usWeightClass
            '\x00\x05' + // usWidthClass
            '\x00\x00' + // fstype (0 to let the font loads via font-face on IE)
            '\x02\x8A' + // ySubscriptXSize
            '\x02\xBB' + // ySubscriptYSize
            '\x00\x00' + // ySubscriptXOffset
@@ -19094,17 +19258,17 @@ var Font = (function FontClosure() {
       if (properties.subtype == 'CIDFontType0C') {
         var toFontChar = [];
         for (var i = 0; i < charstrings.length; ++i) {
           var charstring = charstrings[i];
           toFontChar[charstring.code] = charstring.unicode;
         }
         this.toFontChar = toFontChar;
       }
-      var unitsPerEm = properties.unitsPerEm || 1000; // defaulting to 1000
+      var unitsPerEm = 1 / (properties.fontMatrix || FONT_IDENTITY_MATRIX)[0];
 
       var fields = {
         // PostScript Font Program
         'CFF ': font.data,
 
         // OS/2 and Windows Specific metrics
         'OS/2': stringToArray(createOS2Table(properties, charstrings)),
 
@@ -19270,16 +19434,25 @@ var Font = (function FontClosure() {
             cid += unicode.s;
         } else if (unicode) {
           cidToUnicodeMap[cid] = unicode;
           unicodeToCIDMap[unicode] = cid;
           cid++;
         } else
           cid++;
       }
+
+      var cidEncoding = properties.cidEncoding;
+      if (cidEncoding && cidEncoding.indexOf('Identity-') !== 0) {
+        // input is already Unicode for non-Identity CMap encodings.
+        // However, Unicode-to-CID conversion is needed
+        // regardless of the CMap encoding. So we can't reset
+        // unicodeToCID.
+        this.cidToUnicode = [];
+      }
     },
 
     bindDOM: function Font_bindDOM() {
       if (!this.data)
         return null;
 
       var data = bytesToString(this.data);
       var fontName = this.loadedName;
@@ -19333,42 +19506,38 @@ var Font = (function FontClosure() {
         // setting it to unicode if negative or undefined
         if (!(charcode > 0))
           charcode = glyphUnicode;
         // trying to get width via charcode
         width = this.widths[charcode];
         if (width)
           break; // the non-zero width found
       }
-      width = (width || this.defaultWidth) * this.widthMultiplier;
+      width = width || this.defaultWidth;
       // Do not shadow the property here. See discussion:
       // https://github.com/mozilla/pdf.js/pull/2127#discussion_r1662280
       this._shadowWidth = width;
       return width;
     },
 
     charToGlyph: function Font_charToGlyph(charcode) {
       var fontCharCode, width, operatorList, disabled;
 
       var width = this.widths[charcode];
 
       switch (this.type) {
         case 'CIDFontType0':
           if (this.noUnicodeAdaptation) {
             width = this.widths[this.unicodeToCID[charcode] || charcode];
-            fontCharCode = mapPrivateUseChars(charcode);
-            break;
           }
           fontCharCode = this.toFontChar[charcode] || charcode;
           break;
         case 'CIDFontType2':
           if (this.noUnicodeAdaptation) {
             width = this.widths[this.unicodeToCID[charcode] || charcode];
-            fontCharCode = mapPrivateUseChars(charcode);
-            break;
           }
           fontCharCode = this.toFontChar[charcode] || charcode;
           break;
         case 'Type1':
           var glyphName = this.differences[charcode] || this.encoding[charcode];
           if (!isNum(width))
             width = this.widths[glyphName];
           if (this.noUnicodeAdaptation) {
@@ -19414,17 +19583,17 @@ var Font = (function FontClosure() {
           break;
       }
 
       var unicodeChars = !('toUnicode' in this) ? charcode :
         this.toUnicode[charcode] || charcode;
       if (typeof unicodeChars === 'number')
         unicodeChars = String.fromCharCode(unicodeChars);
 
-      width = (isNum(width) ? width : this.defaultWidth) * this.widthMultiplier;
+      width = isNum(width) ? width : this.defaultWidth;
       disabled = this.unicodeIsEnabled ?
         !this.unicodeIsEnabled[fontCharCode] : false;
 
       return {
         fontChar: String.fromCharCode(fontCharCode),
         unicode: unicodeChars,
         width: width,
         disabled: disabled,
@@ -19443,18 +19612,30 @@ var Font = (function FontClosure() {
           return glyphs;
       }
 
       // lazily create the translation cache
       if (!charsCache)
         charsCache = this.charsCache = Object.create(null);
 
       glyphs = [];
-
-      if (this.wideChars) {
+      var charsCacheKey = chars;
+
+      var converter;
+      var cidEncoding = this.cidEncoding;
+      if (cidEncoding) {
+        converter = CMapConverterL