Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Mon, 10 Sep 2012 12:16:38 -0700
changeset 106880 28bfdee5702699b0e9313837ff125b55a9f90189
parent 106879 8bd7ad214f7a6e9cd93ad99407f43ef97da97ef3 (current diff)
parent 106792 ebd88961b3c3810a37412ea147d468f0f865badc (diff)
child 106881 9425b626563a19d5ee8b3e37e13cc09e1bcd27f8
push id74
push usershu@rfrn.org
push dateTue, 18 Sep 2012 19:23:47 +0000
milestone18.0a1
Merge from mozilla-central.
b2g/installer/package-manifest.in
browser/base/content/browser.js
browser/base/content/browser.xul
browser/base/content/tabbrowser.xml
browser/themes/winstripe/browser.css
build/mobile/b2gemulator.py
build/mobile/devicemanager-utils.py
build/mobile/devicemanager.py
build/mobile/devicemanagerADB.py
build/mobile/devicemanagerSUT.py
build/mobile/droid.py
build/mobile/emulator.py
build/mobile/emulator_battery.py
caps/include/nsScriptSecurityManager.h
caps/src/nsScriptSecurityManager.cpp
chrome/src/nsChromeRegistry.cpp
configure.in
content/base/src/nsContentSink.cpp
content/base/src/nsContentSink.h
content/base/src/nsContentUtils.cpp
content/base/src/nsEventSource.cpp
content/base/src/nsFrameMessageManager.cpp
content/base/src/nsGkAtomList.h
content/base/src/nsPropertyTable.h
content/base/src/nsWebSocket.cpp
content/base/src/nsXMLHttpRequest.cpp
content/base/src/nsXMLHttpRequest.h
content/canvas/src/WebGLContext.cpp
content/html/content/src/nsHTMLOListElement.cpp
content/svg/content/src/nsSVGSVGElement.cpp
content/svg/content/src/nsSVGSVGElement.h
content/xbl/src/nsXBLDocumentInfo.cpp
content/xml/document/src/nsXMLDocument.cpp
content/xul/templates/src/nsContentSupportMap.h
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/base/nsDOMWindowUtils.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsJSEnvironment.cpp
dom/bindings/Codegen.py
dom/indexedDB/IndexedDatabaseManager.cpp
dom/ipc/ContentParent.cpp
dom/ipc/TabChild.cpp
dom/workers/File.cpp
dom/workers/WorkerPrivate.cpp
editor/libeditor/html/nsHTMLDataTransfer.cpp
gfx/gl/GLContext.h
gfx/gl/GLContextProviderOSMesa.cpp
gfx/layers/d3d10/LayerManagerD3D10.cpp
gfx/thebes/gfxPlatform.cpp
image/src/Decoder.cpp
js/ipc/ObjectWrapperParent.cpp
js/src/Makefile.in
js/src/gc/Root.h
js/src/ion/CodeGenerator.cpp
js/src/js.msg
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsatom.h
js/src/jsdbgapi.cpp
js/src/jsexn.cpp
js/src/jsfriendapi.h
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsproxy.cpp
js/src/jsproxy.h
js/src/jstypedarray.cpp
js/src/jstypedarray.h
js/src/jswrapper.cpp
js/src/jswrapper.h
js/src/jsxml.cpp
js/src/methodjit/StubCalls.cpp
js/src/shell/js.cpp
js/src/vm/Debugger.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/ScopeObject.cpp
js/src/vm/Stack.cpp
js/xpconnect/loader/mozJSComponentLoader.cpp
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/XPCConvert.cpp
js/xpconnect/src/XPCQuickStubs.cpp
js/xpconnect/src/XPCQuickStubs.h
js/xpconnect/src/XPCWrappedJSClass.cpp
js/xpconnect/src/XPCWrappedNativeJSOps.cpp
js/xpconnect/src/XPCWrapper.cpp
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/wrappers/AccessCheck.cpp
js/xpconnect/wrappers/AccessCheck.h
js/xpconnect/wrappers/FilteringWrapper.cpp
js/xpconnect/wrappers/WrapperFactory.cpp
js/xpconnect/wrappers/WrapperFactory.h
js/xpconnect/wrappers/XrayWrapper.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsChangeHint.h
layout/base/nsFrameManager.cpp
layout/base/nsFrameManager.h
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockFrame.h
layout/generic/nsBulletFrame.cpp
layout/generic/nsBulletFrame.h
layout/generic/nsFrame.cpp
layout/generic/nsInlineFrame.cpp
layout/generic/nsTextFrameThebes.cpp
layout/mathml/nsMathMLmtableFrame.cpp
layout/reftests/bugs/reftest.list
layout/reftests/canvas/reftest.list
layout/reftests/svg/as-image/reftest.list
layout/reftests/svg/reftest.list
layout/reftests/svg/smil/reftest.list
layout/reftests/svg/smil/transform/reftest.list
layout/style/nsCSSScanner.cpp
layout/style/nsFontFaceLoader.cpp
layout/style/nsStyleContext.cpp
layout/style/nsStyleContext.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/ua.css
layout/svg/base/src/nsSVGEffects.cpp
layout/svg/base/src/nsSVGOuterSVGFrame.cpp
layout/tables/BasicTableLayoutStrategy.cpp
layout/tables/SpanningCellSorter.h
layout/tables/nsTableFrame.cpp
layout/tools/reftest/reftest.js
mobile/android/base/GeckoApp.java
mobile/android/chrome/content/browser.js
modules/libpref/src/init/all.js
parser/htmlparser/src/nsExpatDriver.cpp
testing/mozbase/manifestdestiny/tests/test.py
testing/mozbase/manifestdestiny/tests/test_expressionparser.txt
testing/mozbase/manifestdestiny/tests/test_manifestparser.txt
testing/mozbase/manifestdestiny/tests/test_testmanifest.txt
uriloader/prefetch/nsPrefetchService.cpp
widget/nsIWidget.h
widget/windows/nsClipboard.cpp
widget/windows/nsDataObj.cpp
widget/windows/nsImageClipboard.cpp
xpcom/base/nsCycleCollector.cpp
xpcom/build/nsXPComInit.cpp
xpcom/ds/nsHashtable.h
xpcom/glue/nsCycleCollectionParticipant.h
xpcom/glue/nsISupportsImpl.h
xpcom/glue/nsTHashtable.h
xpcom/io/SpecialSystemDirectory.cpp
xpcom/io/nsDirectoryService.cpp
xpcom/io/nsDirectoryService.h
xpcom/string/public/nsXPCOMStrings.h
--- a/b2g/Makefile.in
+++ b/b2g/Makefile.in
@@ -10,12 +10,16 @@ VPATH      = @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 DIRS = chrome components locales
 
 ifeq ($(OS_ARCH),WINNT)
 DIRS += $(DEPTH)/xulrunner/tools/redit
 endif
 
+ifneq ($(GAIADIR),)
+DIRS += gaia
+endif
+
 DIRS += app
 
 include $(topsrcdir)/config/rules.mk
 include $(topsrcdir)/testing/testsuite-targets.mk
--- a/b2g/app/Makefile.in
+++ b/b2g/app/Makefile.in
@@ -11,17 +11,21 @@ include $(DEPTH)/config/autoconf.mk
 
 PREF_JS_EXPORTS = $(srcdir)/b2g.js
 
 ifdef ENABLE_MARIONETTE
 DEFINES += -DENABLE_MARIONETTE=1
 endif
 
 ifndef LIBXUL_SDK
+ifneq ($(GAIADIR),)
+PROGRAM=$(MOZ_APP_NAME)-bin$(BIN_SUFFIX)
+else
 PROGRAM=$(MOZ_APP_NAME)$(BIN_SUFFIX)
+endif
 
 CPPSRCS = nsBrowserApp.cpp
 
 LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
 LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/base
 LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/build
 LOCAL_INCLUDES += -I$(DEPTH)/build
 
--- a/b2g/app/macbuild/Contents/Info.plist.in
+++ b/b2g/app/macbuild/Contents/Info.plist.in
@@ -1,138 +1,32 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
 	<key>CFBundleDevelopmentRegion</key>
 	<string>English</string>
-	<key>CFBundleDocumentTypes</key>
-	<array>
-		<dict>
-			<key>CFBundleTypeExtensions</key>
-			<array>
-				<string>html</string>
-				<string>htm</string>
-				<string>shtml</string>
-				<string>xht</string>
-				<string>xhtml</string>
-			</array>
-			<key>CFBundleTypeIconFile</key>
-			<string>document.icns</string>
-			<key>CFBundleTypeName</key>
-			<string>HTML Document</string>
-			<key>CFBundleTypeOSTypes</key>
-			<array>
-				<string>HTML</string>
-			</array>
-			<key>CFBundleTypeRole</key>
-			<string>Viewer</string>
-		</dict>
-		<dict>
-			<key>CFBundleTypeExtensions</key>
-			<array>
-			  <string>text</string>
-				<string>txt</string>
-				<string>js</string>
-				<string>log</string>
-				<string>css</string>
-				<string>xul</string>
-				<string>rdf</string>
-			</array>
-			<key>CFBundleTypeIconFile</key>
-			<string>document.icns</string>
-			<key>CFBundleTypeName</key>
-			<string>Text Document</string>
-			<key>CFBundleTypeOSTypes</key>
-			<array>
-				<string>TEXT</string>
-				<string>utxt</string>
-			</array>
-			<key>CFBundleTypeRole</key>
-			<string>Viewer</string>
-		</dict>
-		<dict>
-			<key>CFBundleTypeExtensions</key>
-			<array>
-				<string>jpeg</string>
-				<string>jpg</string>
-				<string>png</string>
-				<string>gif</string>
-			</array>
-			<key>CFBundleTypeIconFile</key>
-			<string>fileBookmark.icns</string>
-			<key>CFBundleTypeName</key>
-			<string>document.icns</string>
-			<key>CFBundleTypeOSTypes</key>
-			<array>
-				<string>GIFf</string>
-				<string>JPEG</string>
-				<string>PNGf</string>
-			</array>
-			<key>CFBundleTypeRole</key>
-			<string>Viewer</string>
-		</dict>
-	</array>
 	<key>CFBundleExecutable</key>
-	<string>%MOZ_APP_NAME%</string>
+	<string>b2g</string>
 	<key>CFBundleGetInfoString</key>
 	<string>%APP_NAME% %APP_VERSION%</string>
 	<key>CFBundleIconFile</key>
 	<string>%MOZ_APP_NAME%</string>
 	<key>CFBundleIdentifier</key>
 	<string>org.mozilla.b2g</string>
 	<key>CFBundleInfoDictionaryVersion</key>
 	<string>%MOZ_APP_VERSION%</string>
 	<key>CFBundleName</key>
 	<string>%APP_NAME%</string>
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
 	<string>%APP_VERSION%</string>
 	<key>CFBundleSignature</key>
 	<string>MOZB</string>
-	<key>CFBundleURLTypes</key>
-	<array>
-		<dict>
-			<key>CFBundleURLIconFile</key>
-			<string>document.icns</string>
-			<key>CFBundleURLName</key>
-			<string>http URL</string>
-			<key>CFBundleURLSchemes</key>
-			<array>
-				<string>http</string>
-			</array>
-		</dict>
-		<dict>
-			<key>CFBundleURLIconFile</key>
-			<string>document.icns</string>
-			<key>CFBundleURLName</key>
-			<string>https URL</string>
-			<key>CFBundleURLSchemes</key>
-			<array>
-				<string>https</string>
-			</array>
-		</dict>
-		<dict>
-			<key>CFBundleURLName</key>
-			<string>ftp URL</string>
-			<key>CFBundleURLSchemes</key>
-			<array>
-				<string>ftp</string>
-			</array>
-		</dict>
-		<dict>
-			<key>CFBundleURLName</key>
-			<string>file URL</string>
-			<key>CFBundleURLSchemes</key>
-			<array>
-				<string>file</string>
-			</array>
-		</dict>
-	</array>
 	<key>CFBundleVersion</key>
 	<string>%APP_VERSION%</string>
 	<key>NSAppleScriptEnabled</key>
 	<true/>
 	<key>CGDisableCoalescedUpdates</key>
 	<true/>
 </dict>
 </plist>
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -347,18 +347,17 @@ var shell = {
                    ObjectWrapper.wrap(details, getContentWindow()));
   },
 
   sendSystemMessage: function shell_sendSystemMessage(msg) {
     let origin = Services.io.newURI(msg.manifest, null, null).prePath;
     this.sendChromeEvent({
       type: 'open-app',
       url: msg.uri,
-      origin: origin,
-      manifest: msg.manifest,
+      manifestURL: msg.manifest,
       isActivity: (msg.type == 'activity'),
       target: msg.target
     });
   },
 
   receiveMessage: function shell_receiveMessage(message) {
     if (message.name != 'content-handler') {
       return;
@@ -596,17 +595,17 @@ var WebappsHelper = {
         DOMApplicationRegistry.getManifestFor(json.origin, function(aManifest) {
           if (!aManifest)
             return;
 
           let manifest = new DOMApplicationManifest(aManifest, json.origin);
           shell.sendChromeEvent({
             "type": "webapps-launch",
             "url": manifest.fullLaunchPath(json.startPoint),
-            "origin": json.origin
+            "manifestURL": json.manifestURL
           });
         });
         break;
       case "webapps-ask-install":
         let id = this.registerInstaller(json);
         shell.sendChromeEvent({
           type: "webapps-ask-install",
           id: id,
new file mode 100644
--- /dev/null
+++ b/b2g/gaia/Makefile.in
@@ -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/.
+
+DEPTH     = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+GAIA_PATH := gaia/profile
+
+
+# We don't have a wrapper script on Windows yet
+ifneq ($(OS_ARCH),WINNT)
+PROGRAM = $(MOZ_APP_NAME)$(BIN_SUFFIX)
+CSRCS = run-b2g.c
+
+DEFINES += \
+  -DB2G_NAME=\"$(MOZ_APP_NAME)-bin$(BIN_SUFFIX)\" \
+  -DGAIA_PATH=\"$(GAIA_PATH)\" \
+  $(NULL)
+
+# This is needed to avoid making run-b2g depend on mozglue
+WRAP_LDFLAGS :=
+endif
+
+GENERATED_DIRS += $(DIST)/bin/$(GAIA_PATH)
+
+include $(topsrcdir)/config/rules.mk
+
+
+libs::
+	# Below here is how Gaia gets built
+	# The Gaia build system freaks out when N > 1 for -jN
+	$(MAKE) -j1 -C $(GAIADIR) clean
+	$(MAKE) -j1 -C $(GAIADIR) profile GAIA_DOMAIN=desktop-builds.$(MOZ_APP_NAME).mozilla.org
+	(cd $(GAIADIR)/profile && tar $(TAR_CREATE_FLAGS) - .) | (cd $(abspath $(DIST))/bin/$(GAIA_PATH) && tar -xf -)
+
+
+
new file mode 100644
--- /dev/null
+++ b/b2g/gaia/run-b2g.c
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <libgen.h>
+
+#ifndef B2G_NAME
+#define B2G_NAME "b2g-bin"
+#endif
+#ifndef GAIA_PATH
+#define GAIA_PATH "gaia/profile"
+#endif
+#define NOMEM "Could not allocate enough memory"
+
+void error(char* msg){
+    fprintf(stderr, "ERROR: %s\n", msg);
+}
+
+int main(int argc, char* argv[], char* envp[]){
+    char* cwd = NULL;
+    char* full_path = NULL;
+    char* full_profile_path = NULL;
+    printf("Starting %s\n", B2G_NAME);
+    cwd = realpath(dirname(argv[0]), NULL);
+    full_path = (char*) malloc(strlen(cwd) + strlen(B2G_NAME) + 2);
+    if (!full_path) {
+        error(NOMEM);
+        return -2;
+    }
+    full_profile_path = (char*) malloc(strlen(cwd) + strlen(GAIA_PATH) + 2);
+    if (!full_profile_path) {
+        error(NOMEM);
+        return -2;
+    }
+    sprintf(full_path, "%s/%s", cwd, B2G_NAME);
+    sprintf(full_profile_path, "%s/%s", cwd, GAIA_PATH);
+    free(cwd);
+    printf("Running: %s -profile %s\n", full_path, full_profile_path);
+    fflush(stdout);
+    fflush(stderr);
+    execle(full_path, full_path, "-profile", full_profile_path, NULL, envp);
+    error("unable to start");
+    perror(argv[0]);
+    free(full_path);
+    free(full_profile_path);
+    return -1;
+}
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -696,8 +696,14 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
 @BINPATH@/components/ActivitiesGlue.js
 @BINPATH@/components/ProcessGlobal.js
 @BINPATH@/components/ContentHandler.js
 @BINPATH@/components/PaymentGlue.js
 
 #ifdef XP_MACOSX
 @BINPATH@/@DLL_PREFIX@plugin_child_interpose@DLL_SUFFIX@
 #endif
+
+#ifdef PACKAGE_GAIA
+[gaia]
+@BINPATH@/gaia/*
+@BINPATH@/b2g-bin@BIN_SUFFIX@
+#endif
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -84,17 +84,17 @@
     <command id="cmd_fullZoomEnlarge" oncommand="FullZoom.enlarge()"/>
     <command id="cmd_fullZoomReset"   oncommand="FullZoom.reset()"/>
     <command id="cmd_fullZoomToggle"  oncommand="ZoomManager.toggleZoom();"/>
     <command id="Browser:OpenLocation" oncommand="openLocation();"/>
 
     <command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/>
     <command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
     <command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();" disabled="true" hidden="true"/>
-    <command id="Tools:DevToolbarFocus" oncommand="DeveloperToolbar.focus();" disabled="true"/>
+    <command id="Tools:DevToolbarFocus" oncommand="DeveloperToolbar.focusToggle();" disabled="true"/>
     <command id="Tools:WebConsole" oncommand="HUDConsoleUI.toggleHUD();"/>
     <command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();"/>
     <command id="Tools:Debugger" oncommand="DebuggerUI.toggleDebugger();" disabled="true" hidden="true"/>
     <command id="Tools:RemoteDebugger" oncommand="DebuggerUI.toggleRemoteDebugger();" disabled="true" hidden="true"/>
     <command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();" disabled="true" hidden="true"/>
     <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true" hidden="true"/>
     <command id="Tools:StyleEditor" oncommand="StyleEditor.toggle();" disabled="true" hidden="true"/>
     <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();" disabled="true" hidden="true"/>
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -215,16 +215,17 @@ let SocialFlyout = {
 
   _createFrame: function() {
     let panel = this.panel;
     if (!Social.provider || 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);
   },
 
   unload: function() {
     let panel = this.panel;
     if (!panel.firstChild)
@@ -531,16 +532,17 @@ var SocialToolbar = {
     for each(let name in iconNames) {
       let icon = provider.ambientNotificationIcons[name];
 
       let notificationFrameId = "social-status-" + icon.name;
       let notificationFrame = document.getElementById(notificationFrameId);
       if (!notificationFrame) {
         notificationFrame = document.createElement("iframe");
         notificationFrame.setAttribute("type", "content");
+        notificationFrame.setAttribute("class", "social-panel-frame");
         notificationFrame.setAttribute("id", notificationFrameId);
         notificationFrame.setAttribute("mozbrowser", "true");
         notificationFrames.appendChild(notificationFrame);
       }
       notificationFrame.setAttribute("origin", provider.origin);
       if (notificationFrame.getAttribute("src") != icon.contentPanel)
         notificationFrame.setAttribute("src", icon.contentPanel);
 
--- a/browser/base/content/browser-tabPreviews.js
+++ b/browser/base/content/browser-tabPreviews.js
@@ -653,17 +653,17 @@ var allTabs = {
       var matches = 0;
       if (filter.length && !tab.hidden) {
         let tabstring = tab.linkedBrowser.currentURI.spec;
         try {
           tabstring = decodeURI(tabstring);
         } catch (e) {}
         tabstring = tab.label + " " + tab.label.toLocaleLowerCase() + " " + tabstring;
         for (let i = 0; i < filter.length; i++)
-          matches += tabstring.indexOf(filter[i]) > -1;
+          matches += tabstring.contains(filter[i]);
       }
       if (matches < filter.length || tab.hidden) {
         preview.hidden = true;
       }
       else {
         this._visible++;
         this._updatePreview(preview);
         preview.hidden = false;
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1044,17 +1044,17 @@ var gBrowserInit = {
           .QueryInterface(Ci.nsIInterfaceRequestor)
           .getInterface(Ci.nsIXULWindow)
           .XULBrowserWindow = window.XULBrowserWindow;
     window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow =
       new nsBrowserAccess();
 
     // set default character set if provided
     if ("arguments" in window && window.arguments.length > 1 && window.arguments[1]) {
-      if (window.arguments[1].indexOf("charset=") != -1) {
+      if (window.arguments[1].startsWith("charset=")) {
         var arrayArgComponents = window.arguments[1].split("=");
         if (arrayArgComponents) {
           //we should "inherit" the charset menu setting in a new window
           getMarkupDocumentViewer().defaultCharacterSet = arrayArgComponents[1];
         }
       }
     }
 
@@ -2753,17 +2753,17 @@ function getMeOutOfHere() {
   // Get the start page from the *default* pref branch, not the user's
   var prefs = Cc["@mozilla.org/preferences-service;1"]
              .getService(Ci.nsIPrefService).getDefaultBranch(null);
   var url = BROWSER_NEW_TAB_URL;
   try {
     url = prefs.getComplexValue("browser.startup.homepage",
                                 Ci.nsIPrefLocalizedString).data;
     // If url is a pipe-delimited set of pages, just take the first one.
-    if (url.indexOf("|") != -1)
+    if (url.contains("|"))
       url = url.split("|")[0];
   } catch(e) {
     Components.utils.reportError("Couldn't get homepage pref: " + e);
   }
   content.location = url;
 }
 
 function BrowserFullScreen()
@@ -3462,17 +3462,17 @@ function FillHistoryMenu(aParent) {
 
     aParent.appendChild(item);
   }
   return true;
 }
 
 function addToUrlbarHistory(aUrlToAdd) {
   if (aUrlToAdd &&
-      aUrlToAdd.indexOf(" ") == -1 &&
+      !aUrlToAdd.contains(" ") &&
       !/[\x00-\x1F]/.test(aUrlToAdd))
     PlacesUIUtils.markPageAsTyped(aUrlToAdd);
 }
 
 function toJavaScriptConsole()
 {
   toOpenWindowByType("global:console", "chrome://global/content/console.xul");
 }
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -263,20 +263,22 @@
                 class="editSharePopupBottomButton"
                 label="&social.sharePopup.undo.label;"
                 accesskey="&social.sharePopup.undo.accesskey;"
                 command="Social:UnsharePage"/>
 #endif
       </hbox>
     </panel>
 
-    <panel id="social-notification-panel" type="arrow" hidden="true" noautofocus="true">
+    <panel id="social-notification-panel" class="social-panel"
+           type="arrow" hidden="true" noautofocus="true">
       <box id="social-notification-box" flex="1"></box>
     </panel>
     <panel id="social-flyout-panel"
+           class="social-panel"
            onpopupshown="SocialFlyout.onShown()"
            onpopuphidden="SocialFlyout.onHidden()"
            side="right"
            type="arrow"
            hidden="true"
            noautofocus="true"
            position="topcenter topright"/>
 
--- a/browser/base/content/pageinfo/pageInfo.xul
+++ b/browser/base/content/pageinfo/pageInfo.xul
@@ -59,17 +59,17 @@
     <command id="cmd_cookieToggle"  oncommand="onRadioClick('cookie');"/>
     <command id="cmd_installToggle" oncommand="onRadioClick('install');"/>
     <command id="cmd_fullscreenToggle" oncommand="onRadioClick('fullscreen');"/>
     <command id="cmd_geoToggle"     oncommand="onRadioClick('geo');"/>
     <command id="cmd_indexedDBToggle" oncommand="onRadioClick('indexedDB');"/>
     <command id="cmd_pluginsToggle" oncommand="onRadioClick('plugins');"/>
   </commandset>
 
-  <keyset>
+  <keyset id="pageInfoKeySet">
     <key key="&closeWindow.key;" modifiers="accel" command="cmd_close"/>
     <key keycode="VK_ESCAPE"                       command="cmd_close"/>
 #ifdef XP_MACOSX
     <key key="."                 modifiers="meta"  command="cmd_close"/>
 #else
     <key keycode="VK_F1"                           command="cmd_help"/>
 #endif
     <key key="&copy.key;"        modifiers="accel" command="cmd_copy"/>
@@ -97,60 +97,60 @@
       <!-- Others added by overlay -->
     </radiogroup>
   </windowdragbox>
 
   <deck id="mainDeck" flex="1">
     <!-- General page information -->
     <vbox id="generalPanel">
       <textbox class="header" readonly="true" id="titletext"/>
-      <grid>
+      <grid id="generalGrid">
         <columns>
           <column/>
           <column class="gridSeparator"/>
           <column flex="1"/>
         </columns>
-        <rows>
-          <row>
+        <rows id="generalRows">
+          <row id="generalURLRow">
             <label control="urltext" value="&generalURL;"/>
             <separator/>
             <textbox readonly="true" id="urltext"/>
           </row>
-          <row>
+          <row id="generalSeparatorRow1">
             <separator class="thin"/>
           </row>
-          <row>
+          <row id="generalTypeRow">
             <label control="typetext" value="&generalType;"/>
             <separator/>
             <textbox readonly="true" id="typetext"/>
           </row>
-          <row>
+          <row id="generalModeRow">
             <label control="modetext" value="&generalMode;"/>
             <separator/>
             <textbox readonly="true" crop="end" id="modetext"/>
           </row>
-          <row>
+          <row id="generalEncodingRow">
             <label control="encodingtext" value="&generalEncoding;"/>
             <separator/>
             <textbox readonly="true" id="encodingtext"/>
           </row>
-          <row>
+          <row id="generalSizeRow">
             <label control="sizetext" value="&generalSize;"/>
             <separator/>
             <textbox readonly="true" id="sizetext"/>
           </row>
-          <row>
+          <row id="generalReferrerRow">
             <label control="refertext" value="&generalReferrer;"/>
             <separator/>
             <textbox readonly="true" id="refertext"/>
           </row>
-          <row>
+          <row id="generalSeparatorRow2">
             <separator class="thin"/>
           </row>
-          <row>
+          <row id="generalModifiedRow">
             <label control="modifiedtext" value="&generalModified;"/>
             <separator/>
             <textbox readonly="true" id="modifiedtext"/>
           </row>
         </rows>
       </grid>
       <separator class="thin"/>
       <groupbox id="metaTags" flex="1" class="collapsable treebox">
@@ -160,24 +160,24 @@
             <treecol id="meta-name"    label="&generalMetaName;"
                      persist="width" flex="1"
                      onclick="gMetaView.onPageMediaSort('meta-name');"/>
             <splitter class="tree-splitter"/>
             <treecol id="meta-content" label="&generalMetaContent;"
                      persist="width" flex="4"
                      onclick="gMetaView.onPageMediaSort('meta-content');"/>
           </treecols>
-          <treechildren flex="1"/>
+          <treechildren id="metatreechildren" flex="1"/>
         </tree>        
       </groupbox>
       <groupbox id="securityBox">
         <caption id="securityBoxCaption" label="&securityHeader;"/>
         <description id="general-security-identity" class="header"/>
         <description id="general-security-privacy"  class="header"/>
-        <hbox align="right">
+        <hbox id="securityDetailsButtonBox" align="right">
           <button id="security-view-details" label="&generalSecurityDetails;"
                   accesskey="&generalSecurityDetails.accesskey;"
                   oncommand="onClickMore();"/>
         </hbox>
       </groupbox>
     </vbox>
 
     <!-- Media information -->
@@ -200,199 +200,200 @@
           <treecol sortSeparators="true" hidden="true" persist="hidden width" flex="4"
                         width="4"  id="image-alt"    label="&mediaAltHeader;"
                         onclick="gImageView.onPageMediaSort('image-alt');"/>
           <splitter class="tree-splitter"/>
           <treecol sortSeparators="true" hidden="true" persist="hidden width" flex="1"
                         width="1"  id="image-count"    label="&mediaCount;"
                         onclick="gImageView.onPageMediaSort('image-count');"/>
         </treecols>
-        <treechildren flex="1"/>
+        <treechildren id="imagetreechildren" flex="1"/>
       </tree>
       <splitter orient="vertical" id="mediaSplitter"/>
       <vbox flex="1" id="mediaPreviewBox" collapsed="true">
         <grid id="mediaGrid">
           <columns>
             <column id="mediaLabelColumn"/>
             <column class="gridSeparator"/>
             <column flex="1"/>
           </columns>
-          <rows>
-            <row>
+          <rows id="mediaRows">
+            <row id="mediaLocationRow">
               <label control="imageurltext" value="&mediaLocation;"/>
               <separator/>
               <textbox readonly="true" id="imageurltext"/>
             </row>
-            <row>
+            <row id="mediaTypeRow">
               <label control="imagetypetext" value="&generalType;"/>
               <separator/>
               <textbox readonly="true" id="imagetypetext"/>
             </row>
-            <row>
+            <row id="mediaSizeRow">
               <label control="imagesizetext" value="&generalSize;"/>
               <separator/>
               <textbox readonly="true" id="imagesizetext"/>
             </row>
-            <row>
+            <row id="mediaDimensionRow">
               <label control="imagedimensiontext" value="&mediaDimension;"/>
               <separator/>
               <textbox readonly="true" id="imagedimensiontext"/>
             </row>
-            <row>
+            <row id="mediaTextRow">
               <label control="imagetext" value="&mediaText;"/>
               <separator/>
               <textbox readonly="true" id="imagetext"/>
             </row>
-            <row>
+            <row id="mediaLongdescRow">
               <label control="imagelongdesctext" value="&mediaLongdesc;"/>
               <separator/>
               <textbox readonly="true" id="imagelongdesctext"/>
             </row>
           </rows>
         </grid>
-        <hbox align="end">
-          <vbox>
+        <hbox id="imageSaveBox" align="end">
+          <vbox id="blockImageBox">
             <checkbox id="blockImage" hidden="true" oncommand="onBlockImage()"
                       accesskey="&mediaBlockImage.accesskey;"/>
             <label control="thepreviewimage" value="&mediaPreview;" class="header"/>
           </vbox>
-          <spacer flex="1"/>
+          <spacer id="imageSaveBoxSpacer" flex="1"/>
           <button label="&mediaSaveAs;" accesskey="&mediaSaveAs.accesskey;"
                   icon="save" id="imagesaveasbutton"
                   oncommand="saveMedia();"/>
         </hbox>
-        <vbox class="inset iframe" flex="1" pack="center">
+        <vbox id="imagecontainerbox" class="inset iframe" flex="1" pack="center">
           <hbox id="theimagecontainer" pack="center">
             <image id="thepreviewimage"/>
           </hbox>
           <hbox id="brokenimagecontainer" pack="center" collapsed="true">
             <image id="brokenimage" src="resource://gre-resources/broken-image.png"/>
           </hbox>
         </vbox>
       </vbox>
       <hbox id="mediaSaveBox" collapsed="true">
-        <spacer flex="1"/>
+        <spacer id="mediaSaveBoxSpacer" flex="1"/>
         <button label="&mediaSaveAs;" accesskey="&mediaSaveAs2.accesskey;"
-                icon="save" oncommand="saveMedia();"/>
+                icon="save" id="mediasaveasbutton"
+                oncommand="saveMedia();"/>
       </hbox>
     </vbox>
 
     <!-- Feeds -->
     <vbox id="feedPanel">
       <richlistbox id="feedListbox" flex="1"/>
     </vbox>
 
     <!-- Permissions -->
     <vbox id="permPanel">
-      <hbox>
+      <hbox id="permHostBox">
         <label value="&permissionsFor;" control="hostText" />
         <textbox id="hostText" class="header" readonly="true"
                  crop="end" flex="1"/>
       </hbox>
 
       <vbox id="permList" flex="1">
-        <vbox class="permission">
+        <vbox class="permission" id="permImageRow">
           <label class="permissionLabel" id="permImageLabel"
                  value="&permImage;" control="imageRadioGroup"/>
-          <hbox role="group" aria-labelledby="permImageLabel">
+          <hbox id="permImageBox" role="group" aria-labelledby="permImageLabel">
             <checkbox id="imageDef" command="cmd_imageDef" label="&permUseDefault;"/>
             <spacer flex="1"/>
             <radiogroup id="imageRadioGroup" orient="horizontal">
               <radio id="image#1" command="cmd_imageToggle" label="&permAllow;"/>
               <radio id="image#2" command="cmd_imageToggle" label="&permBlock;"/>
             </radiogroup>
           </hbox>
         </vbox>
-        <vbox class="permission">
+        <vbox class="permission" id="permPopupRow">
           <label class="permissionLabel" id="permPopupLabel"
                  value="&permPopup;" control="popupRadioGroup"/>
-          <hbox role="group" aria-labelledby="permPopupLabel">
+          <hbox id="permPopupBox" role="group" aria-labelledby="permPopupLabel">
             <checkbox id="popupDef" command="cmd_popupDef" label="&permUseDefault;"/>
             <spacer flex="1"/>
             <radiogroup id="popupRadioGroup" orient="horizontal">
               <radio id="popup#1" command="cmd_popupToggle" label="&permAllow;"/>
               <radio id="popup#2" command="cmd_popupToggle" label="&permBlock;"/>
             </radiogroup>
           </hbox>
         </vbox>
-        <vbox class="permission">
+        <vbox class="permission" id="permCookieRow">
           <label class="permissionLabel" id="permCookieLabel"
                  value="&permCookie;" control="cookieRadioGroup"/>
-          <hbox role="group" aria-labelledby="permCookieLabel">
+          <hbox id="permCookieBox" role="group" aria-labelledby="permCookieLabel">
             <checkbox id="cookieDef" command="cmd_cookieDef" label="&permUseDefault;"/>
             <spacer flex="1"/>
             <radiogroup id="cookieRadioGroup" orient="horizontal">
               <radio id="cookie#1" command="cmd_cookieToggle" label="&permAllow;"/>
               <radio id="cookie#8" command="cmd_cookieToggle" label="&permAllowSession;"/>
               <radio id="cookie#2" command="cmd_cookieToggle" label="&permBlock;"/>
             </radiogroup>
           </hbox>
         </vbox>
-        <vbox class="permission">
+        <vbox class="permission" id="permInstallRow">
           <label class="permissionLabel" id="permInstallLabel"
                  value="&permInstall;" control="installRadioGroup"/>
-          <hbox role="group" aria-labelledby="permInstallLabel">
+          <hbox id="permInstallBox" role="group" aria-labelledby="permInstallLabel">
             <checkbox id="installDef" command="cmd_installDef" label="&permUseDefault;"/>
             <spacer flex="1"/>
             <radiogroup id="installRadioGroup" orient="horizontal">
               <radio id="install#1" command="cmd_installToggle" label="&permAllow;"/>
               <radio id="install#2" command="cmd_installToggle" label="&permBlock;"/>
             </radiogroup>
           </hbox>
         </vbox>
-        <vbox class="permission">
+        <vbox class="permission" id="permGeoRow" >
           <label class="permissionLabel" id="permGeoLabel"
                  value="&permGeo;" control="geoRadioGroup"/>
-          <hbox role="group" aria-labelledby="permGeoLabel">
+          <hbox id="permGeoBox" role="group" aria-labelledby="permGeoLabel">
             <checkbox id="geoDef" command="cmd_geoDef" label="&permAskAlways;"/>
             <spacer flex="1"/>
             <radiogroup id="geoRadioGroup" orient="horizontal">
               <radio id="geo#1" command="cmd_geoToggle" label="&permAllow;"/>
               <radio id="geo#2" command="cmd_geoToggle" label="&permBlock;"/>
             </radiogroup>
           </hbox>
         </vbox>
-        <vbox class="permission">
+        <vbox class="permission" id="permIndexedDBRow">
           <label class="permissionLabel" id="permIndexedDBLabel"
                  value="&permIndexedDB;" control="indexedDBRadioGroup"/>
-          <hbox role="group" aria-labelledby="permIndexedDBLabel">
+          <hbox id="permIndexedDBBox" role="group" aria-labelledby="permIndexedDBLabel">
             <checkbox id="indexedDBDef" command="cmd_indexedDBDef" label="&permUseDefault;"/>
             <spacer flex="1"/>
             <radiogroup id="indexedDBRadioGroup" orient="horizontal">
               <!-- Ask and Allow are purposefully reversed here! -->
               <radio id="indexedDB#1" command="cmd_indexedDBToggle" label="&permAskAlways;"/>
               <radio id="indexedDB#0" command="cmd_indexedDBToggle" label="&permAllow;"/>
               <radio id="indexedDB#2" command="cmd_indexedDBToggle" label="&permBlock;"/>
             </radiogroup>
           </hbox>
-          <hbox>
+          <hbox id="permIndexedDBBox2">
             <spacer flex="1"/>
-            <vbox pack="center">
+            <vbox id="permIndexedDBStatusBox" pack="center">
               <label id="indexedDBStatus" control="indexedDBClear" hidden="true"/>
             </vbox>
             <button id="indexedDBClear" label="&permClearStorage;" hidden="true"
                     accesskey="&permClearStorage.accesskey;" onclick="onIndexedDBClear();"/>
           </hbox>
         </vbox>
         <vbox class="permission" id="permPluginsRow">
           <label class="permissionLabel" id="permPluginsLabel"
                  value="&permPlugins;" control="pluginsRadioGroup"/>
-          <hbox role="group" aria-labelledby="permPluginsLabel">
+          <hbox id="permPluginsBox" role="group" aria-labelledby="permPluginsLabel">
             <checkbox id="pluginsDef" command="cmd_pluginsDef" label="&permAskAlways;"/>
             <spacer flex="1"/>
             <radiogroup id="pluginsRadioGroup" orient="horizontal">
               <radio id="plugins#1" command="cmd_pluginsToggle" label="&permAllow;"/>
               <radio id="plugins#2" command="cmd_pluginsToggle" label="&permBlock;"/>
             </radiogroup>
           </hbox>
         </vbox>
-        <vbox class="permission">
+        <vbox class="permission" id="permFullscreenRow">
           <label class="permissionLabel" id="permFullscreenLabel"
                  value="&permFullscreen;" control="fullscreenRadioGroup"/>
-          <hbox role="group" aria-labelledby="permFullscreenLabel">
+          <hbox id="permFullscreenBox" role="group" aria-labelledby="permFullscreenLabel">
             <checkbox id="fullscreenDef" command="cmd_fullscreenDef" label="&permUseDefault;"/>
             <spacer flex="1"/>
             <radiogroup id="fullscreenRadioGroup" orient="horizontal">
               <radio id="fullscreen#0" command="cmd_fullscreenToggle" label="&permAskAlways;"/>
               <radio id="fullscreen#1" command="cmd_fullscreenToggle" label="&permAllow;"/>
               <radio id="fullscreen#2" command="cmd_fullscreenToggle" label="&permBlock;"/>
             </radiogroup>
           </hbox>
@@ -400,95 +401,102 @@
       </vbox>
     </vbox>
 
     <!-- Security & Privacy -->
     <vbox id="securityPanel">
       <!-- Identity Section -->
       <groupbox id="security-identity-groupbox" flex="1">
         <caption id="security-identity" label="&securityView.identity.header;"/>
-        <grid flex="1">
+        <grid id="security-identity-grid" flex="1">
           <columns>
             <column/>
             <column flex="1"/>
           </columns>
-          <rows>
-            <row><!-- Domain -->
+          <rows id="security-identity-rows">
+            <!-- Domain -->
+            <row id="security-identity-domain-row">
               <label id="security-identity-domain-label"
                      class="fieldLabel"
                      value="&securityView.identity.domain;"
                      control="security-identity-domain-value"/>
               <textbox id="security-identity-domain-value"
                        class="fieldValue" readonly="true"/>
             </row>
-            <row><!-- Owner -->
+            <!-- Owner -->
+            <row id="security-identity-owner-row">
               <label id="security-identity-owner-label"
                      class="fieldLabel"
                      value="&securityView.identity.owner;"
                      control="security-identity-owner-value"/>
               <textbox id="security-identity-owner-value"
                        class="fieldValue" readonly="true"/>
             </row>
-            <row><!-- Verifier -->
+            <!-- Verifier -->
+            <row id="security-identity-verifier-row">
               <label id="security-identity-verifier-label"
                      class="fieldLabel"
                      value="&securityView.identity.verifier;"
                      control="security-identity-verifier-value"/>
               <textbox id="security-identity-verifier-value"
                        class="fieldValue" readonly="true" />
             </row>
           </rows>
         </grid>
         <spacer flex="1"/>
-        <hbox pack="end"><!-- Cert button -->
+        <!-- Cert button -->
+        <hbox id="security-view-cert-box" pack="end">
           <button id="security-view-cert" label="&securityView.certView;"
                   accesskey="&securityView.accesskey;"
                   oncommand="security.viewCert();"/>
         </hbox>
       </groupbox>
       
       <!-- Privacy & History section -->
       <groupbox id="security-privacy-groupbox" flex="1">
         <caption id="security-privacy" label="&securityView.privacy.header;" />
-        <grid>
+        <grid id="security-privacy-grid">
           <columns>
             <column flex="1"/>
             <column flex="1"/>
           </columns>
-          <rows>
-            <row><!-- History -->
+          <rows id="security-privacy-rows">
+            <!-- History -->
+            <row id="security-privacy-history-row">
               <label id="security-privacy-history-label"
                            control="security-privacy-history-value"
                            class="fieldLabel">&securityView.privacy.history;</label>
               <textbox id="security-privacy-history-value"
                        class="fieldValue"
                        value="&securityView.unknown;"
                        readonly="true"/>
             </row>
-            <row><!-- Cookies -->
+            <!-- Cookies -->
+            <row id="security-privacy-cookies-row">
               <label id="security-privacy-cookies-label"
                            control="security-privacy-cookies-value"
                            class="fieldLabel">&securityView.privacy.cookies;</label>
-              <hbox align="center">
+              <hbox id="security-privacy-cookies-box" align="center">
                 <textbox id="security-privacy-cookies-value"
                          class="fieldValue"
                          value="&securityView.unknown;"
                          flex="1"
                          readonly="true"/>
                 <button id="security-view-cookies"
                         label="&securityView.privacy.viewCookies;"
                         accesskey="&securityView.privacy.viewCookies.accessKey;"
                         oncommand="security.viewCookies();"/>
               </hbox>
             </row>
-            <row><!-- Passwords -->
+            <!-- Passwords -->
+            <row id="security-privacy-passwords-row">
               <label id="security-privacy-passwords-label"
                             control="security-privacy-passwords-value"
                             class="fieldLabel">&securityView.privacy.passwords;</label>
-              <hbox align="center">
+              <hbox id="security-privacy-passwords-box" align="center">
                 <textbox id="security-privacy-passwords-value"
                          class="fieldValue"
                          value="&securityView.unknown;"
                          flex="1"
                          readonly="true"/>
                 <button id="security-view-password"
                         label="&securityView.privacy.viewPasswords;"
                         accesskey="&securityView.privacy.viewPasswords.accessKey;"
@@ -497,17 +505,17 @@
             </row>
           </rows>
         </grid>
       </groupbox>
       
       <!-- Technical Details section -->
       <groupbox id="security-technical-groupbox" flex="1">
         <caption id="security-technical" label="&securityView.technical.header;" />
-        <vbox flex="1">
+        <vbox id="security-technical-box" flex="1">
           <label id="security-technical-shortform" class="fieldValue"/>
           <description id="security-technical-longform1" class="fieldLabel"/>
           <description id="security-technical-longform2" class="fieldLabel"/>
         </vbox>
       </groupbox>
     </vbox>
     <!-- Others added by overlay -->
   </deck>
--- a/browser/base/content/socialchat.xml
+++ b/browser/base/content/socialchat.xml
@@ -2,22 +2,20 @@
 
 <bindings id="socialChatBindings"
     xmlns="http://www.mozilla.org/xbl"
     xmlns:xbl="http://www.mozilla.org/xbl"
     xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <binding id="chatbox">
     <content orient="vertical" mousethrough="never">
-      <xul:hbox class="chat-titlebar" xbl:inherits="minimized">
+      <xul:hbox class="chat-titlebar" xbl:inherits="minimized,selected"
+                onclick="document.getBindingParent(this).toggle();">
         <xul:image class="chat-status-icon" xbl:inherits="src=image"/>
         <xul:label class="chat-title" flex="1" xbl:inherits="value=label,crop"/>
-        <xul:toolbarbutton class="chat-toggle-button chat-toolbarbutton"
-                           xbl:inherits="minimized"
-                           oncommand="document.getBindingParent(this).toggle();"/>
         <xul:toolbarbutton class="chat-close-button chat-toolbarbutton"
                            oncommand="document.getBindingParent(this).close();"/>
       </xul:hbox>
       <xul:iframe anonid="iframe" class="chat-frame" flex="1"
                   xbl:inherits="src,origin,collapsed=minimized" type="content"/>
     </content>
 
     <implementation implements="nsIDOMEventListener">
@@ -127,17 +125,29 @@
       </field>
 
       <property name="emptyWidth">
         <getter>
           return document.getAnonymousElementByAttribute(this, "anonid", "spacer").boxObject.width;
         </getter>
       </property>
 
-      <field name="selectedChat"/>
+      <property name="selectedChat">
+        <getter><![CDATA[
+          return this._selectedChat;
+        ]]></getter>
+        <setter><![CDATA[
+          if (this._selectedChat)
+            this._selectedChat.removeAttribute("selected");
+          this._selectedChat = val;
+          if (val) {
+            this._selectedChat.setAttribute("selected", "true");
+          }
+        ]]></setter>
+      </property>
 
       <field name="menuitemMap">new WeakMap()</field>
       <field name="chatboxForURL">new Map();</field>
 
       <property name="firstCollapsedChild">
         <getter><![CDATA[
           let child = this.lastChild;
           while (child && !child.collapsed) {
--- a/browser/base/content/sync/aboutSyncTabs.js
+++ b/browser/base/content/sync/aboutSyncTabs.js
@@ -52,18 +52,18 @@ let RemoteTabViewer = {
     let val = event.target.value.toLowerCase();
     let numTabs = this._tabsList.getRowCount();
     let clientTabs = 0;
     let currentClient = null;
     for (let i = 0;i < numTabs;i++) {
       let item = this._tabsList.getItemAtIndex(i);
       let hide = false;
       if (item.getAttribute("type") == "tab") {
-        if (item.getAttribute("url").toLowerCase().indexOf(val) == -1 &&
-            item.getAttribute("title").toLowerCase().indexOf(val) == -1)
+        if (!item.getAttribute("url").toLowerCase().contains(val) && 
+            !item.getAttribute("title").toLowerCase().contains(val))
           hide = true;
         else
           clientTabs++;
       }
       else if (item.getAttribute("type") == "client") {
         if (currentClient) {
           if (clientTabs == 0)
             currentClient.hidden = true;
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -790,17 +790,17 @@
                 newTitle += sep;
             }
             newTitle += modifier;
 
             // If location bar is hidden and the URL type supports a host,
             // add the scheme and host to the title to prevent spoofing.
             // XXX https://bugzilla.mozilla.org/show_bug.cgi?id=22183#c239
             try {
-              if (docElement.getAttribute("chromehidden").indexOf("location") != -1) {
+              if (docElement.getAttribute("chromehidden").contains("location")) {
                 var uri = this.mURIFixup.createExposableURI(
                             aBrowser.currentURI);
                 if (uri.scheme == "about")
                   newTitle = uri.spec + sep + newTitle;
                 else
                   newTitle = uri.prePath + sep + newTitle;
               }
             } catch (e) {}
@@ -3745,17 +3745,17 @@
         } else {
           // Pass true to disallow dropping javascript: or data: urls
           let url;
           try {
             url = browserDragAndDrop.drop(event, { }, true);
           } catch (ex) {}
 
           // valid urls don't contain spaces ' '; if we have a space it isn't a valid url.
-          if (!url || url.indexOf(" ") != -1)
+          if (!url || url.contains(" "))
             return;
 
           let bgLoad = Services.prefs.getBoolPref("browser.tabs.loadInBackground");
 
           if (event.shiftKey)
             bgLoad = !bgLoad;
 
           let tab = this._getDragTargetTab(event);
--- a/browser/base/content/test/browser_bug555224.js
+++ b/browser/base/content/test/browser_bug555224.js
@@ -14,17 +14,17 @@ function afterZoomAndLoad(aCallback, aTa
       executeSoon(aCallback);
   }, true);
   let oldAPTS = FullZoom._applyPrefToSetting;
   FullZoom._applyPrefToSetting = function(value, browser) {
     if (!value)
       value = undefined;
     oldAPTS.call(FullZoom, value, browser);
     // Don't reset _applyPrefToSetting until we've seen the about:blank load(s)
-    if (browser && (browser.currentURI.spec.indexOf("http:") != -1)) {
+    if (browser && browser.currentURI.spec.startsWith("http:")) {
       FullZoom._applyPrefToSetting = oldAPTS;
       didZoom = true;
     }
     if (didLoad && didZoom)
       executeSoon(aCallback);
   };
 }
 
--- a/browser/base/content/test/browser_homeDrop.js
+++ b/browser/base/content/test/browser_homeDrop.js
@@ -23,17 +23,17 @@ function test() {
     ok(true, "dialog appeared in response to home button drop");
     domwindow.document.documentElement.cancelDialog();
     Services.wm.removeListener(dialogListener);
 
     // Now trigger the invalid URI test
     executeSoon(function () {
       let consoleListener = {
         observe: function (m) {
-          if (m.message.indexOf("NS_ERROR_DOM_BAD_URI") > -1) {
+          if (m.message.contains("NS_ERROR_DOM_BAD_URI")) {
             ok(true, "drop was blocked");
             executeSoon(finish);
           }
         }
       }
       Services.console.registerListener(consoleListener);
       registerCleanupFunction(function () {
         Services.console.unregisterListener(consoleListener);
--- a/browser/base/content/test/browser_popupNotification.js
+++ b/browser/base/content/test/browser_popupNotification.js
@@ -751,17 +751,17 @@ function triggerSecondaryCommand(popup, 
     for (let i = 0; i <= index; i++)
       EventUtils.synthesizeKey("VK_DOWN", {});
 
     // Activate
     EventUtils.synthesizeKey("VK_ENTER", {});
   }, false);
 
   // One down event to open the popup
-  EventUtils.synthesizeKey("VK_DOWN", { altKey: (navigator.platform.indexOf("Mac") == -1) });
+  EventUtils.synthesizeKey("VK_DOWN", { altKey: !navigator.platform.contains("Mac") });
 }
 
 function loadURI(uri, callback) {
   if (callback) {
     gBrowser.addEventListener("load", function() {
       // Ignore the about:blank load
       if (gBrowser.currentURI.spec != uri)
         return;
--- a/browser/base/content/test/browser_social_chatwindow.js
+++ b/browser/base/content/test/browser_social_chatwindow.js
@@ -86,27 +86,29 @@ var tests = {
       }
     }
     let num = numToOpen;
     while (num-- > 0) {
       port.postMessage({topic: "test-chatbox-open", data: { id: num }});
     }
   },
   testWorkerChatWindow: function(next) {
+    const chatUrl = "https://example.com/browser/browser/base/content/test/social_chat.html";
     let port = Social.provider.port;
     ok(port, "provider has a port");
     port.onmessage = function (e) {
       let topic = e.data.topic;
       switch (topic) {
         case "got-chatbox-message":
           ok(true, "got a chat window opened");
           let chats = document.getElementById("pinnedchats");
           while (chats.selectedChat) {
             chats.selectedChat.close();
           }
           ok(!chats.selectedChat, "chats are all closed");
+          ensureSocialUrlNotRemembered(chatUrl);
           next();
           break;
       }
     }
-    port.postMessage({topic: "test-worker-chat" });
+    port.postMessage({topic: "test-worker-chat", data: chatUrl});
   }
 }
--- a/browser/base/content/test/browser_social_shareButton.js
+++ b/browser/base/content/test/browser_social_shareButton.js
@@ -138,25 +138,25 @@ function checkShortcutWorked(keyTarget) 
 
 function checkOKButton() {
   let okButton = document.getElementById("editSharePopupOkButton");
   let undoButton = document.getElementById("editSharePopupUndoButton");
   is(document.activeElement, okButton, "ok button should be focused by default");
 
   // This rest of particular test doesn't really apply on Mac, since buttons
   // aren't focusable by default.
-  if (navigator.platform.indexOf("Mac") != -1) {
+  if (navigator.platform.contains("Mac")) {
     executeSoon(testCloseBySpace);
     return;
   }
 
   let displayName = document.getElementById("socialUserDisplayName");
 
   // Linux has the buttons in the [unshare] [ok] order, so displayName will come first.
-  if (navigator.platform.indexOf("Linux") != -1) {
+  if (navigator.platform.contains("Linux")) {
     checkNextInTabOrder(displayName, function () {
       checkNextInTabOrder(undoButton, function () {
         checkNextInTabOrder(okButton, testCloseBySpace);
       });
     });
   } else {
     checkNextInTabOrder(undoButton, function () {
       checkNextInTabOrder(displayName, function () {
--- a/browser/base/content/test/social_worker.js
+++ b/browser/base/content/test/social_worker.js
@@ -59,17 +59,17 @@ onconnect = function(e) {
         break;
       case "flyout-message":
         testPort.postMessage({topic:"got-flyout-message", result: event.data.result});
         break;
       case "flyout-visibility":
         testPort.postMessage({topic:"got-flyout-visibility", result: event.data.result});
         break;
       case "test-worker-chat":
-        apiPort.postMessage({topic: "social.request-chat", data: "https://example.com/browser/browser/base/content/test/social_chat.html" });
+        apiPort.postMessage({topic: "social.request-chat", data: event.data.data });
         break;
       case "social.initialize":
         // This is the workerAPI port, respond and set up a notification icon.
         apiPort = port;
         port.postMessage({topic: "social.initialize-response"});
         let profile = {
           portrait: "https://example.com/portrait.jpg",
           userName: "trickster",
--- a/browser/base/content/test/test_offline_gzip.html
+++ b/browser/base/content/test/test_offline_gzip.html
@@ -71,17 +71,17 @@ function handleMessageEvents(event) {
         // Sometimes document.body may not exist, and trying to access
         // it will throw an exception, so handle this case.
         try {
           var bodyInnerHTML = frames.testFrame.document.body.innerHTML;
         }
         catch (e) {
           var bodyInnerHTML = "";
         }
-        if (cacheCount == 2 || (bodyInnerHTML.indexOf("error") != -1)) {
+        if (cacheCount == 2 || bodyInnerHTML.contains("error")) {
           clearInterval(intervalID);
           is(cacheCount, 2, "frame not reloaded successfully");
           if (cacheCount != 2) {
             finishTest();
           }
         }
       }, 100);
       break;
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -461,17 +461,17 @@
           var selectedVal = inputVal.substring(this.selectionStart, this.selectionEnd);
 
           // If the selection doesn't start at the beginning or doesn't span the full domain or
           // the URL bar is modified, nothing else to do here.
           if (this.selectionStart > 0 || this.valueIsTyped)
             return selectedVal;
           // The selection doesn't span the full domain if it doesn't contain a slash and is
           // followed by some character other than a slash.
-          if (selectedVal.indexOf("/") == -1) {
+          if (!selectedVal.contains("/")) {
             let remainder = inputVal.replace(selectedVal, "");
             if (remainder != "" && remainder[0] != "/")
               return selectedVal;
           }
 
           let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
 
           let uri;
--- a/browser/components/thumbnails/PageThumbs.jsm
+++ b/browser/components/thumbnails/PageThumbs.jsm
@@ -1,15 +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/. */
 
 "use strict";
 
-let EXPORTED_SYMBOLS = ["PageThumbs", "PageThumbsStorage", "PageThumbsCache"];
+let EXPORTED_SYMBOLS = ["PageThumbs", "PageThumbsStorage"];
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
 const PREF_STORAGE_VERSION = "browser.pagethumbnails.storage_version";
 const LATEST_STORAGE_VERSION = 2;
@@ -528,68 +528,8 @@ let PageThumbsHistoryObserver = {
   onEndUpdateBatch: function () {},
   onVisit: function () {},
   onBeforeDeleteURI: function () {},
   onPageChanged: function () {},
   onDeleteVisits: function () {},
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver])
 };
-
-/**
- * A singleton handling the storage of page thumbnails.
- */
-let PageThumbsCache = {
-  /**
-   * Calls the given callback with a cache entry opened for reading.
-   * @param aKey The key identifying the desired cache entry.
-   * @param aCallback The callback that is called when the cache entry is ready.
-   */
-  getReadEntry: function Cache_getReadEntry(aKey, aCallback) {
-    // Try to open the desired cache entry.
-    this._openCacheEntry(aKey, Ci.nsICache.ACCESS_READ, aCallback);
-  },
-
-  /**
-   * Opens the cache entry identified by the given key.
-   * @param aKey The key identifying the desired cache entry.
-   * @param aAccess The desired access mode (see nsICache.ACCESS_* constants).
-   * @param aCallback The function to be called when the cache entry was opened.
-   */
-  _openCacheEntry: function Cache_openCacheEntry(aKey, aAccess, aCallback) {
-    function onCacheEntryAvailable(aEntry, aAccessGranted, aStatus) {
-      let validAccess = aAccess == aAccessGranted;
-      let validStatus = Components.isSuccessCode(aStatus);
-
-      // Check if a valid entry was passed and if the
-      // access we requested was actually granted.
-      if (aEntry && !(validAccess && validStatus)) {
-        aEntry.close();
-        aEntry = null;
-      }
-
-      aCallback(aEntry);
-    }
-
-    let listener = this._createCacheListener(onCacheEntryAvailable);
-    this._cacheSession.asyncOpenCacheEntry(aKey, aAccess, listener);
-  },
-
-  /**
-   * Returns a cache listener implementing the nsICacheListener interface.
-   * @param aCallback The callback to be called when the cache entry is available.
-   * @return The new cache listener.
-   */
-  _createCacheListener: function Cache_createCacheListener(aCallback) {
-    return {
-      onCacheEntryAvailable: aCallback,
-      QueryInterface: XPCOMUtils.generateQI([Ci.nsICacheListener])
-    };
-  }
-};
-
-/**
- * Define a lazy getter for the cache session.
- */
-XPCOMUtils.defineLazyGetter(PageThumbsCache, "_cacheSession", function () {
-  return Services.cache.createSession(PageThumbs.scheme,
-                                     Ci.nsICache.STORE_ON_DISK, true);
-});
--- a/browser/components/thumbnails/PageThumbsProtocol.js
+++ b/browser/components/thumbnails/PageThumbsProtocol.js
@@ -69,331 +69,33 @@ Protocol.prototype = {
   /**
    * Constructs a new channel from the given URI for this protocol handler.
    * @param aURI The URI for which to construct a channel.
    * @return The newly created channel.
    */
   newChannel: function Proto_newChannel(aURI) {
     let {url} = parseURI(aURI);
     let file = PageThumbsStorage.getFileForURL(url);
-
-    if (file.exists()) {
-      let fileuri = Services.io.newFileURI(file);
-      return Services.io.newChannelFromURI(fileuri);
-    }
-
-    return new Channel(aURI);
+    let fileuri = Services.io.newFileURI(file);
+    return Services.io.newChannelFromURI(fileuri);
   },
 
   /**
    * Decides whether to allow a blacklisted port.
    * @return Always false, we'll never allow ports.
    */
   allowPort: function () false,
 
   classID: Components.ID("{5a4ae9b5-f475-48ae-9dce-0b4c1d347884}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler])
 };
 
 let NSGetFactory = XPCOMUtils.generateNSGetFactory([Protocol]);
 
 /**
- * A channel implementation responsible for delivering cached thumbnails.
- */
-function Channel(aURI) {
-  this._uri = aURI;
-
-  // nsIChannel
-  this.originalURI = aURI;
-
-  // nsIHttpChannel
-  this._responseHeaders = {"content-type": PageThumbs.contentType};
-}
-
-Channel.prototype = {
-  _uri: null,
-  _referrer: null,
-  _canceled: false,
-  _status: Cr.NS_OK,
-  _isPending: false,
-  _wasOpened: false,
-  _responseText: "OK",
-  _responseStatus: 200,
-  _responseHeaders: null,
-  _requestMethod: "GET",
-  _requestStarted: false,
-  _allowPipelining: true,
-  _requestSucceeded: true,
-
-  /* :::::::: nsIChannel ::::::::::::::: */
-
-  get URI() this._uri,
-  owner: null,
-  notificationCallbacks: null,
-  get securityInfo() null,
-
-  contentType: PageThumbs.contentType,
-  contentCharset: null,
-  contentLength: -1,
-
-  get contentDisposition() {
-    throw (Components.returnCode = Cr.NS_ERROR_NOT_AVAILABLE);
-  },
-
-  get contentDispositionFilename() {
-    throw (Components.returnCode = Cr.NS_ERROR_NOT_AVAILABLE);
-  },
-
-  get contentDispositionHeader() {
-    throw (Components.returnCode = Cr.NS_ERROR_NOT_AVAILABLE);
-  },
-
-  open: function Channel_open() {
-    throw (Components.returnCode = Cr.NS_ERROR_NOT_IMPLEMENTED);
-  },
-
-  asyncOpen: function Channel_asyncOpen(aListener, aContext) {
-    if (this._isPending)
-      throw (Components.returnCode = Cr.NS_ERROR_IN_PROGRESS);
-
-    if (this._wasOpened)
-      throw (Components.returnCode = Cr.NS_ERROR_ALREADY_OPENED);
-
-    if (this._canceled)
-      return (Components.returnCode = this._status);
-
-    this._isPending = true;
-    this._wasOpened = true;
-
-    this._listener = aListener;
-    this._context = aContext;
-
-    if (this.loadGroup)
-      this.loadGroup.addRequest(this, null);
-
-    if (this._canceled)
-      return;
-
-    let {url} = parseURI(this._uri);
-    if (!url) {
-      this._serveThumbnailNotFound();
-      return;
-    }
-
-    PageThumbsCache.getReadEntry(url, function (aEntry) {
-      let inputStream = aEntry && aEntry.openInputStream(0);
-      if (!inputStream || !inputStream.available()) {
-        if (aEntry)
-          aEntry.close();
-        this._serveThumbnailNotFound();
-        return;
-      }
-
-      this._entry = aEntry;
-      this._pump = Cc["@mozilla.org/network/input-stream-pump;1"].
-                   createInstance(Ci.nsIInputStreamPump);
-
-      this._pump.init(inputStream, -1, -1, 0, 0, true);
-      this._pump.asyncRead(this, null);
-
-      this._trackThumbnailHitOrMiss(true);
-    }.bind(this));
-  },
-
-  /**
-   * Serves a "404 Not Found" if we didn't find the requested thumbnail.
-   */
-  _serveThumbnailNotFound: function Channel_serveThumbnailNotFound() {
-    this._responseStatus = 404;
-    this._responseText = "Not Found";
-    this._requestSucceeded = false;
-
-    this.onStartRequest(this, null);
-    this.onStopRequest(this, null, Cr.NS_OK);
-
-    this._trackThumbnailHitOrMiss(false);
-  },
-
-  /**
-   * Implements telemetry tracking for thumbnail cache hits and misses.
-   * @param aFound Whether the thumbnail was found.
-   */
-  _trackThumbnailHitOrMiss: function Channel_trackThumbnailHitOrMiss(aFound) {
-    Services.telemetry.getHistogramById("FX_THUMBNAILS_HIT_OR_MISS")
-      .add(aFound);
-  },
-
-  /* :::::::: nsIStreamListener ::::::::::::::: */
-
-  onStartRequest: function Channel_onStartRequest(aRequest, aContext) {
-    if (!this.canceled && Components.isSuccessCode(this._status))
-      this._status = aRequest.status;
-
-    this._requestStarted = true;
-    this._listener.onStartRequest(this, this._context);
-  },
-
-  onDataAvailable: function Channel_onDataAvailable(aRequest, aContext,
-                                                    aInStream, aOffset, aCount) {
-    this._listener.onDataAvailable(this, this._context, aInStream, aOffset, aCount);
-  },
-
-  onStopRequest: function Channel_onStopRequest(aRequest, aContext, aStatus) {
-    this._isPending = false;
-    this._status = aStatus;
-
-    this._listener.onStopRequest(this, this._context, aStatus);
-    this._listener = null;
-    this._context = null;
-
-    if (this._entry)
-      this._entry.close();
-
-    if (this.loadGroup)
-      this.loadGroup.removeRequest(this, null, aStatus);
-  },
-
-  /* :::::::: nsIRequest ::::::::::::::: */
-
-  get status() this._status,
-  get name() this._uri.spec,
-  isPending: function Channel_isPending() this._isPending,
-
-  loadFlags: Ci.nsIRequest.LOAD_NORMAL,
-  loadGroup: null,
-
-  cancel: function Channel_cancel(aStatus) {
-    if (this._canceled)
-      return;
-
-    this._canceled = true;
-    this._status = aStatus;
-
-    if (this._pump)
-      this._pump.cancel(aStatus);
-  },
-
-  suspend: function Channel_suspend() {
-    if (this._pump)
-      this._pump.suspend();
-  },
-
-  resume: function Channel_resume() {
-    if (this._pump)
-      this._pump.resume();
-  },
-
-  /* :::::::: nsIHttpChannel ::::::::::::::: */
-
-  get referrer() this._referrer,
-
-  set referrer(aReferrer) {
-    if (this._wasOpened)
-      throw (Components.returnCode = Cr.NS_ERROR_IN_PROGRESS);
-
-    this._referrer = aReferrer;
-  },
-
-  get requestMethod() this._requestMethod,
-
-  set requestMethod(aMethod) {
-    if (this._wasOpened)
-      throw (Components.returnCode = Cr.NS_ERROR_IN_PROGRESS);
-
-    this._requestMethod = aMethod.toUpperCase();
-  },
-
-  get allowPipelining() this._allowPipelining,
-
-  set allowPipelining(aAllow) {
-    if (this._wasOpened)
-      throw (Components.returnCode = Cr.NS_ERROR_FAILURE);
-
-    this._allowPipelining = aAllow;
-  },
-
-  redirectionLimit: 10,
-
-  get responseStatus() {
-    if (this._requestStarted)
-      throw (Components.returnCode = Cr.NS_ERROR_NOT_AVAILABLE);
-
-    return this._responseStatus;
-  },
-
-  get responseStatusText() {
-    if (this._requestStarted)
-      throw (Components.returnCode = Cr.NS_ERROR_NOT_AVAILABLE);
-
-    return this._responseText;
-  },
-
-  get requestSucceeded() {
-    if (this._requestStarted)
-      throw (Components.returnCode = Cr.NS_ERROR_NOT_AVAILABLE);
-
-    return this._requestSucceeded;
-  },
-
-  isNoCacheResponse: function Channel_isNoCacheResponse() false,
-  isNoStoreResponse: function Channel_isNoStoreResponse() false,
-
-  getRequestHeader: function Channel_getRequestHeader() {
-    throw (Components.returnCode = Cr.NS_ERROR_NOT_AVAILABLE);
-  },
-
-  setRequestHeader: function Channel_setRequestHeader() {
-    if (this._wasOpened)
-      throw (Components.returnCode = Cr.NS_ERROR_IN_PROGRESS);
-  },
-
-  visitRequestHeaders: function Channel_visitRequestHeaders() {},
-
-  getResponseHeader: function Channel_getResponseHeader(aHeader) {
-    let name = aHeader.toLowerCase();
-    if (name in this._responseHeaders)
-      return this._responseHeaders[name];
-
-    throw (Components.returnCode = Cr.NS_ERROR_NOT_AVAILABLE);
-  },
-
-  setResponseHeader: function Channel_setResponseHeader(aHeader, aValue, aMerge) {
-    let name = aHeader.toLowerCase();
-    if (!aValue && !aMerge)
-      delete this._responseHeaders[name];
-    else
-      this._responseHeaders[name] = aValue;
-  },
-
-  visitResponseHeaders: function Channel_visitResponseHeaders(aVisitor) {
-    for (let name in this._responseHeaders) {
-      let value = this._responseHeaders[name];
-
-      try {
-        aVisitor.visitHeader(name, value);
-      } catch (e) {
-        // The visitor can throw to stop the iteration.
-        return;
-      }
-    }
-  },
-
-  /* :::::::: nsIHttpChannelInternal ::::::::::::::: */
-
-  documentURI: null,
-  get canceled() this._canceled,
-  allowSpdy: false,
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel,
-                                         Ci.nsIHttpChannel,
-                                         Ci.nsIHttpChannelInternal,
-                                         Ci.nsIRequest])
-}
-
-/**
  * Parses a given URI and extracts all parameters relevant to this protocol.
  * @param aURI The URI to parse.
  * @return The parsed parameters.
  */
 function parseURI(aURI) {
   let {scheme, staticHost} = PageThumbs;
   let re = new RegExp("^" + scheme + "://" + staticHost + ".*?\\?");
   let query = aURI.spec.replace(re, "");
--- a/browser/devtools/commandline/CmdScreenshot.jsm
+++ b/browser/devtools/commandline/CmdScreenshot.jsm
@@ -36,17 +36,17 @@ gcli.addCommand({
     },
     {
       name: "fullpage",
       type: "boolean",
       description: gcli.lookup("screenshotFullPageDesc"),
       manual: gcli.lookup("screenshotFullPageManual")
     },
     {
-      name: "node",
+      name: "selector",
       type: "node",
       defaultValue: null,
       description: gcli.lookup("inspectNodeDesc"),
       manual: gcli.lookup("inspectNodeManual")
     }
   ],
   exec: function Command_screenshot(args, context) {
     var document = context.environment.contentDocument;
@@ -54,17 +54,17 @@ gcli.addCommand({
       var promise = context.createPromise();
       document.defaultView.setTimeout(function Command_screenshotDelay() {
         let reply = this.grabScreen(document, args.filename);
         promise.resolve(reply);
       }.bind(this), args.delay * 1000);
       return promise;
     }
     else {
-      return this.grabScreen(document, args.filename, args.fullpage, args.node);
+      return this.grabScreen(document, args.filename, args.fullpage, args.selector);
     }
   },
   grabScreen:
   function Command_screenshotGrabScreen(document, filename, fullpage, node) {
     let window = document.defaultView;
     let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
     let left = 0;
     let top = 0;
--- a/browser/devtools/commandline/gcli.jsm
+++ b/browser/devtools/commandline/gcli.jsm
@@ -1702,17 +1702,17 @@ exports.ArrayArgument = ArrayArgument;
 define('gcli/types/selection', ['require', 'exports', 'module' , 'gcli/l10n', 'gcli/types', 'gcli/types/spell'], function(require, exports, module) {
 
 
 var l10n = require('gcli/l10n');
 var types = require('gcli/types');
 var Type = require('gcli/types').Type;
 var Status = require('gcli/types').Status;
 var Conversion = require('gcli/types').Conversion;
-var Speller = require('gcli/types/spell').Speller;
+var spell = require('gcli/types/spell');
 
 
 /**
  * Registration and de-registration.
  */
 exports.startup = function() {
   types.registerType(SelectionType);
 };
@@ -1883,23 +1883,24 @@ SelectionType.prototype._findPredictions
         if (predictions.indexOf(option) === -1) {
           this._addToPredictions(predictions, option, arg);
         }
       }
     }
   }
 
   // Try fuzzy matching if we don't get a prefix match
-  if (false && predictions.length === 0) {
-    var speller = new Speller();
-    var names = lookup.map(function(opt) {
-      return opt.name;
+  if (predictions.length === 0) {
+    var names = [];
+    lookup.forEach(function(opt) {
+      if (!opt.value.hidden) {
+        names.push(opt.name);
+      }
     });
-    speller.train(names);
-    var corrected = speller.correct(match);
+    var corrected = spell.correct(match, names);
     if (corrected) {
       lookup.forEach(function(opt) {
         if (opt.name === corrected) {
           predictions.push(opt);
         }
       }, this);
     }
   }
@@ -2000,163 +2001,126 @@ SelectionType.prototype._findValue = fun
 
 SelectionType.prototype.name = 'selection';
 
 exports.SelectionType = SelectionType;
 
 
 });
 /*
- * Copyright (c) 2009 Panagiotis Astithas
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ * Copyright 2012, Mozilla Foundation and contributors
+ *
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
  */
 
 define('gcli/types/spell', ['require', 'exports', 'module' ], function(require, exports, module) {
 
-
-/**
- * A spell-checker based on the statistical algorithm described by Peter Norvig
- * in http://norvig.com/spell-correct.html, and converted to JavaScript by Past
- * http://past.github.com/speller/
- *
- * Usage requires a two-step process:
- * 1) call speller.train() one or more times with a large text to train the
- *    language model
- * 2) call speller.correct(word) to retrieve the correction for the specified
- *    word
- */
-function Speller() {
-  // A map of words to the count of times they were encountered during training.
-  this._nWords = {};
-}
-
-Speller.letters = "abcdefghijklmnopqrstuvwxyz".split("");
-
-/**
- * A function that trains the language model with the words in the supplied
- * text. Multiple invocation of this function can extend the training of the
- * model.
- */
-Speller.prototype.train = function(words) {
-  words.forEach(function(word) {
-    word = word.toLowerCase();
-    this._nWords[word] = this._nWords.hasOwnProperty(word) ?
-            this._nWords[word] + 1 :
-            1;
-  }, this);
+/*
+ * A spell-checker based on Damerau-Levenshtein distance.
+ */
+
+var INSERTION_COST = 1;
+var DELETION_COST = 1;
+var SWAP_COST = 1;
+var SUBSTITUTION_COST = 2;
+var MAX_EDIT_DISTANCE = 4;
+
+/**
+ * Compute Damerau-Levenshtein Distance
+ * @see http://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
+ */
+function damerauLevenshteinDistance(wordi, wordj) {
+  var N = wordi.length;
+  var M = wordj.length;
+
+  // We only need to store three rows of our dynamic programming matrix.
+  // (Without swap, it would have been two.)
+  var row0 = new Array(N+1);
+  var row1 = new Array(N+1);
+  var row2 = new Array(N+1);
+  var tmp;
+
+  var i, j;
+
+  // The distance between the empty string and a string of size i is the cost
+  // of i insertions.
+  for (i = 0; i <= N; i++) {
+    row1[i] = i * INSERTION_COST;
+  }
+
+  // Row-by-row, we're computing the edit distance between substrings wordi[0..i]
+  // and wordj[0..j].
+  for (j = 1; j <= M; j++)
+  {
+    // Edit distance between wordi[0..0] and wordj[0..j] is the cost of j
+    // insertions.
+    row0[0] = j * INSERTION_COST;
+
+    for (i = 1; i <= N; i++)
+    {
+      // Handle deletion, insertion and substitution: we can reach each cell
+      // from three other cells corresponding to those three operations. We
+      // want the minimum cost.
+      row0[i] = Math.min(
+          row0[i-1] + DELETION_COST,
+          row1[i] + INSERTION_COST,
+          row1[i-1] + (wordi[i-1] === wordj[j-1] ? 0 : SUBSTITUTION_COST));
+      // We handle swap too, eg. distance between help and hlep should be 1. If
+      // we find such a swap, there's a chance to update row0[1] to be lower.
+      if (i > 1 && j > 1 && wordi[i-1] === wordj[j-2] && wordj[j-1] === wordi[i-2]) {
+        row0[i] = Math.min(row0[i], row2[i-2] + SWAP_COST);
+      }
+    }
+
+    tmp = row2;
+    row2 = row1;
+    row1 = row0;
+    row0 = tmp;
+  }
+
+  return row1[N];
 };
 
 /**
  * A function that returns the correction for the specified word.
  */
-Speller.prototype.correct = function(word) {
-  if (this._nWords.hasOwnProperty(word)) {
-    return word;
-  }
-
-  var candidates = {};
-  var list = this._edits(word);
-  list.forEach(function(edit) {
-    if (this._nWords.hasOwnProperty(edit)) {
-      candidates[this._nWords[edit]] = edit;
-    }
-  }, this);
-
-  if (this._countKeys(candidates) > 0) {
-    return candidates[this._max(candidates)];
-  }
-
-  list.forEach(function(edit) {
-    this._edits(edit).forEach(function(w) {
-      if (this._nWords.hasOwnProperty(w)) {
-        candidates[this._nWords[w]] = w;
-      }
-    }, this);
-  }, this);
-
-  return this._countKeys(candidates) > 0 ?
-      candidates[this._max(candidates)] :
-      null;
-};
-
-/**
- * A helper function that counts the keys in the supplied object.
- */
-Speller.prototype._countKeys = function(object) {
-  // return Object.keys(object).length; ?
-  var count = 0;
-  for (var attr in object) {
-    if (object.hasOwnProperty(attr)) {
-      count++;
-    }
-  }
-  return count;
-};
-
-/**
- * A helper function that returns the word with the most occurrences in the
- * language model, among the supplied candidates.
- * @param candidates
- */
-Speller.prototype._max = function(candidates) {
-  var arr = [];
-  for (var candidate in candidates) {
-    if (candidates.hasOwnProperty(candidate)) {
-      arr.push(candidate);
-    }
-  }
-  return Math.max.apply(null, arr);
-};
-
-/**
- * A function that returns the set of possible corrections of the specified
- * word. The edits can be deletions, insertions, alterations or transpositions.
- */
-Speller.prototype._edits = function(word) {
-  var results = [];
-
-  // Deletion
-  for (var i = 0; i < word.length; i++) {
-    results.push(word.slice(0, i) + word.slice(i + 1));
-  }
-
-  // Transposition
-  for (i = 0; i < word.length - 1; i++) {
-    results.push(word.slice(0, i) + word.slice(i + 1, i + 2)
-            + word.slice(i, i + 1) + word.slice(i + 2));
-  }
-
-  // Alteration
-  for (i = 0; i < word.length; i++) {
-    Speller.letters.forEach(function(l) {
-      results.push(word.slice(0, i) + l + word.slice(i + 1));
-    }, this);
-  }
-
-  // Insertion
-  for (i = 0; i <= word.length; i++) {
-    Speller.letters.forEach(function(l) {
-      results.push(word.slice(0, i) + l + word.slice(i));
-    }, this);
-  }
-
-  return results;
-};
-
-exports.Speller = Speller;
+exports.correct = function(word, names) {
+  var distance = {};
+  var sorted_candidates;
+
+  names.forEach(function(candidate) {
+    distance[candidate] = damerauLevenshteinDistance(word, candidate);
+  });
+
+  sorted_candidates = names.sort(function(worda, wordb) {
+    if (distance[worda] !== distance[wordb]) {
+      return distance[worda] - distance[wordb];
+    } else {
+      // if the score is the same, always return the first string
+      // in the lexicographical order
+      return worda < wordb;
+    }
+  });
+
+  if (distance[sorted_candidates[0]] <= MAX_EDIT_DISTANCE) {
+    return sorted_candidates[0];
+  } else {
+    return undefined;
+  }
+};
 
 
 });
 /*
  * Copyright 2012, Mozilla Foundation and contributors
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -4063,17 +4027,19 @@ if (typeof document !== 'undefined') {
  */
 exports._empty = [];
 
 /**
  * Setter for the document that contains the nodes we're matching
  */
 exports.setDocument = function(document) {
   doc = document;
-  exports._empty = doc.querySelectorAll('x>:root');
+  if (doc != null) {
+    exports._empty = doc.querySelectorAll('x>:root');
+  }
 };
 
 /**
  * Undo the effects of setDocument()
  */
 exports.unsetDocument = function() {
   doc = undefined;
 };
@@ -5122,17 +5088,17 @@ define('gcli/ui/domtemplate', ['require'
 define('gcli/cli', ['require', 'exports', 'module' , 'gcli/util', 'gcli/ui/view', 'gcli/l10n', 'gcli/canon', 'gcli/promise', 'gcli/types', 'gcli/types/basic', 'gcli/argument'], function(require, exports, module) {
 
 
 var util = require('gcli/util');
 var view = require('gcli/ui/view');
 var l10n = require('gcli/l10n');
 
 var canon = require('gcli/canon');
-var Promise = require('gcli/promise').Promise;
+var Q = require('gcli/promise');
 
 var Status = require('gcli/types').Status;
 var Conversion = require('gcli/types').Conversion;
 var ArrayType = require('gcli/types/basic').ArrayType;
 var StringType = require('gcli/types/basic').StringType;
 var BooleanType = require('gcli/types/basic').BooleanType;
 var NumberType = require('gcli/types/basic').NumberType;
 
@@ -5185,18 +5151,16 @@ function Assignment(param, paramIndex) {
   this.conversion = undefined;
 
   // The index of this parameter in the parent Requisition. paramIndex === -1
   // is the command assignment although this should not be relied upon, it is
   // better to test param instanceof CommandAssignment
   this.paramIndex = paramIndex;
 
   this.onAssignmentChange = util.createEvent('Assignment.onAssignmentChange');
-
-  this.setBlank();
 }
 
 /**
  * Easy accessor for conversion.arg.
  * This is a read-only property because writes to arg should be done through
  * the 'conversion' property.
  */
 Object.defineProperty(Assignment.prototype, 'arg', {
@@ -5271,49 +5235,16 @@ Assignment.prototype.getPredictionAt = f
  * to take this into account.
  */
 Assignment.prototype.isInName = function() {
   return this.conversion.arg.type === 'NamedArgument' &&
          this.conversion.arg.prefix.slice(-1) !== ' ';
 };
 
 /**
- * Report on the status of the last parse() conversion.
- * We force mutations to happen through this method rather than have
- * setValue and setArgument functions to help maintain integrity when we
- * have ArrayArguments and don't want to get confused. This way assignments
- * are just containers for a conversion rather than things that store
- * a connection between an arg/value.
- * @see types.Conversion
- */
-Assignment.prototype.setConversion = function(conversion) {
-  var oldConversion = this.conversion;
-
-  this.conversion = conversion;
-  this.conversion.assign(this);
-
-  if (this.conversion.equals(oldConversion)) {
-    return;
-  }
-
-  this.onAssignmentChange({
-    assignment: this,
-    conversion: this.conversion,
-    oldConversion: oldConversion
-  });
-};
-
-/**
- * Setup an empty value for the conversion by parsing an empty argument.
- */
-Assignment.prototype.setBlank = function() {
-  this.setConversion(this.param.type.getBlank());
-};
-
-/**
  * Make sure that there is some content for this argument by using an
  * Argument of '' if needed.
  */
 Assignment.prototype.ensureVisibleArgument = function() {
   // It isn't clear if we should be sending events from this method.
   // It should only be called when structural changes are happening in which
   // case we're going to ignore the event anyway. But on the other hand
   // perhaps this function shouldn't need to know how it is used, and should
@@ -5449,18 +5380,16 @@ function CommandAssignment() {
           value.description :
           'The command to execute';
     },
     enumerable: true
   });
   this.param = new canon.Parameter(commandParamMetadata);
   this.paramIndex = -1;
   this.onAssignmentChange = util.createEvent('CommandAssignment.onAssignmentChange');
-
-  this.setBlank();
 }
 
 CommandAssignment.prototype = Object.create(Assignment.prototype);
 
 CommandAssignment.prototype.getStatus = function(arg) {
   return Status.combine(
     Assignment.prototype.getStatus.call(this, arg),
     this.conversion.value && this.conversion.value.exec ?
@@ -5535,16 +5464,17 @@ function Requisition(environment, doc) {
     catch (ex) {
       // Ignore
     }
   }
 
   // The command that we are about to execute.
   // @see setCommandConversion()
   this.commandAssignment = new CommandAssignment();
+  this._setAssignment(this.commandAssignment, null, true);
 
   // The object that stores of Assignment objects that we are filling out.
   // The Assignment objects are stored under their param.name for named
   // lookup. Note: We make use of the property of Javascript objects that
   // they are not just hashmaps, but linked-list hashmaps which iterate in
   // insertion order.
   // _assignments excludes the commandAssignment.
   this._assignments = {};
@@ -5621,16 +5551,17 @@ Requisition.prototype._commandAssignment
 
   this._assignments = {};
 
   var command = this.commandAssignment.value;
   if (command) {
     for (var i = 0; i < command.params.length; i++) {
       var param = command.params[i];
       var assignment = new Assignment(param, i);
+      this._setAssignment(assignment, null, true);
       assignment.onAssignmentChange.add(this._assignmentChanged, this);
       this._assignments[param.name] = assignment;
     }
   }
   this.assignmentCount = Object.keys(this._assignments).length;
 };
 
 /**
@@ -5743,58 +5674,99 @@ Requisition.prototype.getAssignments = f
   }
   Object.keys(this._assignments).forEach(function(name) {
     assignments.push(this.getAssignment(name));
   }, this);
   return assignments;
 };
 
 /**
- * Alter the given assignment using the given arg. This function is better than
- * calling assignment.setConversion(assignment.param.type.parse(arg)) because
- * it adjusts the args in this requisition to keep things up to date
+ * Alter the given assignment using the given arg.
+ * @param assignment The assignment to alter
+ * @param arg The new value for the assignment. An instance of Argument, or an
+ * instance of Conversion, or null to set the blank value.
  */
 Requisition.prototype.setAssignment = function(assignment, arg) {
-  var originalArgs = assignment.arg.getArgs();
-  var conversion = assignment.param.type.parse(arg);
-  assignment.setConversion(conversion);
-
-  var replacementArgs = arg.getArgs();
-  var maxLen = Math.max(originalArgs.length, replacementArgs.length);
-  for (var i = 0; i < maxLen; i++) {
-    // If there are no more original args, or if the original arg was blank
-    // (i.e. not typed by the user), we'll just need to add at the end
-    if (i >= originalArgs.length || originalArgs[i].type === 'BlankArgument') {
-      this._args.push(replacementArgs[i]);
-      continue;
-    }
-
-    var index = this._args.indexOf(originalArgs[i]);
-    if (index === -1) {
-      console.error('Couldn\'t find ', originalArgs[i], ' in ', this._args);
-      throw new Error('Couldn\'t find ' + originalArgs[i]);
-    }
-
-    // If there are no more replacement args, we just remove the original args
-    // Otherwise swap original args and replacements
-    if (i >= replacementArgs.length) {
-      this._args.splice(index, 1);
-    }
-    else {
-      this._args[index] = replacementArgs[i];
-    }
-  }
+  this._setAssignment(assignment, arg, false);
+};
+
+/**
+ * Internal function to alter the given assignment using the given arg.
+ * @param assignment The assignment to alter
+ * @param arg The new value for the assignment. An instance of Argument, or an
+ * instance of Conversion, or null to set the blank value.
+ * @param skipArgUpdate (default=false) Adjusts the args in this requisition to
+ * keep things up to date. Args should only be skipped when setAssignment is
+ * being called as part of the update process.
+ */
+Requisition.prototype._setAssignment = function(assignment, arg, skipArgUpdate) {
+  if (!skipArgUpdate) {
+    var originalArgs = assignment.arg.getArgs();
+
+    // Update the args array
+    var replacementArgs = arg.getArgs();
+    var maxLen = Math.max(originalArgs.length, replacementArgs.length);
+    for (var i = 0; i < maxLen; i++) {
+      // If there are no more original args, or if the original arg was blank
+      // (i.e. not typed by the user), we'll just need to add at the end
+      if (i >= originalArgs.length || originalArgs[i].type === 'BlankArgument') {
+        this._args.push(replacementArgs[i]);
+        continue;
+      }
+
+      var index = this._args.indexOf(originalArgs[i]);
+      if (index === -1) {
+        console.error('Couldn\'t find ', originalArgs[i], ' in ', this._args);
+        throw new Error('Couldn\'t find ' + originalArgs[i]);
+      }
+
+      // If there are no more replacement args, we just remove the original args
+      // Otherwise swap original args and replacements
+      if (i >= replacementArgs.length) {
+        this._args.splice(index, 1);
+      }
+      else {
+        this._args[index] = replacementArgs[i];
+      }
+    }
+  }
+
+  var conversion;
+  if (arg == null) {
+    conversion = assignment.param.type.getBlank();
+  }
+  else if (typeof arg.getStatus === 'function') {
+    conversion = arg;
+  }
+  else {
+    conversion = assignment.param.type.parse(arg);
+  }
+
+  var oldConversion = assignment.conversion;
+
+  assignment.conversion = conversion;
+  assignment.conversion.assign(assignment);
+
+  if (assignment.conversion.equals(oldConversion)) {
+    return;
+  }
+
+  assignment.onAssignmentChange({
+    assignment: assignment,
+    conversion: assignment.conversion,
+    oldConversion: oldConversion
+  });
 };
 
 /**
  * Reset all the assignments to their default values
  */
 Requisition.prototype.setBlankArguments = function() {
   this.getAssignments().forEach(function(assignment) {
-    assignment.setBlank();
+    this._setAssignment(assignment, null, true);
   }, this);
 };
 
 /**
  * Complete the argument at <tt>cursor</tt>.
  * Basically the same as:
  *   assignment = getAssignmentAt(cursor);
  *   assignment.value = assignment.conversion.predictions[0];
@@ -6143,18 +6115,18 @@ Requisition.prototype.getAssignmentAt = 
  *    argument values as passed to command.exec. This method is significantly
  *    faster, and designed for use from keyboard shortcuts.
  * In addition to these properties, the input parameter can contain a 'hidden'
  * property which can be set to true to hide the output from the
  * CommandOutputManager.
  * @param input (optional) The command to execute. See above.
  */
 Requisition.prototype.exec = function(input) {
-  var command;
-  var args;
+  var command = null;
+  var args = null;
   var hidden = false;
   if (input && input.hidden) {
     hidden = true;
   }
 
   if (input) {
     if (typeof input === 'string') {
       this.update(input);
@@ -6195,44 +6167,69 @@ Requisition.prototype.exec = function(in
     args: args,
     typed: typed,
     canonical: this.toCanonicalString(),
     hidden: hidden
   });
 
   this.commandOutputManager.onOutput({ output: output });
 
+  var onDone = function(data) {
+    output.complete(data);
+  };
+
+  var onError = function(error) {
+    console.error(error);
+    output.error = true;
+    output.complete(error);
+  };
+
   try {
     var context = exports.createExecutionContext(this);
     var reply = command.exec(args, context);
 
-    if (reply != null && typeof reply.then === 'function') {
-      reply.then(
-          function(data) { output.complete(data); },
-          function(error) { output.error = true; output.complete(error); });
-
-      output.promise = reply;
-      // Add progress to our promise and add a handler for it here
-      // See bug 659300
-    }
-    else {
-      output.complete(reply);
-    }
+    this._then(reply, onDone, onError);
   }
   catch (ex) {
-    console.error(ex);
-    output.error = true;
-    output.complete(ex);
+    onError(ex);
   }
 
   this.update('');
   return output;
 };
 
 /**
+ * Different types of promise have different ways of doing 'then'. This is a
+ * catch-all so we can ignore the differences. It also handles concrete values
+ * and calls onDone directly if thing is not a promise.
+ * @param thing The value to test for 'promiseness'
+ * @param onDone The action to take if thing is resolved
+ * @param onError The action to take if thing is rejected
+ */
+Requisition.prototype._then = function(thing, onDone, onError) {
+  var then = null;
+  if (thing != null && typeof thing.then === 'function') {
+    // Old GCLI style / simple promises with a then function
+    then = thing.then;
+  }
+  else if (thing != null && thing.promise != null &&
+                typeof thing.promise.then === 'function') {
+    // Q / Mozilla add-ons style
+    then = thing.promise.then;
+  }
+
+  if (then != null) {
+    then(onDone, onError);
+  }
+  else {
+    onDone(thing);
+  }
+};
+
+/**
  * Called by the UI when ever the user interacts with a command line input
  * @param typed The contents of the input field
  */
 Requisition.prototype.update = function(typed) {
   this._structuralChangeInProgress = true;
 
   this._args = this._tokenize(typed);
   var args = this._args.slice(0); // i.e. clone
@@ -6512,26 +6509,26 @@ Requisition.prototype._split = function(
   // Handle the special case of the user typing { javascript(); }
   // We use the hidden 'eval' command directly rather than shift()ing one of
   // the parameters, and parse()ing it.
   var conversion;
   if (args[0].type === 'ScriptArgument') {
     // Special case: if the user enters { console.log('foo'); } then we need to
     // use the hidden 'eval' command
     conversion = new Conversion(evalCommand, new ScriptArgument());
-    this.commandAssignment.setConversion(conversion);
+    this._setAssignment(this.commandAssignment, conversion, true);
     return;
   }
 
   var argsUsed = 1;
 
   while (argsUsed <= args.length) {
     var arg = (argsUsed === 1) ?
-      args[0] :
-      new MergedArgument(args, 0, argsUsed);
+              args[0] :
+              new MergedArgument(args, 0, argsUsed);
     conversion = this.commandAssignment.param.type.parse(arg);
 
     // We only want to carry on if this command is a parent command,
     // which means that there is a commandAssignment, but not one with
     // an exec function.
     if (!conversion.value || conversion.value.exec) {
       break;
     }
@@ -6539,17 +6536,17 @@ Requisition.prototype._split = function(
     // Previously we needed a way to hide commands depending context.
     // We have not resurrected that feature yet, but if we do we should
     // insert code here to ignore certain commands depending on the
     // context/environment
 
     argsUsed++;
   }
 
-  this.commandAssignment.setConversion(conversion);
+  this._setAssignment(this.commandAssignment, conversion, true);
 
   for (var i = 0; i < argsUsed; i++) {
     args.shift();
   }
 
   // This could probably be re-written to consume args as we go
 };
 
@@ -6585,21 +6582,18 @@ Requisition.prototype._assign = function
     return;
   }
 
   // Special case: if there is only 1 parameter, and that's of type
   // text, then we put all the params into the first param
   if (this.assignmentCount === 1) {
     var assignment = this.getAssignment(0);
     if (assignment.param.type instanceof StringType) {
-      var arg = (args.length === 1) ?
-        args[0] :
-        new MergedArgument(args);
-      var conversion = assignment.param.type.parse(arg);
-      assignment.setConversion(conversion);
+      var arg = (args.length === 1) ? args[0] : new MergedArgument(args);
+      this._setAssignment(assignment, arg, true);
       return;
     }
   }
 
   // Positional arguments can still be specified by name, but if they are
   // then we need to ignore them when working them out positionally
   var unassignedParams = this.getParameterNames();
 
@@ -6634,77 +6628,74 @@ Requisition.prototype._assign = function
           var arrayArg = arrayArgs[assignment.param.name];
           if (!arrayArg) {
             arrayArg = new ArrayArgument();
             arrayArgs[assignment.param.name] = arrayArg;
           }
           arrayArg.addArgument(arg);
         }
         else {
-          var conversion = assignment.param.type.parse(arg);
-          assignment.setConversion(conversion);
+          this._setAssignment(assignment, arg, true);
         }
       }
       else {
         // Skip this parameter and handle as a positional parameter
         i++;
       }
     }
   }, this);
 
   // What's left are positional parameters assign in order
   unassignedParams.forEach(function(name) {
     var assignment = this.getAssignment(name);
 
     // If not set positionally, and we can't set it non-positionally,
     // we have to default it to prevent previous values surviving
     if (!assignment.param.isPositionalAllowed) {
-      assignment.setBlank();
+      this._setAssignment(assignment, null, true);
       return;
     }
 
     // If this is a positional array argument, then it swallows the
     // rest of the arguments.
     if (assignment.param.type instanceof ArrayType) {
       var arrayArg = arrayArgs[assignment.param.name];
       if (!arrayArg) {
         arrayArg = new ArrayArgument();
         arrayArgs[assignment.param.name] = arrayArg;
       }
       arrayArg.addArguments(args);
       args = [];
     }
     else {
       if (args.length === 0) {
-        assignment.setBlank();
+        this._setAssignment(assignment, null, true);
       }
       else {
         var arg = args.splice(0, 1)[0];
         // --foo and -f are named parameters, -4 is a number. So '-' is either
         // the start of a named parameter or a number depending on the context
         var isIncompleteName = assignment.param.type instanceof NumberType ?
             /-[-a-zA-Z_]/.test(arg.text) :
             arg.text.charAt(0) === '-';
 
         if (isIncompleteName) {
           this._unassigned.push(new UnassignedAssignment(this, arg));
         }
         else {
-          var conversion = assignment.param.type.parse(arg);
-          assignment.setConversion(conversion);
+          this._setAssignment(assignment, arg, true);
         }
       }
     }
   }, this);
 
   // Now we need to assign the array argument (if any)
   Object.keys(arrayArgs).forEach(function(name) {
     var assignment = this.getAssignment(name);
-    var conversion = assignment.param.type.parse(arrayArgs[name]);
-    assignment.setConversion(conversion);
+    this._setAssignment(assignment, arrayArgs[name], true);
   }, this);
 
   // What's left is can't be assigned, but we need to extract
   this._addUnassignedArgs(args);
 };
 
 exports.Requisition = Requisition;
 
@@ -6724,27 +6715,41 @@ function Output(options) {
   this.error = false;
   this.start = new Date();
 
   this.onClose = util.createEvent('Output.onClose');
   this.onChange = util.createEvent('Output.onChange');
 }
 
 /**
- * Called when there is data to display
- * @param data
- */
-Output.prototype.complete = function(data) {
+ * Called when there is data to display, but the command is still executing
+ * @param data The new data. If the data structure has been altered but the
+ * root object is still the same, The same root object should be passed in the
+ * data parameter.
+ * @param ev Optional additional event data, for example to explain how the
+ * data structure has changed
+ */
+Output.prototype.changed = function(data, ev) {
   this.data = data;
 
+  ev = ev || {};
+  ev.output = this;
+  this.onChange(ev);
+};
+
+/**
+ * Called when there is data to display, and the command has finished executing
+ * See changed() for details on parameters.
+ */
+Output.prototype.complete = function(data, ev) {
   this.end = new Date();
   this.duration = this.end.getTime() - this.start.getTime();
   this.completed = true;
 
-  this.onChange({ output: this });
+  this.changed(data, ev);
 };
 
 /**
  * Convert to a DOM element for display.
  * @param element The DOM node to which the data should be written. Existing
  * content of 'element' will be removed before 'outputData' is added.
  */
 Output.prototype.toDom = function(element) {
@@ -6825,18 +6830,25 @@ exports.Output = Output;
  */
 exports.createExecutionContext = function(requisition) {
   return {
     exec: requisition.exec.bind(requisition),
     update: requisition.update.bind(requisition),
     document: requisition.document,
     environment: requisition.environment,
     createView: view.createView,
+    defer: function() {
+      return Q.defer();
+    },
+    /**
+     * @deprecated Use defer() instead, which does the same thing, but is not
+     * confusingly named
+     */
     createPromise: function() {
-      return new Promise();
+      return Q.defer();
     }
   };
 };
 
 
 });
 /*
  * Copyright 2012, Mozilla Foundation and contributors
@@ -6851,18 +6863,23 @@ exports.createExecutionContext = functio
  * 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.
  */
 
 define('gcli/promise', ['require', 'exports', 'module' ], function(require, exports, module) {
 
-  Components.utils.import("resource:///modules/devtools/Promise.jsm");
-  exports.Promise = Promise;
+  var imported = {};
+  Components.utils.import("resource://gre/modules/commonjs/promise/core.js",
+                          imported);
+
+  exports.defer = imported.Promise.defer;
+  exports.resolve = imported.Promise.resolve;
+  exports.reject = imported.Promise.reject;
 
 });
 define("text!gcli/ui/intro.html", [], "\n" +
   "<div>\n" +
   "  <p>${l10n.introTextOpening}</p>\n" +
   "\n" +
   "  <p>\n" +
   "    ${l10n.introTextCommands}\n" +
@@ -7232,17 +7249,17 @@ FocusManager.prototype._checkShow = func
   var showOutput = this._shouldShowOutput();
   if (this.isOutputVisible !== showOutput.visible) {
     ev.outputVisible = this.isOutputVisible = showOutput.visible;
     fire = true;
   }
 
   if (fire) {
     if (this._debug) {
-      console.debug('FocusManager.onVisibilityChange', ev);
+      console.log('FocusManager.onVisibilityChange', ev);
     }
     this.onVisibilityChange(ev);
   }
 };
 
 /**
  * Calculate if we should be showing or hidden taking into account all the
  * available inputs
@@ -7730,16 +7747,19 @@ Field.prototype.element = undefined;
 
 /**
  * Indicates that this field should drop any resources that it has created
  */
 Field.prototype.destroy = function() {
   delete this.messageElement;
 };
 
+// Note: We could/should probably change Fields from working with Conversions
+// to working with Arguments (Tokens), which makes for less calls to parse()
+
 /**
  * Update this field display with the value from this conversion.
  * Subclasses should provide an implementation of this function.
  */
 Field.prototype.setConversion = function(conversion) {
   throw new Error('Field should not be used directly');
 };
 
@@ -9388,17 +9408,19 @@ Inputter.prototype.textChanged = functio
   }
 
   var newStr = this.requisition.toString();
   var input = this.getInputState();
 
   input.typed = newStr;
   this._processCaretChange(input);
 
-  this.element.value = newStr;
+  if (this.element.value !== newStr) {
+    this.element.value = newStr;
+  }
   this.onInputChange({ inputState: input });
 };
 
 /**
  * Various ways in which we need to manipulate the caret/selection position.
  * A value of null means we're not expecting a change
  */
 var Caret = {
@@ -9464,18 +9486,22 @@ Inputter.prototype._processCaretChange =
   start = (start > input.typed.length) ? input.typed.length : start;
   end = (end > input.typed.length) ? input.typed.length : end;
 
   var newInput = {
     typed: input.typed,
     cursor: { start: start, end: end }
   };
 
-  this.element.selectionStart = start;
-  this.element.selectionEnd = end;
+  if (this.element.selectionStart !== start) {
+    this.element.selectionStart = start;
+  }
+  if (this.element.selectionEnd !== end) {
+    this.element.selectionEnd = end;
+  }
 
   this._checkAssignment(start);
 
   this._caretChange = null;
   return newInput;
 };
 
 /**
@@ -9600,17 +9626,17 @@ Inputter.prototype.onKeyUp = function(ev
     else if (this.element.value === '' || this._scrollingThroughHistory) {
       this._scrollingThroughHistory = true;
       this.requisition.update(this.history.backward());
     }
     else {
       // If the user is on a valid value, then we increment the value, but if
       // they've typed something that's not right we page through predictions
       if (this.assignment.getStatus() === Status.VALID) {
-        this.requisition.increment(assignment);
+        this.requisition.increment(this.assignment);
         // See notes on focusManager.onInputChange in onKeyDown
         if (this.focusManager) {
           this.focusManager.onInputChange();
         }
       }
       else {
         this.changeChoice(-1);
       }
@@ -9624,17 +9650,17 @@ Inputter.prototype.onKeyUp = function(ev
     }
     else if (this.element.value === '' || this._scrollingThroughHistory) {
       this._scrollingThroughHistory = true;
       this.requisition.update(this.history.forward());
     }
     else {
       // See notes above for the UP key
       if (this.assignment.getStatus() === Status.VALID) {
-        this.requisition.decrement(assignment);
+        this.requisition.decrement(this.assignment);
         // See notes on focusManager.onInputChange in onKeyDown
         if (this.focusManager) {
           this.focusManager.onInputChange();
         }
       }
       else {
         this.changeChoice(+1);
       }
@@ -10309,17 +10335,17 @@ Tooltip.prototype.selectChoice = functio
   }
   return false;
 };
 
 /**
  * Called by the onFieldChange event on the current Field
  */
 Tooltip.prototype.fieldChanged = function(ev) {
-  this.assignment.setConversion(ev.conversion);
+  this.requisition.setAssignment(this.assignment, ev.conversion.arg);
 
   var isError = ev.conversion.message != null && ev.conversion.message !== '';
   this.focusManager.setError(isError);
 
   // Nasty hack, the inputter won't know about the text change yet, so it will
   // get it's calculations wrong. We need to wait until the current set of
   // changes has had a chance to propagate
   this.document.defaultView.setTimeout(function() {
--- a/browser/devtools/commandline/test/browser_cmd_pref.js
+++ b/browser/devtools/commandline/test/browser_cmd_pref.js
@@ -158,18 +158,18 @@ function testPrefStatus() {
       setting: { arg: ' devtools.editor.tabsize' },
       value: { value: 4 },
     }
   });
 
   helpers.setInput('pref list');
   helpers.check({
     input:  'pref list',
-    hints:           '',
-    markup: 'EEEEVEEEE',
+    hints:           ' -> pref set',
+    markup: 'IIIIVIIII',
     status: 'ERROR'
   });
 }
 
 function testPrefSetEnable() {
   DeveloperToolbarTest.exec({
     typed: "pref set devtools.editor.tabsize 9",
     args: {
--- a/browser/devtools/commandline/test/browser_gcli_web.js
+++ b/browser/devtools/commandline/test/browser_gcli_web.js
@@ -103,33 +103,33 @@ define('gclitest/index', ['require', 'ex
   };
 
   /**
    * A simple proxy to examiner.run, for convenience - this is run from the
    * top level.
    * @param options Lookup of options that customize test running. Includes:
    * - window (default=undefined) A reference to the DOM window. If left
    *   undefined then a reduced set of tests will run.
-   * - isNode (default=false) Are we running under NodeJS, specifically, are we
-   *   using JSDom, which isn't a 100% complete DOM implementation.
+   * - isJsdom (default=false) Are we running under JSDom, specifically, which
+   *   isn't a 100% complete DOM implementation.
    *   Some tests are skipped when using NodeJS.
    * - display (default=undefined) A reference to a Display implementation.
    *   A reduced set of tests will run if left undefined
    * - detailedResultLog (default=false) do we output a test summary to
    *   |console.log| on test completion.
    * - hideExec (default=false) Set the |hidden| property in calls to
    *   |requisition.exec()| which prevents the display from becoming messed up,
    *   however use of hideExec restricts the set of tests that are run
    */
-  exports.run = function(options) {
+  exports.runAsync = function(options, callback) {
     options = options || {};
     examiner.mergeDefaultOptions(options);
 
     examiner.reset();
-    examiner.run(options);
+    examiner.runAsync(options, callback);
 
     // A better set of default than those specified above, come from the set
     // that are passed to run().
     examiner.defaultOptions = {
       window: options.window,
       display: options.display,
       hideExec: options.hideExec
     };
@@ -150,23 +150,16 @@ define('gclitest/index', ['require', 'ex
       settings.setDefaults(options.settings);
     }
 
     window.display = new Display(options);
     var requisition = window.display.requisition;
 
     // setTimeout keeps stack traces clear of RequireJS frames
     window.setTimeout(function() {
-      var options = {
-        window: window,
-        display: window.display,
-        hideExec: true
-      };
-      exports.run(options);
-
       window.createDebugCheck = function() {
         require([ 'gclitest/helpers' ], function(helpers) {
           helpers.setup(options);
           console.log(helpers._createDebugCheck());
           helpers.shutdown(options);
         });
       };
 
@@ -193,17 +186,25 @@ define('gclitest/index', ['require', 'ex
         }
       }, true);
 
       window.testCommands = function() {
         require([ 'gclitest/mockCommands' ], function(mockCommands) {
           mockCommands.setup();
         });
       };
-      window.testCommands();
+
+      var options = {
+        window: window,
+        display: window.display,
+        hideExec: true
+      };
+      exports.runAsync(options, function() {
+        window.testCommands();
+      });
     }, 10);
 
     return {
       /**
        * The exact shape of the object returned by exec is likely to change in
        * the near future. If you do use it, please expect your code to break.
        */
       exec: requisition.exec.bind(requisition),
@@ -322,35 +323,16 @@ examiner.mergeDefaultOptions = function(
   Object.keys(examiner.defaultOptions).forEach(function(name) {
     if (options[name] == null) {
       options[name] = examiner.defaultOptions[name];
     }
   });
 };
 
 /**
- * Run the tests defined in the test suite synchronously
- */
-examiner.run = function(options) {
-  Object.keys(examiner.suites).forEach(function(suiteName) {
-    var suite = examiner.suites[suiteName];
-    suite.run(options);
-  }.bind(this));
-
-  if (options.detailedResultLog) {
-    examiner.detailedResultLog();
-  }
-  else {
-    console.log('Completed test suite');
-  }
-
-  return examiner.suites;
-};
-
-/**
  * Run all the tests asynchronously
  */
 examiner.runAsync = function(options, callback) {
   this._runAsyncInternal(0, options, callback);
 };
 
 /**
  * Run all the test suits asynchronously
@@ -473,31 +455,16 @@ function Suite(suiteName, suite) {
  */
 Suite.prototype.reset = function() {
   Object.keys(this.tests).forEach(function(testName) {
     this.tests[testName].reset();
   }, this);
 };
 
 /**
- * Run all the tests in this suite synchronously
- */
-Suite.prototype.run = function(options) {
-  if (!this._setup(options)) {
-    return;
-  }
-
-  Object.keys(this.tests).forEach(function(testName) {
-    this.tests[testName].run(options);
-  }, this);
-
-  this._shutdown(options);
-};
-
-/**
  * Run all the tests in this suite asynchronously
  */
 Suite.prototype.runAsync = function(options, callback) {
   if (!this._setup(options)) {
     if (typeof callback === 'function') {
       callback();
     }
     return;
@@ -631,35 +598,42 @@ Suite.prototype._logToAllTests = functio
  * A test represents data about a single test function
  */
 function Test(suite, name, func) {
   this.suite = suite;
   this.name = name;
   this.func = func;
   this.title = name.replace(/^test/, '').replace(/([A-Z])/g, ' $1');
 
+  this.outstanding = [];
+  this.callback = undefined;
+
   this.failures = [];
   this.status = stati.notrun;
   this.checks = 0;
 }
 
 /**
  * Reset the test to its original state
  */
 Test.prototype.reset = function() {
+  this.outstanding = [];
+  this.callback = undefined;
+
   this.failures = [];
   this.status = stati.notrun;
   this.checks = 0;
 };
 
 /**
- * Run just a single test
+ * Run all the tests in this suite asynchronously
  */
-Test.prototype.run = function(options) {
+Test.prototype.runAsync = function(options, callback) {
   assert.currentTest = this;
+  this.callback = callback;
   this.status = stati.executing;
   this.failures = [];
   this.checks = 0;
 
   try {
     this.func.apply(this.suite, [ options ]);
   }
   catch (ex) {
@@ -670,28 +644,30 @@ Test.prototype.run = function(options) {
     }
   }
 
   if (this.status === stati.executing) {
     this.status = stati.pass;
   }
 
   assert.currentTest = null;
+
+  this.checkFinish();
 };
 
 /**
- * Run all the tests in this suite asynchronously
+ * Check to see if the currently executing test is completed (i.e. the list of
+ * outstanding tasks has all been completed)
  */
-Test.prototype.runAsync = function(options, callback) {
-  setTimeout(function() {
-    this.run(options);
-    if (typeof callback === 'function') {
-      callback();
+Test.prototype.checkFinish = function() {
+  if (this.outstanding.length == 0) {
+    if (typeof this.callback === 'function') {
+      this.callback();
     }
-  }.bind(this), delay);
+  }
 };
 
 /**
  * Create a JSON object suitable for serialization
  */
 Test.prototype.toRemote = function() {
   return {
     name: this.name,
@@ -887,31 +863,35 @@ define('gclitest/testCanon', ['require',
  *
  * 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.
  */
 
-define('gclitest/helpers', ['require', 'exports', 'module' , 'test/assert', 'gcli/util'], function(require, exports, module) {
+define('gclitest/helpers', ['require', 'exports', 'module' , 'test/assert'], function(require, exports, module) {
 
 
 var test = require('test/assert');
-var util = require('gcli/util');
-
+// A copy of this code exists in firefox mochitests; when updated here it
+// should be updated there too. Hence the use of an exports synonym for non
+// AMD contexts.
 var helpers = exports;
 
 helpers._display = undefined;
+helpers._options = undefined;
 
 helpers.setup = function(options) {
+  helpers._options = options;
   helpers._display = options.display;
 };
 
 helpers.shutdown = function(options) {
+  helpers._options = undefined;
   helpers._display = undefined;
 };
 
 /**
  * Various functions to return the actual state of the command line
  */
 helpers._actual = {
   input: function() {
@@ -1020,16 +1000,26 @@ helpers._createDebugCheck = function() {
  * and check() which ensures that things are in the right place afterwards.
  */
 helpers.setInput = function(typed, cursor) {
   helpers._display.inputter.setInput(typed);
 
   if (cursor) {
     helpers._display.inputter.setCursor({ start: cursor, end: cursor });
   }
+  else {
+    // This is a hack because jsdom appears to not handle cursor updates
+    // in the same way as most browsers.
+    if (helpers._options.isJsdom) {
+      helpers._display.inputter.setCursor({
+        start: typed.length,
+        end: typed.length
+      });
+    }
+  }
 
   helpers._display.focusManager.onInputChange();
 };
 
 /**
  * Simulate focusing the input field
  */
 helpers.focusInput = function() {
@@ -1584,17 +1574,17 @@ exports.testSingleNumber = function() {
 exports.testElement = function(options) {
   update({ typed: 'tse', cursor: { start: 3, end: 3 } });
   test.is(        'VVV', statuses);
   test.is(Status.ERROR, status);
   test.is('tse', requ.commandAssignment.value.name);
   test.ok(assign1.arg.type === 'BlankArgument');
   test.is(undefined, assign1.value);
 
-  if (!options.isNode) {
+  if (!options.isJsdom) {
     update({ typed: 'tse :root', cursor: { start: 9, end: 9 } });
     test.is(        'VVVVVVVVV', statuses);
     test.is(Status.VALID, status);
     test.is('tse', requ.commandAssignment.value.name);
     test.is(':root', assign1.arg.text);
     if (!options.window.isFake) {
       test.is(options.window.document.documentElement, assign1.value);
     }
@@ -1620,17 +1610,17 @@ exports.testElement = function(options) 
     // digging into the CSS engine we can't tell that so we default to incomplete
     test.is(        'VVVVIIIIIIIIIIIII', statuses);
     test.is(Status.ERROR, status);
     test.is('tse', requ.commandAssignment.value.name);
     test.is('#gcli-nomatch', assign1.arg.text);
     test.is(undefined, assign1.value);
   }
   else {
-    test.log('Skipping :root test due to jsdom (from isNode)');
+    test.log('Skipping :root test due to jsdom');
   }
 
   update({ typed: 'tse #', cursor: { start: 5, end: 5 } });
   test.is(        'VVVVE', statuses);
   test.is(Status.ERROR, status);
   test.is('tse', requ.commandAssignment.value.name);
   test.is('#', assign1.arg.text);
   test.is(undefined, assign1.value);
@@ -2355,17 +2345,17 @@ exports.testActivate = function(options)
 
   helpers.setInput('tsg b');
   helpers.check({
     hints: 'bb [options]'
   });
 
   helpers.setInput('tsg d');
   helpers.check({
-    hints: ' [options]'
+    hints: ' [options] -> ccc'
   });
 
   helpers.setInput('tsg aa');
   helpers.check({
     hints: 'a [options]'
   });
 
   helpers.setInput('tsg aaa');
@@ -2769,33 +2759,39 @@ var mockDoc = {
 
 });
 /*
  * Copyright 2009-2011 Mozilla Foundation and contributors
  * Licensed under the New BSD license. See LICENSE.txt or:
  * http://opensource.org/licenses/BSD-3-Clause
  */
 
-define('gclitest/testFocus', ['require', 'exports', 'module' , 'gclitest/helpers', 'gclitest/mockCommands'], function(require, exports, module) {
-
-
+define('gclitest/testFocus', ['require', 'exports', 'module' , 'test/assert', 'gclitest/helpers', 'gclitest/mockCommands'], function(require, exports, module) {
+
+
+var test = require('test/assert');
 var helpers = require('gclitest/helpers');
 var mockCommands = require('gclitest/mockCommands');
 
 exports.setup = function(options) {
   mockCommands.setup();
   helpers.setup(options);
 };
 
 exports.shutdown = function(options) {
   mockCommands.shutdown();
   helpers.shutdown(options);
 };
 
 exports.testBasic = function(options) {
+  if (options.isJsdom) {
+    test.log('jsdom does not pass on focus events properly, skipping testBasic');
+    return;
+  }
+
   helpers.focusInput();
   helpers.exec(options, 'help');
 
   helpers.setInput('tsn deep');
   helpers.check({
     input:  'tsn deep',
     hints:          '',
     markup: 'IIIVIIII',
@@ -3338,19 +3334,18 @@ exports.testIncomplete = function(option
   test.is(requisition._unassigned[0].param.type.isIncompleteName, true,
           'unassigned.isIncompleteName: tsg -');
 };
 
 exports.testHidden = function(options) {
   helpers.setInput('tshidde');
   helpers.check({
     input:  'tshidde',
-    markup: 'EEEEEEE',
-    status: 'ERROR',
-    hints:  '',
+    hints:         ' -> tse',
+    status: 'ERROR'
   });
 
   helpers.setInput('tshidden');
   helpers.check({
     input:  'tshidden',
     hints:          ' [options]',
     markup: 'VVVVVVVV',
     status: 'VALID',
@@ -3465,32 +3460,33 @@ exports.testHidden = function(options) {
  *
  * 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.
  */
 
-define('gclitest/testIntro', ['require', 'exports', 'module' , 'gclitest/helpers', 'test/assert'], function(require, exports, module) {
+define('gclitest/testIntro', ['require', 'exports', 'module' , 'gclitest/helpers', 'test/assert', 'gcli/canon'], function(require, exports, module) {
 
   var helpers = require('gclitest/helpers');
   var test = require('test/assert');
+  var canon = require('gcli/canon');
 
   exports.setup = function(options) {
     helpers.setup(options);
   };
 
   exports.shutdown = function(options) {
     helpers.shutdown(options);
   };
 
   exports.testIntroStatus = function(options) {
-    if (options.isFirefox) {
-      test.log('Skipping testIntroStatus in Firefox.');
+    if (canon.getCommand('intro') == null) {
+      test.log('Skipping testIntroStatus; missing intro command.');
       return;
     }
 
     helpers.setInput('intro');
     helpers.check({
       typed:  'intro',
       markup: 'VVVVV',
       status: 'VALID',
@@ -3502,18 +3498,18 @@ define('gclitest/testIntro', ['require',
       typed:  'intro foo',
       markup: 'VVVVVVEEE',
       status: 'ERROR',
       hints: ''
     });
   };
 
   exports.testIntroExec = function(options) {
-    if (options.isFirefox) {
-      test.log('Skipping testIntroExec in Firefox.');
+    if (canon.getCommand('intro') == null) {
+      test.log('Skipping testIntroStatus; missing intro command.');
       return;
     }
 
     helpers.exec(options, {
       typed: 'intro',
       args: { },
       outputMatch: [
         /command\s*line/,
@@ -3678,17 +3674,20 @@ exports.testBasic = function(options) {
   check('VVVVVVVVVVVVVVVVVVVVVVV', Status.VALID, 'window.document.title', 0);
 
   input('{ d');
   check('VVI', Status.ERROR, 'd', 'document');
 
   input('{ document.title');
   check('VVVVVVVVVVVVVVVV', Status.VALID, 'document.title', 0);
 
-  test.ok('donteval' in options.window, 'donteval exists');
+  if (!options.isJsdom) {
+    // jsdom causes an eval here, maybe that's node/v8?
+    test.ok('donteval' in options.window, 'donteval exists');
+  }
 
   input('{ don');
   check('VVIII', Status.ERROR, 'don', 'donteval');
 
   input('{ donteval');
   check('VVVVVVVVVV', Status.VALID, 'donteval', 0);
 
   /*
@@ -3827,22 +3826,22 @@ exports.testComplete = function(options)
 
   if (!canon.getCommand('{')) {
     test.log('Skipping exec tests because { is not registered');
   }
   else {
     check('{ wind', COMPLETES_TO, '{ window', 0);
     check('{ window.docum', COMPLETES_TO, '{ window.document', 0);
 
-    // Bug 717228: This fails under node
-    if (!options.isNode) {
+    // Bug 717228: This fails under jsdom
+    if (!options.isJsdom) {
       check('{ window.document.titl', COMPLETES_TO, '{ window.document.title ', 0);
     }
     else {
-      test.log('Running under Node. Skipping tests due to bug 717228.');
+      test.log('Skipping tests due to jsdom and bug 717228.');
     }
   }
 };
 
 exports.testInternalComplete = function(options) {
   // Bug 664377
   // check('tsela 1', COMPLETES_TO, 'tselarr 1', 0, 3, 8);
 };
@@ -4003,71 +4002,76 @@ exports.testNode = function(options) {
         status: 'ERROR',
         message: 'Syntax error in CSS query'
       },
       nodes: { status: 'VALID' },
       nodes2: { status: 'VALID' }
     }
   });
 
-  helpers.setInput('tse :root');
-  helpers.check({
-    input:  'tse :root',
-    hints:           ' [options]',
-    markup: 'VVVVVVVVV',
-    cursor: 9,
-    current: 'node',
-    status: 'VALID',
-    args: {
-      command: { name: 'tse' },
-      node: { arg: ' :root', status: 'VALID' },
-      nodes: { status: 'VALID' },
-      nodes2: { status: 'VALID' }
-    }
-  });
-
-  helpers.setInput('tse :root ');
-  helpers.check({
-    input:  'tse :root ',
-    hints:            '[options]',
-    markup: 'VVVVVVVVVV',
-    cursor: 10,
-    current: 'node',
-    status: 'VALID',
-    args: {
-      command: { name: 'tse' },
-      node: { arg: ' :root ', status: 'VALID' },
-      nodes: { status: 'VALID' },
-      nodes2: { status: 'VALID' }
-    }
-  });
-  test.is(requisition.getAssignment('node').value.tagName,
-          'HTML',
-          'root id');
-
-  helpers.setInput('tse #gcli-nomatch');
-  helpers.check({
-    input:  'tse #gcli-nomatch',
-    hints:                   ' [options]',
-    markup: 'VVVVIIIIIIIIIIIII',
-    cursor: 17,
-    current: 'node',
-    status: 'ERROR',
-    args: {
-      command: { name: 'tse' },
-      node: {
-        value: undefined,
-        arg: ' #gcli-nomatch',
-        status: 'INCOMPLETE',
-        message: 'No matches'
-      },
-      nodes: { status: 'VALID' },
-      nodes2: { status: 'VALID' }
-    }
-  });
+  if (options.isJsdom) {
+    test.log('skipping node tests because jsdom');
+  }
+  else {
+    helpers.setInput('tse :root');
+    helpers.check({
+      input:  'tse :root',
+      hints:           ' [options]',
+      markup: 'VVVVVVVVV',
+      cursor: 9,
+      current: 'node',
+      status: 'VALID',
+      args: {
+        command: { name: 'tse' },
+        node: { arg: ' :root', status: 'VALID' },
+        nodes: { status: 'VALID' },
+        nodes2: { status: 'VALID' }
+      }
+    });
+
+    helpers.setInput('tse :root ');
+    helpers.check({
+      input:  'tse :root ',
+      hints:            '[options]',
+      markup: 'VVVVVVVVVV',
+      cursor: 10,
+      current: 'node',
+      status: 'VALID',
+      args: {
+        command: { name: 'tse' },
+        node: { arg: ' :root ', status: 'VALID' },
+        nodes: { status: 'VALID' },
+        nodes2: { status: 'VALID' }
+      }
+    });
+    test.is(requisition.getAssignment('node').value.tagName,
+            'HTML',
+            'root id');
+
+    helpers.setInput('tse #gcli-nomatch');
+    helpers.check({
+      input:  'tse #gcli-nomatch',
+      hints:                   ' [options]',
+      markup: 'VVVVIIIIIIIIIIIII',
+      cursor: 17,
+      current: 'node',
+      status: 'ERROR',
+      args: {
+        command: { name: 'tse' },
+        node: {
+          value: undefined,
+          arg: ' #gcli-nomatch',
+          status: 'INCOMPLETE',
+          message: 'No matches'
+        },
+        nodes: { status: 'VALID' },
+        nodes2: { status: 'VALID' }
+      }
+    });
+  }
 
   helpers.setInput('tse #');
   helpers.check({
     input:  'tse #',
     hints:       ' [options]',
     markup: 'VVVVE',
     cursor: 5,
     current: 'node',
@@ -4126,16 +4130,21 @@ exports.testNode = function(options) {
       nodes2: { status: 'VALID' }
     }
   });
 };
 
 exports.testNodes = function(options) {
   var requisition = options.display.requisition;
 
+  if (options.isJsdom) {
+    test.log('skipping node tests because jsdom');
+    return;
+  }
+
   helpers.setInput('tse :root --nodes *');
   helpers.check({
     input:  'tse :root --nodes *',
     hints:                       ' [options]',
     markup: 'VVVVVVVVVVVVVVVVVVV',
     current: 'nodes',
     status: 'VALID',
     args: {
@@ -4231,23 +4240,24 @@ exports.testNodes = function(options) {
  *
  * 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.
  */
 
-define('gclitest/testPref', ['require', 'exports', 'module' , 'gcli/commands/pref', 'gclitest/helpers', 'gclitest/mockSettings', 'test/assert'], function(require, exports, module) {
+define('gclitest/testPref', ['require', 'exports', 'module' , 'gcli/commands/pref', 'gclitest/helpers', 'gclitest/mockSettings', 'test/assert', 'gcli/canon'], function(require, exports, module) {
 
 
 var pref = require('gcli/commands/pref');
 var helpers = require('gclitest/helpers');
 var mockSettings = require('gclitest/mockSettings');
 var test = require('test/assert');
+var canon = require('gcli/canon');
 
 
 exports.setup = function(options) {
   helpers.setup(options);
 
   if (!options.isFirefox) {
     mockSettings.setup();
   }
@@ -4265,16 +4275,21 @@ exports.shutdown = function(options) {
 };
 
 exports.testPrefShowStatus = function(options) {
   if (options.isFirefox) {
     test.log('Skipping testPrefShowStatus in Firefox.');
     return;
   }
 
+  if (canon.getCommand('intro') == null) {
+    test.log('Skipping testIntroStatus; missing intro command.');
+    return;
+  }
+
   helpers.setInput('pref s');
   helpers.check({
     typed:  'pref s',
     hints:        'et',
     markup: 'IIIIVI',
     status: 'ERROR'
   });
 
@@ -4328,16 +4343,21 @@ exports.testPrefShowStatus = function(op
 };
 
 exports.testPrefSetStatus = function(options) {
   if (options.isFirefox) {
     test.log('Skipping testPrefSetStatus in Firefox.');
     return;
   }
 
+  if (canon.getCommand('intro') == null) {
+    test.log('Skipping testIntroStatus; missing intro command.');
+    return;
+  }
+
   helpers.setInput('pref s');
   helpers.check({
     typed:  'pref s',
     hints:        'et',
     markup: 'IIIIVI',
     status: 'ERROR',
   });
 
@@ -4347,17 +4367,17 @@ exports.testPrefSetStatus = function(opt
     hints:          ' <setting> <value>',
     markup: 'VVVVVVVV',
     status: 'ERROR'
   });
 
   helpers.setInput('pref xxx');
   helpers.check({
     typed:  'pref xxx',
-    markup: 'EEEEVEEE',
+    markup: 'IIIIVIII',
     status: 'ERROR'
   });
 
   helpers.setInput('pref set ');
   helpers.check({
     typed:  'pref set ',
     hints:           'allowSet <value>',
     markup: 'VVVVVVVVV',
@@ -4390,16 +4410,21 @@ exports.testPrefSetStatus = function(opt
 };
 
 exports.testPrefExec = function(options) {
   if (options.isFirefox) {
     test.log('Skipping testPrefExec in Firefox.');
     return;
   }
 
+  if (canon.getCommand('intro') == null) {
+    test.log('Skipping testIntroStatus; missing intro command.');
+    return;
+  }
+
   var initialAllowSet = pref.allowSet.value;
   pref.allowSet.value = false;
 
   test.is(mockSettings.tempNumber.value, 42, 'set to 42');
 
   helpers.exec(options, {
     typed: 'pref set tempNumber 4',
     args: {
@@ -4739,22 +4764,21 @@ exports.testPredictions = function(optio
   test.ok(options2.length > 1, 'have resources');
   options2.forEach(function(prediction) {
     checkPrediction(resource2, prediction);
   });
 
   var resource3 = types.getType({ name: 'resource', include: 'text/css' });
   var options3 = resource3.getLookup();
   // jsdom fails to support digging into stylesheets
-  if (!options.isNode) {
+  if (!options.isJsdom) {
     test.ok(options3.length >= 1, 'have resources');
   }
   else {
-    test.log('Running under Node. ' +
-             'Skipping checks due to jsdom document.stylsheets support.');
+    test.log('Skipping checks due to jsdom document.stylsheets support.');
   }
   options3.forEach(function(prediction) {
     checkPrediction(resource3, prediction);
   });
 
   var resource4 = types.getType({ name: 'resource' });
   var options4 = resource4.getLookup();
 
@@ -5013,39 +5037,38 @@ exports.testChange = function(options) {
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 define('gclitest/testSpell', ['require', 'exports', 'module' , 'test/assert', 'gcli/types/spell'], function(require, exports, module) {
 
 var test = require('test/assert');
-var Speller = require('gcli/types/spell').Speller;
+var spell = require('gcli/types/spell');
 
 exports.setup = function() {
 };
 
 exports.shutdown = function() {
 };
 
 exports.testSpellerSimple = function(options) {
   if (options.isFirefox) {
     test.log('Skipping testPref in Firefox.');
     return;
   }
 
-  var speller = new Speller();
-  speller.train(Object.keys(options.window));
-
-  test.is(speller.correct('document'), 'document');
-  test.is(speller.correct('documen'), 'document');
-  test.is(speller.correct('ocument'), 'document');
-  test.is(speller.correct('odcument'), 'document');
-
-  test.is(speller.correct('========='), null);
+  var alternatives = Object.keys(options.window);
+
+  test.is(spell.correct('document', alternatives), 'document');
+  test.is(spell.correct('documen', alternatives), 'document');
+  test.is(spell.correct('ocument', alternatives), 'document');
+  test.is(spell.correct('odcument', alternatives), 'document');
+
+  test.is(spell.correct('=========', alternatives), undefined);
 };
 
 
 });
 /*
  * Copyright 2012, Mozilla Foundation and contributors
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -5453,17 +5476,17 @@ function type(typed, tests, options) {
   var inputter = options.display.inputter;
   var tooltip = options.display.tooltip;
 
   inputter.setInput(typed);
   if (tests.cursor) {
     inputter.setCursor({ start: tests.cursor, end: tests.cursor });
   }
 
-  if (!options.isNode) {
+  if (!options.isJsdom) {
     if (tests.important) {
       test.ok(tooltip.field.isImportant, 'Important for ' + typed);
     }
     else {
       test.ok(!tooltip.field.isImportant, 'Not important for ' + typed);
     }
 
     if (tests.options) {
@@ -5483,39 +5506,37 @@ function type(typed, tests, options) {
 }
 
 exports.testActivate = function(options) {
   if (!options.display) {
     test.log('No display. Skipping activate tests');
     return;
   }
 
-  if (options.isNode) {
-    test.log('Running under Node. Reduced checks due to JSDom.textContent');
+  if (options.isJsdom) {
+    test.log('Reduced checks due to JSDom.textContent');
   }
 
   type(' ', { }, options);
 
   type('tsb ', {
     important: true,
     options: [ 'false', 'true' ]
   }, options);
 
   type('tsb t', {
     important: true,
     options: [ 'true' ]
   }, options);
 
   type('tsb tt', {
     important: true,
-    options: [ ],
-    error: 'Can\'t use \'tt\'.'
+    options: [ 'true' ]
   }, options);
 
-
   type('asdf', {
     important: false,
     options: [ ],
     error: 'Can\'t use \'asdf\'.'
   }, options);
 
   type('', { }, options);
 };
@@ -5567,19 +5588,18 @@ function forEachType(options, callback) 
     }
 
     var type = types.getType(options);
     callback(type);
   });
 }
 
 exports.testDefault = function(options) {
-  if (options.isNode) {
-    test.log('Running under Node. ' +
-             'Skipping tests due to issues with resource type.');
+  if (options.isJsdom) {
+    test.log('Skipping tests due to issues with resource type.');
     return;
   }
 
   forEachType({}, function(type) {
     var blank = type.getBlank().value;
 
     // boolean and array types are exempt from needing undefined blank values
     if (type.name === 'boolean') {
--- a/browser/devtools/debugger/test/browser_dbg_displayName.html
+++ b/browser/devtools/debugger/test/browser_dbg_displayName.html
@@ -9,18 +9,21 @@ var a = function() {
   return function() {
     debugger;
   }
 }
 
 var anon = a();
 anon.displayName = "anonFunc";
 
+var inferred = a();
+
 function evalCall() {
   eval("anon();");
+  eval("inferred();");
 }
 
 </script>
 </head>
 
 <body></body>
 
 </html>
--- a/browser/devtools/debugger/test/browser_dbg_displayName.js
+++ b/browser/devtools/debugger/test/browser_dbg_displayName.js
@@ -1,13 +1,16 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+// Tests that anonymous functions appear in the stack frame list with either
+// their displayName property or a SpiderMonkey-inferred name.
+
 var gPane = null;
 var gTab = null;
 var gDebuggee = null;
 var gDebugger = null;
 
 const TAB_URL = EXAMPLE_URL + "browser_dbg_displayName.html";
 
 function test() {
@@ -31,21 +34,43 @@ function testAnonCall() {
         "Should only be getting stack frames while paused.");
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 3,
         "Should have three frames.");
 
       is(frames.querySelector("#stackframe-0 .dbg-stackframe-name").getAttribute("value"),
         "anonFunc", "Frame name should be anonFunc");
 
+      testInferredName();
+    }}, 0);
+  });
+
+  gDebuggee.evalCall();
+}
+
+function testInferredName() {
+  gDebugger.DebuggerController.activeThread.addOneTimeListener("framesadded", function() {
+    Services.tm.currentThread.dispatch({ run: function() {
+
+      let frames = gDebugger.DebuggerView.StackFrames._frames;
+
+      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(frames.querySelector("#stackframe-0 .dbg-stackframe-name").getAttribute("value"),
+        "a/<", "Frame name should be a/<");
+
       resumeAndFinish();
     }}, 0);
   });
 
-  gDebuggee.evalCall();
+  gDebugger.DebuggerController.activeThread.resume();
 }
 
 function resumeAndFinish() {
   gDebugger.DebuggerController.activeThread.resume(function() {
     removeTab(gTab);
     gPane = null;
     gDebuggee = null;
     finish();
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -1,19 +1,19 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 let tempScope = {};
-Cu.import("resource:///modules/devtools/dbg-server.jsm", tempScope);
-Cu.import("resource:///modules/devtools/dbg-client.jsm", tempScope);
-Cu.import("resource:///modules/Services.jsm", tempScope);
+Cu.import("resource://gre/modules/devtools/dbg-server.jsm", tempScope);
+Cu.import("resource://gre/modules/devtools/dbg-client.jsm", tempScope);
+Cu.import("resource://gre/modules/Services.jsm", tempScope);
 let DebuggerServer = tempScope.DebuggerServer;
 let DebuggerTransport = tempScope.DebuggerTransport;
 let DebuggerClient = tempScope.DebuggerClient;
 let Services = tempScope.Services;
 
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/debugger/test/";
 
 const TAB1_URL = EXAMPLE_URL + "browser_dbg_tab1.html";
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -30,16 +30,17 @@ Cu.import("resource://gre/modules/jsdebu
 const SCRATCHPAD_CONTEXT_CONTENT = 1;
 const SCRATCHPAD_CONTEXT_BROWSER = 2;
 const SCRATCHPAD_L10N = "chrome://browser/locale/devtools/scratchpad.properties";
 const DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
 const PREF_RECENT_FILES_MAX = "devtools.scratchpad.recentFilesMax";
 const BUTTON_POSITION_SAVE = 0;
 const BUTTON_POSITION_CANCEL = 1;
 const BUTTON_POSITION_DONT_SAVE = 2;
+const BUTTON_POSITION_REVERT=0;
 
 let keysbundle = Services.strings.createBundle("chrome://global-platform/locale/platformKeys.properties");
 
 
 function SP_Pretty_Key(aElemKey) {
 
   let elemString = "";
   let elemMod = aElemKey.getAttribute("modifiers");
@@ -918,16 +919,82 @@ var Scratchpad = {
         if (aCallback) {
           aCallback(aStatus);
         }
       });
     }
   },
 
   /**
+   * Restore content from saved version of current file.
+   *
+   * @param function aCallback
+   *        Optional function you want to call when file is saved
+   */
+  revertFile: function SP_revertFile(aCallback)
+  {
+    
+    let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+    file.initWithPath(this.filename);
+
+    if (!file.exists()) {
+      return;
+    }
+
+    this.importFromFile(file, false, function(aStatus, aContent) {
+      if (aCallback) {
+        aCallback(aStatus);
+      }
+    });
+  },
+
+  /**
+   * Prompt to revert scratchpad if it has unsaved changes.
+   *
+   * @param function aCallback
+   *        Optional function you want to call when file is saved. The callback
+   *        receives three arguments:
+   *          - aRevert (boolean) - tells if the file has been reverted.
+   *          - status (number) - the file revert status result (if the file was
+   *          saved).
+   */
+  promptRevert: function SP_promptRervert(aCallback)
+  {
+    if (this.filename) {
+      let ps = Services.prompt;
+      let flags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_REVERT +
+                  ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL;
+
+      let button = ps.confirmEx(window,
+                          this.strings.GetStringFromName("confirmRevert.title"),
+                          this.strings.GetStringFromName("confirmRevert"),
+                          flags, null, null, null, null, {});
+      if (button == BUTTON_POSITION_CANCEL) {
+        if (aCallback) {
+          aCallback(false);
+        }
+
+        return;
+      }
+      if (button == BUTTON_POSITION_REVERT) {
+        this.revertFile(function(aStatus) {
+          if(aCallback){
+            aCallback(true, aStatus);
+          }
+        });
+
+        return;
+      }
+    }
+    if (aCallback) {
+      aCallback(false);
+    }
+  },
+
+  /**
    * Open the Error Console.
    */
   openErrorConsole: function SP_openErrorConsole()
   {
     this.browserWindow.toJavaScriptConsole();
   },
 
   /**
@@ -1102,16 +1169,24 @@ var Scratchpad = {
    * @private
    * @see SourceEditor.EVENTS.DIRTY_CHANGED
    * @param object aEvent
    *        The DirtyChanged event object.
    */
   _onDirtyChanged: function SP__onDirtyChanged(aEvent)
   {
     Scratchpad._updateTitle();
+    if (Scratchpad.filename) {
+      if (Scratchpad.editor.dirty) {
+        document.getElementById("sp-cmd-revert").removeAttribute("disabled");
+      }
+      else {
+        document.getElementById("sp-cmd-revert").setAttribute("disabled", true);
+      }
+    }
   },
 
   /**
    * Undo the last action of the user.
    */
   undo: function SP_undo()
   {
     this.editor.undo();
--- a/browser/devtools/scratchpad/scratchpad.xul
+++ b/browser/devtools/scratchpad/scratchpad.xul
@@ -28,16 +28,17 @@
 <commandset id="sourceEditorCommands"/>
 
 <commandset id="sp-commandset">
   <command id="sp-cmd-newWindow" oncommand="Scratchpad.openScratchpad();"/>
   <command id="sp-cmd-openFile" oncommand="Scratchpad.openFile();"/>
   <command id="sp-cmd-clearRecentFiles" oncommand="Scratchpad.clearRecentFiles();"/>
   <command id="sp-cmd-save" oncommand="Scratchpad.saveFile();"/>
   <command id="sp-cmd-saveas" oncommand="Scratchpad.saveFileAs();"/>
+  <command id="sp-cmd-revert" oncommand="Scratchpad.promptRevert();" disabled="true"/>
 
   <!-- TODO: bug 650340 - implement printFile()
   <command id="sp-cmd-printFile" oncommand="Scratchpad.printFile();" disabled="true"/>
  -->
 
   <command id="sp-cmd-close" oncommand="Scratchpad.close();"/>
   <command id="sp-cmd-run" oncommand="Scratchpad.run();"/>
   <command id="sp-cmd-inspect" oncommand="Scratchpad.inspect();"/>
@@ -127,16 +128,20 @@
                 label="&saveFileCmd.label;"
                 accesskey="&saveFileCmd.accesskey;"
                 key="sp-key-save"
                 command="sp-cmd-save"/>
       <menuitem id="sp-menu-saveas"
                 label="&saveFileAsCmd.label;"
                 accesskey="&saveFileAsCmd.accesskey;"
                 command="sp-cmd-saveas"/>
+      <menuitem id="sp-menu-revert"
+                label="&revertCmd.label;"
+                accesskey="&revertCmd.accesskey;"
+                command="sp-cmd-revert"/>
       <menuseparator/>
 
       <!-- TODO: bug 650340 - implement printFile
       <menuitem id="sp-menu-print"
                 label="&printCmd.label;"
                 accesskey="&printCmd.accesskey;"
                 command="sp-cmd-printFile"/>
       <menuseparator/>
--- a/browser/devtools/scratchpad/test/Makefile.in
+++ b/browser/devtools/scratchpad/test/Makefile.in
@@ -28,11 +28,12 @@ MOCHITEST_BROWSER_FILES = \
 		browser_scratchpad_bug_653427_confirm_close.js \
 		browser_scratchpad_bug684546_reset_undo.js \
 		browser_scratchpad_bug690552_display_outputs_errors.js \
 		browser_scratchpad_bug650345_find_ui.js \
 		browser_scratchpad_bug714942_goto_line_ui.js \
 		browser_scratchpad_bug_650760_help_key.js \
 		browser_scratchpad_bug_651942_recent_files.js \
 		browser_scratchpad_bug756681_display_non_error_exceptions.js \
+		browser_scratchpad_bug_751744_revert_to_saved.js \
 		head.js \
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug_751744_revert_to_saved.js
@@ -0,0 +1,137 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   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 nsIFiles.
+let gFile;
+
+// Temporary file name.
+let gFileName = "testFileForBug751744.tmp"
+
+
+// Content for the temporary file.
+let gFileContent = "/* this file is already saved */\n" +
+                   "function foo() { alert('bar') }";
+let gLength = gFileContent.length;
+
+// Reference to the menu entry.
+let menu;
+
+function startTest()
+{
+  gScratchpad = gScratchpadWindow.Scratchpad;
+  menu = gScratchpadWindow.document.getElementById("sp-menu-revert");
+  createAndLoadTemporaryFile();
+}
+
+function testAfterSaved() {
+  // Check if the revert menu is disabled as the file is at saved state.
+  ok(menu.hasAttribute("disabled"), "The revert menu entry is disabled.");
+
+  // chancging the text in the file
+  gScratchpad.setText("\nfoo();", gLength, gLength);
+  // Checking the text got changed
+  is(gScratchpad.getText(), gFileContent + "\nfoo();",
+     "The text changed the first time.");
+
+  // Revert menu now should be enabled.
+  ok(!menu.hasAttribute("disabled"),
+     "The revert menu entry is enabled after changing text first time");
+
+  // reverting back to last saved state.
+  gScratchpad.revertFile(testAfterRevert);
+}
+
+function testAfterRevert() {
+  // Check if the file's text got reverted
+  is(gScratchpad.getText(), gFileContent,
+     "The text reverted back to original text.");
+  // The revert menu should be disabled again.
+  ok(menu.hasAttribute("disabled"),
+     "The revert menu entry is disabled after reverting.");
+
+  // chancging the text in the file again
+  gScratchpad.setText("\nalert(foo.toSource());", gLength, gLength);
+  // Saving the file.
+  gScratchpad.saveFile(testAfterSecondSave);
+}
+
+function testAfterSecondSave() {
+  // revert menu entry should be disabled.
+  ok(menu.hasAttribute("disabled"),
+     "The revert menu entry is disabled after saving.");
+
+  // changing the text.
+  gScratchpad.setText("\nfoo();", gLength + 23, gLength + 23);
+
+  // revert menu entry should get enabled yet again.
+  ok(!menu.hasAttribute("disabled"),
+     "The revert menu entry is enabled after changing text third time");
+
+  // reverting back to last saved state.
+  gScratchpad.revertFile(testAfterSecondRevert);
+}
+
+function testAfterSecondRevert() {
+  // Check if the file's text got reverted
+  is(gScratchpad.getText(), gFileContent + "\nalert(foo.toSource());",
+     "The text reverted back to the changed saved text.");
+  // The revert menu should be disabled again.
+  ok(menu.hasAttribute("disabled"),
+     "Revert menu entry is disabled after reverting to changed saved state.");
+  gFile.remove(false);
+  gFile = null;
+  gScratchpad = null;
+}
+
+function createAndLoadTemporaryFile()
+{
+  // Create a temporary file.
+  gFile = FileUtils.getFile("TmpD", [gFileName]);
+  gFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
+
+  // Write the temporary file.
+  let fout = Cc["@mozilla.org/network/file-output-stream;1"].
+             createInstance(Ci.nsIFileOutputStream);
+  fout.init(gFile.QueryInterface(Ci.nsILocalFile), 0x02 | 0x08 | 0x20,
+            0644, fout.DEFER_OPEN);
+
+  let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
+                  createInstance(Ci.nsIScriptableUnicodeConverter);
+  converter.charset = "UTF-8";
+  let fileContentStream = converter.convertToInputStream(gFileContent);
+
+  NetUtil.asyncCopy(fileContentStream, fout, tempFileSaved);
+}
+
+function tempFileSaved(aStatus)
+{
+  ok(Components.isSuccessCode(aStatus),
+     "the temporary file was saved successfully");
+
+  // Import the file into Scratchpad.
+  gScratchpad.setFilename(gFile.path);
+  gScratchpad.importFromFile(gFile.QueryInterface(Ci.nsILocalFile),  true,
+                             testAfterSaved);
+}
+
+function test()
+{
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+    openScratchpad(startTest);
+  }, true);
+
+  content.location = "data:text/html,<p>test reverting to last saved state of" +
+                     " a file </p>";
+}
--- a/browser/devtools/shared/DeveloperToolbar.jsm
+++ b/browser/devtools/shared/DeveloperToolbar.jsm
@@ -10,16 +10,18 @@ const NS_XHTML = "http://www.w3.org/1999
 
 const WEBCONSOLE_CONTENT_SCRIPT_URL =
   "chrome://browser/content/devtools/HUDService-content.js";
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource:///modules/devtools/Commands.jsm");
 
+const Node = Components.interfaces.nsIDOMNode;
+
 XPCOMUtils.defineLazyModuleGetter(this, "console",
                                   "resource://gre/modules/devtools/Console.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "gcli",
                                   "resource:///modules/devtools/gcli.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "CmdCommands",
                                   "resource:///modules/devtools/CmdCmd.jsm");
@@ -133,16 +135,38 @@ DeveloperToolbar.prototype.focus = funct
   if (this.visible) {
     this._input.focus();
   } else {
     this.show(true);
   }
 };
 
 /**
+ * Called from browser.xul in response to menu-click or keyboard shortcut to
+ * toggle the toolbar
+ */
+DeveloperToolbar.prototype.focusToggle = function DT_focusToggle()
+{
+  if (this.visible) {
+    // If we have focus then the active element is the HTML input contained
+    // inside the xul input element
+    var active = this._chromeWindow.document.activeElement;
+    var position = this._input.compareDocumentPosition(active);
+    if (position & Node.DOCUMENT_POSITION_CONTAINED_BY) {
+      this.hide();
+    }
+    else {
+      this._input.focus();
+    }
+  } else {
+    this.show(true);
+  }
+};
+
+/**
  * Even if the user has not clicked on 'Got it' in the intro, we only show it
  * once per session.
  * Warning this is slightly messed up because this.DeveloperToolbar is not the
  * same as this.DeveloperToolbar when in browser.js context.
  */
 DeveloperToolbar.introShownThisSession = false;
 
 /**
--- a/browser/devtools/styleeditor/StyleEditor.jsm
+++ b/browser/devtools/styleeditor/StyleEditor.jsm
@@ -754,16 +754,97 @@ StyleEditor.prototype = {
         break;
       default:
         this._loadSourceFromCache(this.styleSheet.href);
         break;
     }
   },
 
   /**
+   * Decode a CSS source string to unicode according to the character set rules
+   * defined in <http://www.w3.org/TR/CSS2/syndata.html#charset>.
+   *
+   * @param string aString
+   *        Source of a CSS stylesheet, loaded from file or cache.
+   * @param string aChannelCharset
+   *        Charset of the source string if set by the HTTP channel.
+   * @return string
+   *         The CSS string, in unicode.
+   */
+  _decodeCSSCharset: function SE__decodeCSSCharset(aString, aChannelCharset)
+  {
+    // StyleSheet's charset can be specified from multiple sources
+
+    if (aChannelCharset.length > 0) {
+      // step 1 of syndata.html: charset given in HTTP header.
+      return this._convertToUnicode(aString, aChannelCharset);
+    }
+
+    let sheet = this.styleSheet;
+    if (sheet) {
+      // Do we have a @charset rule in the stylesheet?
+      // step 2 of syndata.html (without the BOM check).
+      if (sheet.cssRules) {
+        let rules = sheet.cssRules;
+        if (rules.length
+            && rules.item(0).type == Ci.nsIDOMCSSRule.CHARSET_RULE) {
+          return this._convertToUnicode(aString, rules.item(0).encoding);
+        }
+      }
+
+      if (sheet.ownerNode) {
+        // step 3: see <link charset="…">
+        let linkCharset = sheet.ownerNode.getAttribute("charset");
+        if (linkCharset != null) {
+          return this._convertToUnicode(aString, linkCharset);
+        }
+      }
+
+      // step 4 (1 of 2): charset of referring stylesheet.
+      let parentSheet = sheet.parentStyleSheet;
+      if (parentSheet && parentSheet.cssRules &&
+          parentSheet.cssRules[0].type == Ci.nsIDOMCSSRule.CHARSET_RULE) {
+        return this._convertToUnicode(aString,
+            parentSheet.cssRules[0].encoding);
+      }
+
+      // step 4 (2 of 2): charset of referring document.
+      if (sheet.ownerNode && sheet.ownerNode.ownerDocument.characterSet) {
+        return this._convertToUnicode(aString,
+            sheet.ownerNode.ownerDocument.characterSet);
+      }
+    }
+
+    // step 5: default to utf-8.
+    return this._convertToUnicode(aString, "UTF-8");
+  },
+
+  /**
+   * Convert a given string, encoded in a given character set, to unicode.
+   * @param string aString
+   *        A string.
+   * @param string aCharset
+   *        A character set.
+   * @return string
+   *         A unicode string.
+   */
+  _convertToUnicode: function SE__convertToUnicode(aString, aCharset) {
+    // Decoding primitives.
+    let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+        .createInstance(Ci.nsIScriptableUnicodeConverter);
+
+    try {
+      converter.charset = aCharset;
+      return converter.ConvertToUnicode(aString);
+    } catch(e) {
+      return aString;
+    }
+  },
+
+  /**
    * Load source from a file or file-like resource.
    *
    * @param string aHref
    *        URL for the stylesheet.
    */
   _loadSourceFromFile: function SE__loadSourceFromFile(aHref)
   {
     try {
@@ -785,45 +866,55 @@ StyleEditor.prototype = {
    *
    * @param string aHref
    *        URL for the stylesheet.
    */
   _loadSourceFromCache: function SE__loadSourceFromCache(aHref)
   {
     let channel = Services.io.newChannel(aHref, null, null);
     let chunks = [];
+    let channelCharset = "";
     let streamListener = { // nsIStreamListener inherits nsIRequestObserver
       onStartRequest: function (aRequest, aContext, aStatusCode) {
         if (!Components.isSuccessCode(aStatusCode)) {
           return this._signalError(LOAD_ERROR);
         }
       }.bind(this),
       onDataAvailable: function (aRequest, aContext, aStream, aOffset, aCount) {
+        let channel = aRequest.QueryInterface(Ci.nsIChannel);
+        if (!channelCharset) {
+          channelCharset = channel.contentCharset;
+        }
         chunks.push(NetUtil.readInputStreamToString(aStream, aCount));
       },
       onStopRequest: function (aRequest, aContext, aStatusCode) {
         if (!Components.isSuccessCode(aStatusCode)) {
           return this._signalError(LOAD_ERROR);
         }
 
-        this._onSourceLoad(chunks.join(""));
+        this._onSourceLoad(chunks.join(""), channelCharset);
       }.bind(this)
     };
 
     channel.loadFlags = channel.LOAD_FROM_CACHE;
     channel.asyncOpen(streamListener, null);
   },
 
   /**
    * Called when source has been loaded.
    *
    * @param string aSourceText
+   * @param string aCharset
+   *        Optional. The character set to use. The default is to detect the
+   *        character set following the standard (see
+   *        <http://www.w3.org/TR/CSS2/syndata.html#charset>).
    */
-  _onSourceLoad: function SE__onSourceLoad(aSourceText)
+  _onSourceLoad: function SE__onSourceLoad(aSourceText, aCharset)
   {
+    aSourceText = this._decodeCSSCharset(aSourceText, aCharset || "");
     this._restoreExpando();
     this._state.text = prettifyCSS(aSourceText);
     this._loaded = true;
     this._triggerAction("Load");
   },
 
   /**
    * Create a new style sheet and append it to the content document.
--- a/browser/devtools/styleeditor/test/browser_styleeditor_init.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_init.js
@@ -66,16 +66,20 @@ function testEditorAdded(aChrome, aEdito
 
   if (gEditorAddedCount == 2) {
     finish();
   }
 }
 
 function testFirstStyleSheetEditor(aChrome, aEditor)
 {
+  // Note: the html <link> contains charset="UTF-8".
+  ok(aEditor._state.text.indexOf("\u263a") >= 0,
+     "stylesheet is unicode-aware.");
+
   //testing TESTCASE's simple.css stylesheet
   is(aEditor.styleSheetIndex, 0,
      "first stylesheet is at index 0");
 
   is(aEditor, aChrome.editors[0],
      "first stylesheet corresponds to StyleEditorChrome.editors[0]");
 
   ok(!aEditor.hasFlag("inline"),
--- a/browser/devtools/styleeditor/test/simple.css
+++ b/browser/devtools/styleeditor/test/simple.css
@@ -1,8 +1,9 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+/* ☺ */
 
 body {
   margin: 0;
 }
 
--- a/browser/devtools/styleeditor/test/simple.html
+++ b/browser/devtools/styleeditor/test/simple.html
@@ -1,13 +1,13 @@
 <!doctype html>
 <html>
 <head>
   <title>simple testcase</title>
-  <link rel="stylesheet" type="text/css" media="screen" href="simple.css"/>
+  <link rel="stylesheet" charset="UTF-8" type="text/css" media="screen" href="simple.css"/>
   <style type="text/css">
   body {
     background: white;
   }
 
   div {
     font-size: 4em;
   }
--- a/browser/devtools/styleinspector/test/browser_ruleview_734259_style_editor_link.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_734259_style_editor_link.js
@@ -2,17 +2,17 @@
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let win;
 let doc;
 let contentWindow;
 
 let tempScope = {};
-Cu.import("resource:///modules/Services.jsm", tempScope);
+Cu.import("resource://gre/modules/Services.jsm", tempScope);
 let Services = tempScope.Services;
 
 function createDocument()
 {
   doc.body.innerHTML = '<style type="text/css"> ' +
     'html { color: #000000; } ' +
     'span { font-variant: small-caps; color: #000000; } ' +
     '.nomatches {color: #ff0000;}</style> <div id="first" style="margin: 10em; ' +
--- a/browser/devtools/styleinspector/test/head.js
+++ b/browser/devtools/styleinspector/test/head.js
@@ -1,17 +1,17 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 let tempScope = {};
 Cu.import("resource:///modules/devtools/CssLogic.jsm", tempScope);
 Cu.import("resource:///modules/devtools/CssHtmlTree.jsm", tempScope);
-Cu.import("resource://gre/modules/HUDService.jsm", tempScope);
+Cu.import("resource:///modules/HUDService.jsm", tempScope);
 let HUDService = tempScope.HUDService;
 let ConsoleUtils = tempScope.ConsoleUtils;
 let CssLogic = tempScope.CssLogic;
 let CssHtmlTree = tempScope.CssHtmlTree;
 
 function log(aMsg)
 {
   dump("*** WebConsoleTest: " + aMsg + "\n");
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_601667_filter_buttons.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_601667_filter_buttons.js
@@ -43,22 +43,24 @@ function testMenuFilterButton(aCategory)
     }
     menuItem = menuItem.nextSibling;
   }
 
   // Turn all the filters on; make sure the button gets checked.
   menuItem = firstMenuItem;
   let prefKey;
   while (menuItem) {
-    prefKey = menuItem.getAttribute("prefKey");
-    chooseMenuItem(menuItem);
-    ok(isChecked(menuItem), "menu item " + prefKey + " for category " +
-       aCategory + " is checked after clicking it");
-    ok(hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
-       "on after clicking the appropriate menu item");
+    if (menuItem.hasAttribute("prefKey")) {
+      prefKey = menuItem.getAttribute("prefKey");
+      chooseMenuItem(menuItem);
+      ok(isChecked(menuItem), "menu item " + prefKey + " for category " +
+         aCategory + " is checked after clicking it");
+      ok(hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
+         "on after clicking the appropriate menu item");
+    }
     menuItem = menuItem.nextSibling;
   }
   ok(isChecked(button), "the button for category " + aCategory + " is " +
      "checked after turning on all its menu items");
 
   // Turn one filter off; make sure the button is still checked.
   prefKey = firstMenuItem.getAttribute("prefKey");
   chooseMenuItem(firstMenuItem);
@@ -98,21 +100,23 @@ function testMenuFilterButton(aCategory)
   // Turn all the filters on by clicking the main part of the button.
   clickButton(subbutton);
 
   ok(isChecked(button), "the button for category " + aCategory + " is " +
      "checked after clicking its main part");
 
   menuItem = firstMenuItem;
   while (menuItem) {
-    let prefKey = menuItem.getAttribute("prefKey");
-    ok(isChecked(menuItem), "menu item " + prefKey + " for category " +
-       aCategory + " is checked after clicking the button");
-    ok(hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
-       "on after clicking the button");
+    if (menuItem.hasAttribute("prefKey")) {
+      let prefKey = menuItem.getAttribute("prefKey");
+      ok(isChecked(menuItem), "menu item " + prefKey + " for category " +
+         aCategory + " is checked after clicking the button");
+      ok(hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
+         "on after clicking the button");
+    }
     menuItem = menuItem.nextSibling;
   }
 
   // Uncheck the main button by unchecking all the filters
   menuItem = firstMenuItem;
   while (menuItem) {
     chooseMenuItem(menuItem);
     menuItem = menuItem.nextSibling;
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_602572_log_bodies_checkbox.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_602572_log_bodies_checkbox.js
@@ -3,17 +3,17 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  *
  * Contributor(s):
  *   Mihai Șucan <mihai.sucan@gmail.com>
  *
  * ***** END LICENSE BLOCK ***** */
 
-let menuitems = [], menupopups = [], huds = [], tabs = [];
+let menuitems = [], menupopups = [], huds = [], tabs = [], runCount = 0;
 
 function test()
 {
   // open tab 1
   addTab("data:text/html;charset=utf-8,Web Console test for bug 602572: log bodies checkbox. tab 1");
   tabs.push(tab);
 
   browser.addEventListener("load", function onLoad1(aEvent) {
@@ -38,23 +38,28 @@ function test()
       }, true);
     });
   }, true);
 }
 
 function startTest()
 {
   // Find the relevant elements in the Web Console of tab 2.
-  let win2 = tabs[1].linkedBrowser.contentWindow;
+  let win2 = tabs[runCount*2 + 1].linkedBrowser.contentWindow;
   let hudId2 = HUDService.getHudIdByWindow(win2);
   huds[1] = HUDService.hudReferences[hudId2];
   HUDService.disableAnimation(hudId2);
 
-  menuitems[1] = huds[1].ui.rootElement.querySelector("#saveBodies");
-  menupopups[1] = huds[1].ui.rootElement.querySelector("menupopup");
+  if (runCount == 0) {
+    menuitems[1] = huds[1].ui.rootElement.querySelector("#saveBodies");
+  }
+  else {
+    menuitems[1] = huds[1].ui.rootElement.querySelector("#saveBodiesContextMenu");
+  }
+  menupopups[1] = menuitems[1].parentNode;
 
   // Open the context menu from tab 2.
   menupopups[1].addEventListener("popupshown", onpopupshown2, false);
   executeSoon(function() {
     menupopups[1].openPopup();
   });
 }
 
@@ -90,33 +95,33 @@ function onpopupshown2b(aEvent)
 {
   menupopups[1].removeEventListener(aEvent.type, onpopupshown2b, false);
   is(menuitems[1].getAttribute("checked"), "true", "menuitems[1] is checked");
 
   menupopups[1].addEventListener("popuphidden", function _onhidden(aEvent) {
     menupopups[1].removeEventListener(aEvent.type, _onhidden, false);
 
     // Switch to tab 1 and open the Web Console context menu from there.
-    gBrowser.selectedTab = tabs[0];
+    gBrowser.selectedTab = tabs[runCount*2];
     waitForFocus(function() {
       // Find the relevant elements in the Web Console of tab 1.
-      let win1 = tabs[0].linkedBrowser.contentWindow;
+      let win1 = tabs[runCount*2].linkedBrowser.contentWindow;
       let hudId1 = HUDService.getHudIdByWindow(win1);
       huds[0] = HUDService.hudReferences[hudId1];
       HUDService.disableAnimation(hudId1);
 
       info("iframe1 height " + huds[0].iframe.clientHeight);
       info("iframe1 root height " + huds[0].ui.rootElement.clientHeight);
 
       menuitems[0] = huds[0].ui.rootElement.querySelector("#saveBodies");
       menupopups[0] = huds[0].ui.rootElement.querySelector("menupopup");
 
       menupopups[0].addEventListener("popupshown", onpopupshown1, false);
       menupopups[0].openPopup();
-    }, tabs[0].linkedBrowser.contentWindow);
+    }, tabs[runCount*2].linkedBrowser.contentWindow);
   }, false);
 
   executeSoon(function() {
     menupopups[1].hidePopup();
   });
 }
 
 function onpopupshown1(aEvent)
@@ -129,42 +134,55 @@ function onpopupshown1(aEvent)
 
   // Enable body logging for tab 1 as well.
   huds[0].ui.saveRequestAndResponseBodies = true;
 
   // Close the menu, and switch back to tab 2.
   menupopups[0].addEventListener("popuphidden", function _onhidden(aEvent) {
     menupopups[0].removeEventListener(aEvent.type, _onhidden, false);
 
-    gBrowser.selectedTab = tabs[1];
+    gBrowser.selectedTab = tabs[runCount*2 + 1];
     waitForFocus(function() {
       // Reopen the context menu from tab 2.
       menupopups[1].addEventListener("popupshown", onpopupshown2c, false);
       menupopups[1].openPopup();
-    }, tabs[1].linkedBrowser.contentWindow);
+    }, tabs[runCount*2 + 1].linkedBrowser.contentWindow);
   }, false);
 
   executeSoon(function() {
     menupopups[0].hidePopup();
   });
 }
 
 function onpopupshown2c(aEvent)
 {
   menupopups[1].removeEventListener(aEvent.type, onpopupshown2c, false);
 
   is(menuitems[1].getAttribute("checked"), "true", "menuitems[1] is checked");
 
   menupopups[1].addEventListener("popuphidden", function _onhidden(aEvent) {
     menupopups[1].removeEventListener(aEvent.type, _onhidden, false);
 
-    // Done!
-    huds = menuitems = menupopups = tabs = null;
+    // Done if on second run
     closeConsole(gBrowser.selectedTab, function() {
-      gBrowser.removeCurrentTab();
-      executeSoon(finishTest);
+      if (runCount == 0) {
+        runCount++;
+        executeSoon(test);
+      }
+      else {
+        gBrowser.removeCurrentTab();
+        gBrowser.selectedTab = tabs[2];
+        gBrowser.removeCurrentTab();
+        gBrowser.selectedTab = tabs[1];
+        gBrowser.removeCurrentTab();
+        gBrowser.selectedTab = tabs[0];
+        gBrowser.removeCurrentTab();
+        huds = menuitems = menupopups = tabs = null;
+        executeSoon(finishTest);
+      }
     });
+
   }, false);
 
   executeSoon(function() {
     menupopups[1].hidePopup();
   });
 }
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -338,21 +338,40 @@ WebConsoleFrame.prototype = {
       this.inputNode.style.fontSize = fontSize + "px";
     }
 
     let saveBodies = doc.getElementById("saveBodies");
     saveBodies.addEventListener("command", function() {
       this.saveRequestAndResponseBodies = !this.saveRequestAndResponseBodies;
     }.bind(this));
     saveBodies.setAttribute("checked", this.saveRequestAndResponseBodies);
-
-    let contextMenuId = this.outputNode.getAttribute("context");
-    let contextMenu = doc.getElementById(contextMenuId);
-    contextMenu.addEventListener("popupshowing", function() {
+    saveBodies.disabled = !this.getFilterState("networkinfo") &&
+                          !this.getFilterState("network");
+
+    saveBodies.parentNode.addEventListener("popupshowing", function() {
       saveBodies.setAttribute("checked", this.saveRequestAndResponseBodies);
+      saveBodies.disabled = !this.getFilterState("networkinfo") &&
+                            !this.getFilterState("network");
+    }.bind(this));
+
+    // Remove this part when context menu entry is removed.
+    let saveBodiesContextMenu = doc.getElementById("saveBodiesContextMenu");
+    saveBodiesContextMenu.addEventListener("command", function() {
+      this.saveRequestAndResponseBodies = !this.saveRequestAndResponseBodies;
+    }.bind(this));
+    saveBodiesContextMenu.setAttribute("checked",
+                                       this.saveRequestAndResponseBodies);
+    saveBodiesContextMenu.disabled = !this.getFilterState("networkinfo") &&
+                                     !this.getFilterState("network");
+
+    saveBodiesContextMenu.parentNode.addEventListener("popupshowing", function() {
+      saveBodiesContextMenu.setAttribute("checked",
+                                         this.saveRequestAndResponseBodies);
+      saveBodiesContextMenu.disabled = !this.getFilterState("networkinfo") &&
+                                       !this.getFilterState("network");
     }.bind(this));
 
     this.closeButton = doc.getElementById("webconsole-close-button");
     this.closeButton.addEventListener("command",
                                       this.owner.onCloseButton.bind(this.owner));
 
     let clearButton = doc.getElementsByClassName("webconsole-clear-console-button")[0];
     clearButton.addEventListener("command",
@@ -681,23 +700,32 @@ WebConsoleFrame.prototype = {
 
       case "menuitem": {
         let state = target.getAttribute("checked") !== "true";
         target.setAttribute("checked", state);
 
         let prefKey = target.getAttribute("prefKey");
         this.setFilterState(prefKey, state);
 
+        // Disable the log response and request body if network logging is off.
+        if (prefKey == "networkinfo" || prefKey == "network") {
+          let checkState = !this.getFilterState("networkinfo") &&
+                           !this.getFilterState("network");
+          this.document.getElementById("saveBodies").disabled = checkState;
+          this.document.getElementById("saveBodiesContextMenu").disabled = checkState;
+        }
+
         // Adjust the state of the button appropriately.
         let menuPopup = target.parentNode;
 
         let someChecked = false;
         let menuItem = menuPopup.firstChild;
         while (menuItem) {
-          if (menuItem.getAttribute("checked") === "true") {
+          if (menuItem.hasAttribute("prefKey") &&
+              menuItem.getAttribute("checked") === "true") {
             someChecked = true;
             break;
           }
           menuItem = menuItem.nextSibling;
         }
         let toolbarButton = menuPopup.parentNode;
         toolbarButton.setAttribute("checked", someChecked);
         break;
--- a/browser/devtools/webconsole/webconsole.xul
+++ b/browser/devtools/webconsole/webconsole.xul
@@ -34,17 +34,17 @@
     <key key="&fullZoomEnlargeCmd.commandkey3;" command="cmd_fullZoomEnlarge" modifiers="accel"/>
     <key id="key_fullZoomReset" key="&fullZoomResetCmd.commandkey;" command="cmd_fullZoomReset" modifiers="accel"/>
     <key key="&fullZoomResetCmd.commandkey2;" command="cmd_fullZoomReset" modifiers="accel"/>
   </keyset>
   <keyset id="editMenuKeys"/>
 
   <popupset id="mainPopupSet">
     <menupopup id="output-contextmenu">
-      <menuitem id="saveBodies" type="checkbox" label="&saveBodies.label;"
+      <menuitem id="saveBodiesContextMenu" type="checkbox" label="&saveBodies.label;"
                 accesskey="&saveBodies.accesskey;"/>
       <menuitem id="menu_copy"/>
       <menuitem id="menu_selectAll"/>
     </menupopup>
   </popupset>
 
   <vbox class="hud-outer-wrapper" flex="1">
     <vbox class="hud-console-wrapper" flex="1">
@@ -58,16 +58,19 @@
         <toolbarbutton label="&btnPageNet.label;" type="menu-button"
                        category="net" class="devtools-toolbarbutton webconsole-filter-button"
                        tooltiptext="&btnPageNet.tooltip;">
           <menupopup>
             <menuitem label="&btnConsoleErrors;" type="checkbox" autocheck="false"
                       prefKey="network"/>
             <menuitem label="&btnConsoleLog;" type="checkbox" autocheck="false"
                       prefKey="networkinfo"/>
+            <menuseparator id="saveBodiesSeparator" />
+            <menuitem id="saveBodies" type="checkbox" label="&saveBodies.label;"
+                      accesskey="&saveBodies.accesskey;"/>
           </menupopup>
         </toolbarbutton>
         <toolbarbutton label="&btnPageCSS.label;" type="menu-button"
                        category="css" class="devtools-toolbarbutton webconsole-filter-button"
                        tooltiptext="&btnPageCSS.tooltip;">
           <menupopup>
             <menuitem label="&btnConsoleErrors;" type="checkbox" autocheck="false"
                       prefKey="csserror"/>
--- a/browser/locales/en-US/chrome/browser/devtools/scratchpad.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/scratchpad.dtd
@@ -31,16 +31,19 @@
 
 <!ENTITY openFileCmd.label            "Open File…">
 <!ENTITY openFileCmd.accesskey        "O">
 <!ENTITY openFileCmd.commandkey       "o">
 
 <!ENTITY openRecentMenu.label         "Open Recent">
 <!ENTITY openRecentMenu.accesskey     "R">
 
+<!ENTITY revertCmd.label              "Revert…">
+<!ENTITY revertCmd.accesskey          "t">
+
 <!ENTITY saveFileCmd.label            "Save">
 <!ENTITY saveFileCmd.accesskey        "S">
 <!ENTITY saveFileCmd.commandkey       "s">
 
 <!ENTITY saveFileAsCmd.label          "Save As…">
 <!ENTITY saveFileAsCmd.accesskey      "A">
 
 <!ENTITY closeCmd.label               "Close">
--- a/browser/locales/en-US/chrome/browser/devtools/scratchpad.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/scratchpad.properties
@@ -49,16 +49,24 @@ saveFile.failed=The file save operation 
 # LOCALIZATION NOTE  (confirmClose): This is message in the prompt dialog when
 # you try to close a scratchpad with unsaved changes.
 confirmClose=Do you want to save the changes you made to this scratchpad?
 
 # LOCALIZATION NOTE  (confirmClose.title): This is title of the prompt dialog when
 # you try to close a scratchpad with unsaved changes.
 confirmClose.title=Unsaved Changes
 
+# LOCALIZATION NOTE  (confirmRevert): This is message in the prompt dialog when
+# you try to revert unsaved content of scratchpad.
+confirmRevert=Do you want to revert the changes you made to this scratchpad?
+
+# LOCALIZATION NOTE  (confirmRevert.title): This is title of the prompt dialog when
+# you try to revert unsaved content of scratchpad.
+confirmRevert.title=Revert Changes
+
 # LOCALIZATION NOTE  (scratchpadIntro): This is a multi-line comment explaining
 # how to use the Scratchpad. Note that this should be a valid JavaScript
 # comment inside /* and */.
 scratchpadIntro1=/*\n * This is a JavaScript Scratchpad.\n *\n * Enter some JavaScript, then Right Click or choose from the Execute Menu:\n * 1. Run to evaluate the selected text (%1$S),\n * 2. Inspect to bring up an Object Inspector on the result (%2$S), or,\n * 3. Display to insert the result in a comment after the selection. (%3$S)\n */\n\n
 
 # LOCALIZATION NOTE  (notification.browserContext): This is the message displayed
 # over the top of the editor when the user has switched to browser context.
 browserContext.notification=This scratchpad executes in the Browser context.
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -2483,18 +2483,17 @@ html|*#gcli-output-frame {
 
 .gcli-in-closebrace {
   color: hsl(0,0%,80%);
 }
 
 /* Responsive Mode */
 
 .browserContainer[responsivemode] {
-  background: #2a3643 url("chrome://browser/skin/devtools/responsive-background.png");
-  box-shadow: 0 0 7px black inset;
+  background: #222 url("chrome://browser/skin/devtools/responsive-background.png");
   padding: 0 20px 20px 20px;
 }
 
 .browserStack[responsivemode] {
   box-shadow: 0 0 7px black;
 }
 
 .devtools-responsiveui-toolbar {
@@ -2709,16 +2708,20 @@ html|*#gcli-output-frame {
   cursor: pointer;
   min-width: 0;
   margin: 0 6px;
 }
 #social-statusarea-username:hover {
   text-decoration: underline;
 }
 
+.social-panel > .panel-arrowcontainer > .panel-arrowcontent {
+  padding: 0;
+}
+
 .chat-status-icon {
   max-height: 16px;
   max-width: 16px;
   padding: 0;
 }
 
 .chat-toolbarbutton {
   -moz-appearance: none;
@@ -2728,87 +2731,75 @@ html|*#gcli-output-frame {
   background: none;
 }
 
 .chat-toolbarbutton > .toolbarbutton-text {
   display: none;
 }
 
 .chat-close-button {
-  list-style-image: url("chrome://global/skin/icons/close.png");
-  -moz-image-region: rect(0, 16px, 16px, 0);
-}
-
-.chat-close-button:hover {
-  -moz-image-region: rect(0, 32px, 16px, 16px);
+  list-style-image: url('chrome://browser/skin/social/chat-close.png');
+  -moz-image-region: rect(0, 14px, 14px, 0);
 }
 
 .chat-close-button:hover:active {
-  -moz-image-region: rect(0, 48px, 16px, 32px);
-}
-
-
-.chat-toggle-button {
-  /* XXX get a real image for this */
-  list-style-image: url("chrome://global/skin/icons/checkbox.png");
-  -moz-image-region: rect(0, 32px, 16px, 16px);
-}
-
-.chat-toggle-button:hover {
-  -moz-image-region: rect(0, 16px, 16px, 0);
-}
-
-.chat-toggle-button[minimized="true"] {
-  -moz-image-region: rect(0, 16px, 16px, 0);
-}
-
-.chat-toggle-button[minimized="true"]:hover {
-  -moz-image-region: rect(0, 32px, 16px, 16px);
+  -moz-image-region: rect(14px, 14px, 28px, 0);
+}
+
+.chat-close-button:hover {
+  -moz-image-region: rect(28px, 14px, 42px, 0);
 }
 
 .chat-title {
   font-weight: bold;
   color: -moz-dialogtext;
+  cursor: inherit;
 }
 
 .chat-titlebar {
-  background-image: linear-gradient(white, #ddd);
+  background-color: #d9d9d9;
   height: 20px;
   min-height: 20px;
   width: 100%;
   margin: 0;
   padding: 2px;
   border: none;
   border-bottom: 1px solid gray;
+  cursor: pointer;
 }
 
 .chat-titlebar[minimized="true"] {
   border-bottom: none;
 }
 
+.chat-titlebar[selected] {
+  background-color: #f0f0f0;
+}
+
 .chat-frame {
   padding: 0;
   margin: 0;
   overflow: hidden;
 }
 
 .chatbar-button {
-  /* XXX get a real image for this */
+  background-color: #d9d9d9;
   list-style-image: url("chrome://browser/skin/social/social.png");
   border: none;
   margin: 0;
   padding: 2px;
   height: 21px;
   width: 21px;
   border-top: 1px solid gray;
   -moz-border-end: 1px solid gray;
 }
 
 .chatbar-button[open="true"],
 .chatbar-button:active:hover {
+  background-color: #f0f0f0;
   box-shadow: inset 0 2px 5px rgba(0,0,0,0.6), 0 1px rgba(255,255,255,0.2);
 }
 
 .chatbar-button > .toolbarbutton-text,
 .chatbar-button > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
--- a/browser/themes/gnomestripe/jar.mn
+++ b/browser/themes/gnomestripe/jar.mn
@@ -80,16 +80,18 @@ browser.jar:
   skin/classic/browser/preferences/Options.png        (preferences/Options.png)
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/preferences/Options-sync.png   (preferences/Options-sync.png)
 #endif
 * skin/classic/browser/preferences/preferences.css    (preferences/preferences.css)
   skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
   skin/classic/browser/preferences/applications.css   (preferences/applications.css)
   skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
+  skin/classic/browser/social/social.png              (social/social.png)
+  skin/classic/browser/social/chat-close.png          (social/chat-close.png)
   skin/classic/browser/tabbrowser/alltabs.png          (tabbrowser/alltabs.png)
   skin/classic/browser/tabbrowser/connecting.png      (tabbrowser/connecting.png)
   skin/classic/browser/tabbrowser/loading.png         (tabbrowser/loading.png)
   skin/classic/browser/tabbrowser/tab.png             (tabbrowser/tab.png)
   skin/classic/browser/tabbrowser/tab-overflow-border.png (tabbrowser/tab-overflow-border.png)
   skin/classic/browser/tabbrowser/tabDragIndicator.png (tabbrowser/tabDragIndicator.png)
   skin/classic/browser/tabview/edit-light.png         (tabview/edit-light.png)
   skin/classic/browser/tabview/search.png             (tabview/search.png)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cc773cbc1c45756ba57d191fa2c41afedd420287
GIT binary patch
literal 405
zc$@*10c!q<P)<h;3K|Lk000e1NJLTq000gE001fo1^@s6gLP{000048Nkl<ZNQuSL
zv2KGf5C-6T6~6+{<Iy`y9)Ju{mMmdO=15ggQ&sI7bgMhFm<hZC@`l86oh%vqOlVqB
zBrnFF?Emfn{_RsraUlekQgZ*Fl#*{Yn_wRR0E7@6061f8f~Az4F~$LaXIU0v_khLj
zUXmodEKB-UA1^`_UW_Q{XTFP)BndJvLS<QUs$c=M?eH_XnCzueil=Ex7Gib)*88fe
z!bjHcVO`f~j6tn+a7b&7UDwgj7=wt2<2X*R)*8ogL`1~4ZA0iDu-M%jh5?(Vp)>k8
zhREW$IEEHMKl9zE4#N;+CLvV_s$kJ}_?cYn_Hr@+eczLXm>q!iX7%Iq3m;j3125Mi
zda!tTrQzjT;C8pi!!Zxg;h5ugx5wMtJAI=);9|R%pU(3cT^vKv;#d$viy)PG_Nn>l
zOlF=fj{CeA4%$Iwve@nA<i@Ax+s_AJwORcJc}JrE9N5!t00000NkvXXu0mjf@*cTh
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..14916fb074c79b18c732f41661110027983c5d03
GIT binary patch
literal 1654
zc%17D@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKV3x~tb`J1#c2)=|%1_J8No8QD
zU~CP!pM5z%<ln8-xf>pKTcrzf+GR}4O9?J<p0h1GeAn5iS7zH4J!UV<jhS^!<!#*t
z_M6@LR!7S}vfKS+=egOOUp<NUm`LJ}H62Pm-;_4bn{DE&9?ZP+`|kTc-&v|Ze-gLg
zXJOQ35q*A<&|v?ghyEOSB69x|W1Nh$_MZ&j?lv(mHFpR8%bP0SZ~n|DcPyvmUH<*m
z|B|oHn#v>iK}KQU6w@P04<i?3##|{8esHbx{ai!SLuY-8BbKvp%BNf8CpSLq=~bSV
zW@73vi}MisQ70?!9S#;VG9P^Ay>YHl<cYf4%1L_NHmhBO^q;1<ZsPg&LB6>8Q~ZV%
zQ(n$q`&^(mcb25z#hR{r@|Lv)J<k}f9<+~_*z34sZb5|E<He?RPG?ot399XV{;ni*
z#@p0c(<3#7RAp55B!#Rq&dGen&D=Tb+2yxi0w!&kZ?fOSIP&x3w*gaGUK<|$`pw(z
z%dWlGZ%>@e{@B!Rx#qQchktv&e=X{IS~=;%>n(D<SHDD0vI@I>`^nj5%7>4X2ih}x
zI5qXjq-Z>t=){vaEBVN(A3kD|N?uDYWtN0gZnC&$tucApTN(F?lkt2z;>5m2Z`$$g
z!S8u@Rj>E$5p6xXeesRJw_6)u?%E_gOS--^ap~f`hvqw<&X&$y&CL<`uj}bXkGRxR
z{!M|`OU<552%i3@l>5BVly|CSLJ>T>)_jQjp_Rks-zt7+`u-~?&fQxpc)Fob$8Pn!
z;Iqfxs9y59^g3n7{;sc1$0UAEFm#TZyHCY;PM77UTLIkaY~@X_pFN7vzs^2y_nFv-
zzjku0)hkl&J0$+qwMOy7KI46M@!Rt|5|`iJcRA(tV#BD{yDv}NwP<72l>Xc5-)#F<
z2}^F;xNl=!7>D+jMafxSn@ju|m{oKArhjIg{9dqyyQX8EZfF1Mr0@y;+FbcFdE%z?
zc&_)jskgzp|1I;j;A^qZGUJOaKWi1dl>U48wVrt>Tb$&9i^Y243ye#jaBirH(|Ij=
zTi#~g_J)bpx0Bb}<|gjywhLQ*yF@Qqj&V2t>Z|k5uey5o`ICC?ip77}Rw{E{JG3y~
z2$*3w3p^r=85p>QL70(Y)*J~21}3@8kcg59UmvUF{9L`nl>DSry^7od1`x2RumUo3
zQ%e#RDspr3imfVamB1>jfNYSkzLEl1NlCV?QiN}Sf^&XRs)C80iJpOy9hZWFf=y9M
znpKdC8&o@xXRDM^Qc_^0uU}qXu2*iXmtT~wZ)j<0sc&GUZ)BtkRH0j3nOBlnp_^B%
z3^4>|j!SBBa#3bMNoIbY0*IfOlwVq6tE2=~0|5|=Qj+2J6qFX_fNe=h)=$kz%}vcK
zDb_dCGt`G04l+L_*{ZlSDJwO(#16$cu*FV^>H5fu^)Xf3=%cxiT$cj_6Yli9VrYB-
zV@AIsu>k0715{@NRa-e1r6!i-7lq{K=h!)dlq-1VCZ?wbr6#6S+UTRJ!0TR!C@{Y4
zxPUrgac{>JuxDWiFgaZDba4!^5ZpS+k*nE2!1eg0&o^3yHs09FE&eET(-v_d@p%gu
zU164NUFp0s<xxlLrACKOm6e_+Dmpm!U#ZM54iq^P6FOCW<Ghk)oAZXJjgFc7GMC2Y
zt32YpWYStWZ)SthN|%EszCzk*{{<zIk6d2zL8Ue7wYBFP$C*dEnpkztS~S^e{EjN{
z7TV&!+%mPVq2=i^{&j^tUYhN-`oH(JyWidvm(ZlqWx&H!_*uP%kGUjvyVni#xElGs
zss@SOGY-79lfE;1v${^ePIjxGr*<;6TgUBVI3k(zKwqXI1(e=BUHx3vIVCg!0Cn%A
AApigX
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -3201,18 +3201,17 @@ html|*#gcli-output-frame {
 
 .gcli-in-closebrace {
   color: hsl(0,0%,80%);
 }
 
 /* Responsive Mode */
 
 .browserContainer[responsivemode] {
-  background: #2a3643 url("chrome://browser/skin/devtools/responsive-background.png");
-  box-shadow: 0 0 7px black inset;
+  background: #222 url("chrome://browser/skin/devtools/responsive-background.png");
   padding: 0 20px 20px 20px;
 }
 
 .browserStack[responsivemode] {
   box-shadow: 0 0 7px black;
 }
 
 .devtools-responsiveui-toolbar {
@@ -3393,16 +3392,25 @@ html|*#gcli-output-frame {
   min-width: 0;
   margin: 0 6px;
 }
 
 #social-statusarea-username:hover {
   text-decoration: underline;
 }
 
+.social-panel > .panel-arrowcontainer > .panel-arrowcontent {
+  padding: 0;
+}
+
+#social-notification-box,
+.social-panel-frame {
+  border-radius: inherit;
+}
+
 /* === end of social toolbar provider menu === */
 
 .chat-status-icon {
   max-height: 16px;
   max-width: 16px;
   padding: 0;
 }
 
@@ -3414,88 +3422,75 @@ html|*#gcli-output-frame {
   background: none;
 }
 
 .chat-toolbarbutton > .toolbarbutton-text {
   display: none;
 }
 
 .chat-close-button {
-  list-style-image: url("chrome://global/skin/icons/close.png");
-  -moz-image-region: rect(0, 16px, 16px, 0);
-}
-
-.chat-close-button:hover {
-  -moz-image-region: rect(0, 32px, 16px, 16px);
+  list-style-image: url('chrome://browser/skin/social/chat-close.png');
+  -moz-image-region: rect(0, 14px, 14px, 0);
 }
 
 .chat-close-button:hover:active {
-  -moz-image-region: rect(0, 48px, 16px, 32px);
-}
-
-
-.chat-toggle-button {
-  /* XXX get a real image for this */
-  list-style-image: url("chrome://global/skin/icons/checkbox.png");
-  -moz-image-region: rect(0, 32px, 16px, 16px);
-}
-
-.chat-toggle-button:hover {
-  -moz-image-region: rect(0, 16px, 16px, 0);
-}
-
-.chat-toggle-button[minimized="true"] {
-  -moz-image-region: rect(0, 16px, 16px, 0);
-}
-
-.chat-toggle-button[minimized="true"]:hover {
-  -moz-image-region: rect(0, 32px, 16px, 16px);
+  -moz-image-region: rect(14px, 14px, 28px, 0);
+}
+
+.chat-close-button:hover {
+  -moz-image-region: rect(28px, 14px, 42px, 0);
 }
 
 .chat-title {
   font-weight: bold;
   color: -moz-dialogtext;
+  cursor: inherit;
 }
 
 .chat-titlebar {
-  background-image: linear-gradient(white, #ddd);
+  background-color: #d9d9d9;
   height: 20px;
   min-height: 20px;
   width: 100%;
   margin: 0;
   padding: 2px;
   border: none;
   border-bottom: 1px solid #404040;
+  cursor: pointer;
 }
 
 .chat-titlebar[minimized="true"] {
   border-bottom: none;
 }
 
+.chat-titlebar[selected] {
+  background-color: #f0f0f0;
+}
+
 .chat-frame {
   padding: 0;
   margin: 0;
   overflow: hidden;
 }
 
 .chatbar-button {
-  /* XXX get a real image for this */
-  background-image: linear-gradient(white, #ddd);
+  background-color: #d9d9d9;
   list-style-image: url("chrome://browser/skin/social/social.png");
   border: none;
   margin: 0;
   padding: 2px;
   height: 21px;
   width: 21px;
   border-top: 1px solid #404040;
   -moz-border-end: 1px solid #404040;
 }
 
 .chatbar-button[open="true"],
 .chatbar-button:active:hover {
+  background-color: #f0f0f0;
   box-shadow: inset 0 2px 5px rgba(0,0,0,0.6), 0 1px rgba(255,255,255,0.2);
 }
 
 .chatbar-button > .toolbarbutton-text,
 .chatbar-button > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
--- a/browser/themes/pinstripe/jar.mn
+++ b/browser/themes/pinstripe/jar.mn
@@ -103,16 +103,17 @@ browser.jar:
   skin/classic/browser/preferences/Options-sync.png         (preferences/Options-sync.png)
 #endif
   skin/classic/browser/preferences/saveFile.png             (preferences/saveFile.png)
 * skin/classic/browser/preferences/preferences.css          (preferences/preferences.css)
   skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
   skin/classic/browser/preferences/applications.css         (preferences/applications.css)
   skin/classic/browser/preferences/aboutPermissions.css     (preferences/aboutPermissions.css)
   skin/classic/browser/social/social.png                    (social/social.png)
+  skin/classic/browser/social/chat-close.png                             (social/chat-close.png)
   skin/classic/browser/tabbrowser/alltabs-box-bkgnd-icon.png             (tabbrowser/alltabs-box-bkgnd-icon.png)
   skin/classic/browser/tabbrowser/newtab.png                             (tabbrowser/newtab.png)
   skin/classic/browser/tabbrowser/connecting.png                         (tabbrowser/connecting.png)
   skin/classic/browser/tabbrowser/loading.png                            (tabbrowser/loading.png)
   skin/classic/browser/tabbrowser/tab-arrow-left.png                     (tabbrowser/tab-arrow-left.png)
   skin/classic/browser/tabbrowser/tab-arrow-right.png                    (tabbrowser/tab-arrow-right.png)
   skin/classic/browser/tabbrowser/tabbar-bottom-bg-active.png            (tabbrowser/tabbar-bottom-bg-active.png)
   skin/classic/browser/tabbrowser/tabbar-bottom-bg-inactive.png          (tabbrowser/tabbar-bottom-bg-inactive.png)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cc773cbc1c45756ba57d191fa2c41afedd420287
GIT binary patch
literal 405
zc$@*10c!q<P)<h;3K|Lk000e1NJLTq000gE001fo1^@s6gLP{000048Nkl<ZNQuSL
zv2KGf5C-6T6~6+{<Iy`y9)Ju{mMmdO=15ggQ&sI7bgMhFm<hZC@`l86oh%vqOlVqB
zBrnFF?Emfn{_RsraUlekQgZ*Fl#*{Yn_wRR0E7@6061f8f~Az4F~$LaXIU0v_khLj
zUXmodEKB-UA1^`_UW_Q{XTFP)BndJvLS<QUs$c=M?eH_XnCzueil=Ex7Gib)*88fe
z!bjHcVO`f~j6tn+a7b&7UDwgj7=wt2<2X*R)*8ogL`1~4ZA0iDu-M%jh5?(Vp)>k8
zhREW$IEEHMKl9zE4#N;+CLvV_s$kJ}_?cYn_Hr@+eczLXm>q!iX7%Iq3m;j3125Mi
zda!tTrQzjT;C8pi!!Zxg;h5ugx5wMtJAI=);9|R%pU(3cT^vKv;#d$viy)PG_Nn>l
zOlF=fj{CeA4%$Iwve@nA<i@Ax+s_AJwORcJc}JrE9N5!t00000NkvXXu0mjf@*cTh
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -3163,18 +3163,17 @@ html|*#gcli-output-frame {
 
 .gcli-in-closebrace {
   color: hsl(0,0%,80%);
 }
 
 /* Responsive Mode */
 
 .browserContainer[responsivemode] {
-  background: #2a3643 url("chrome://browser/skin/devtools/responsive-background.png");
-  box-shadow: 0 0 7px black inset;
+  background: #222 url("chrome://browser/skin/devtools/responsive-background.png");
   padding: 0 20px 20px 20px;
 }
 
 .browserStack[responsivemode] {
   box-shadow: 0 0 7px black;
 }
 
 .devtools-responsiveui-toolbar {
@@ -3412,16 +3411,25 @@ html|*#gcli-output-frame {
   cursor: pointer;
   min-width: 0;
   margin: 0 6px;
 }
 #social-statusarea-username:hover {
   text-decoration: underline;
 }
 
+.social-panel > .panel-arrowcontainer > .panel-arrowcontent {
+  padding: 0;
+}
+
+#social-notification-box,
+.social-panel-frame {
+  border-radius: inherit;
+}
+
 .chat-status-icon {
   max-height: 16px;
   max-width: 16px;
   padding: 0;
 }
 
 .chat-toolbarbutton {
   -moz-appearance: none;
@@ -3431,77 +3439,65 @@ html|*#gcli-output-frame {
   background: none;
 }
 
 .chat-toolbarbutton > .toolbarbutton-text {
   display: none;
 }
 
 .chat-close-button {
-  list-style-image: url("chrome://global/skin/icons/Close.gif");
-  padding: 2px 4px;
-}
-
-.chat-close-button:hover {
-  box-shadow: inset 0 2px 5px rgba(0,0,0,0.6), 0 1px rgba(255,255,255,0.2);
+  list-style-image: url('chrome://browser/skin/social/chat-close.png');
+  -moz-image-region: rect(0, 14px, 14px, 0);
 }
 
 .chat-close-button:hover:active {
-  box-shadow: inset 0 2px 5px rgba(0,0,0,0.6), 0 1px rgba(255,255,255,0.2);
-}
-
-
-.chat-toggle-button {
-  /* XXX get a real image for this */
-  list-style-image: url("chrome://global/skin/icons/expand.png");
-}
-
-.chat-toggle-button:hover {
-  box-shadow: inset 0 2px 5px rgba(0,0,0,0.6), 0 1px rgba(255,255,255,0.2);
-}
-
-.chat-toggle-button[minimized="true"] {
-  list-style-image: url("chrome://global/skin/icons/collapse.png");
-}
-
-.chat-toggle-button[minimized="true"]:hover {
-  box-shadow: inset 0 2px 5px rgba(0,0,0,0.6), 0 1px rgba(255,255,255,0.2);
+  -moz-image-region: rect(14px, 14px, 28px, 0);
+}
+
+.chat-close-button:hover {
+  -moz-image-region: rect(28px, 14px, 42px, 0);
 }
 
 .chat-title {
   font-weight: bold;
   color: -moz-dialogtext;
+  cursor: inherit;
 }
 
 .chat-titlebar {
-  background-image: linear-gradient(white, #ddd);
+  background-color: #c4cfde;
   height: 20px;
   min-height: 20px;
   width: 100%;
   margin: 0;
   padding: 2px;
   border: none;
   border-bottom: 1px solid gray;
+  cursor: pointer;
 }
 
 .chat-titlebar[minimized="true"] {
   border-bottom: none;
 }
 
+.chat-titlebar[selected] {
+  background-color: #dae3f0;
+}
+
 .chat-frame {
   padding: 0;
   margin: 0;
   overflow: hidden;
 }
 
 .chatbar-button {
   /* XXX get a real image for this */
   -moz-appearance: none;
   list-style-image: url("chrome://browser/skin/social/social.png");
-  background-image: linear-gradient(white, #ddd);
+  background-color: #c4cfde;
   border: none;
   margin: 0;
   padding: 2px;
   height: 21px;
   width: 21px;
   border-top: 1px solid gray;
   -moz-border-end: 1px solid gray;
 }
@@ -3510,16 +3506,17 @@ html|*#gcli-output-frame {
   max-height: 16px;
   max-width: 16px;
   padding: 2px;
 }
 
 .chatbar-button[open="true"],
 .chatbar-button:hover,
 .chatbar-button:active:hover {
+  background-color: #dae3f0;
   box-shadow: inset 0 2px 5px rgba(0,0,0,0.6), 0 1px rgba(255,255,255,0.2);
 }
 
 .chatbar-button > .toolbarbutton-text,
 .chatbar-button > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
--- a/browser/themes/winstripe/jar.mn
+++ b/browser/themes/winstripe/jar.mn
@@ -100,16 +100,17 @@ browser.jar:
         skin/classic/browser/preferences/Options-sync.png            (preferences/Options-sync.png)
 #endif
         skin/classic/browser/preferences/saveFile.png                (preferences/saveFile.png)
 *       skin/classic/browser/preferences/preferences.css             (preferences/preferences.css)
         skin/classic/browser/preferences/in-content/preferences.css  (preferences/in-content/preferences.css)
         skin/classic/browser/preferences/applications.css            (preferences/applications.css)
         skin/classic/browser/preferences/aboutPermissions.css        (preferences/aboutPermissions.css)
         skin/classic/browser/social/social.png                       (social/social.png)
+        skin/classic/browser/social/chat-close.png                   (social/chat-close.png)
         skin/classic/browser/tabbrowser/alltabs.png                  (tabbrowser/alltabs.png)
         skin/classic/browser/tabbrowser/newtab.png                   (tabbrowser/newtab.png)
         skin/classic/browser/tabbrowser/newtab-inverted.png          (tabbrowser/newtab-inverted.png)
         skin/classic/browser/tabbrowser/connecting.png               (tabbrowser/connecting.png)
         skin/classic/browser/tabbrowser/loading.png                  (tabbrowser/loading.png)
         skin/classic/browser/tabbrowser/tab.png                      (tabbrowser/tab.png)
         skin/classic/browser/tabbrowser/tab-arrow-left.png           (tabbrowser/tab-arrow-left.png)
         skin/classic/browser/tabbrowser/tab-arrow-left-inverted.png  (tabbrowser/tab-arrow-left-inverted.png)
@@ -302,16 +303,17 @@ browser.jar:
         skin/classic/aero/browser/preferences/Options-sync.png       (preferences/Options-sync.png)
 #endif
         skin/classic/aero/browser/preferences/saveFile.png           (preferences/saveFile-aero.png)
 *       skin/classic/aero/browser/preferences/preferences.css        (preferences/preferences.css)
         skin/classic/aero/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
         skin/classic/aero/browser/preferences/applications.css       (preferences/applications.css)
         skin/classic/aero/browser/preferences/aboutPermissions.css   (preferences/aboutPermissions.css)
         skin/classic/aero/browser/social/social.png                  (social/social.png)
+        skin/classic/aero/browser/social/chat-close.png              (social/chat-close.png)
         skin/classic/aero/browser/tabbrowser/alltabs.png             (tabbrowser/alltabs.png)
         skin/classic/aero/browser/tabbrowser/newtab.png              (tabbrowser/newtab.png)
         skin/classic/aero/browser/tabbrowser/newtab-inverted.png     (tabbrowser/newtab-inverted.png)
         skin/classic/aero/browser/tabbrowser/connecting.png          (tabbrowser/connecting.png)
         skin/classic/aero/browser/tabbrowser/loading.png             (tabbrowser/loading.png)
         skin/classic/aero/browser/tabbrowser/tab.png                 (tabbrowser/tab.png)
         skin/classic/aero/browser/tabbrowser/tab-arrow-left.png      (tabbrowser/tab-arrow-left.png)
         skin/classic/aero/browser/tabbrowser/tab-arrow-left-inverted.png (tabbrowser/tab-arrow-left-inverted.png)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5260f1019668f8c9c6c64f1ec61f5346609cc5c3
GIT binary patch
literal 419
zc$@*F0bKrxP)<h;3K|Lk000e1NJLTq000gE001fo1^@s6gLP{00004MNkl<ZNQuSL
zu}T9$5C-6Rm3@YLopiY_fhz{C2x$(|q>)%C5>T+w7w{1jwowljlx+kthf#uPLdcP3
ztQPimXD?@z^I)63{n-6y2H@X5tu@ARj8aN;|4At^O;c|l0081RMgTw|L<LJJQ3!zm
zfLWIL*gjyey%$9hDy8^aUEBz9xH00On|UXSqR7kK2q~qIE0_mOJKRh*W_zi%#w1DD
zLevgGd*Ai_6YaBp4{sk{Xueodt_$x_t_zyIee!U=SP~J@rrcI=t_#|fTOuNQe*N&F
zeZXLQZ?!Jz?s3Lvba4o=#j$Y+HG*#Doljk@OD{7Exk7LS3%0|}WMjLRl>sQ;zt}?5
z4nTXedU<t2`>el#<H<C5@bKiEhvUhVI^90?aOj~P4yn`a)A8vAf1^2IW3z{YQGG@m
zhfuIM282)}$Yrj5Dh@_$=Gx-e&kJF%9b6`Z?Os-HJUBZ0`2e&wtKW~RZs^>=v#$UE
N002ovPDHLkV1mgzxiJ6$
--- a/build/autoconf/compiler-opts.m4
+++ b/build/autoconf/compiler-opts.m4
@@ -29,27 +29,23 @@ case "$target" in
     if test -z "$MIDL"; then MIDL=midl; fi
 
     # need override this flag since we don't use $(LDFLAGS) for this.
     if test -z "$HOST_LDFLAGS" ; then
         HOST_LDFLAGS=" "
     fi
     ;;
 *-darwin*)
-    # we prefer gcc-4.2 over gcc on older darwin, so
-    # use that specific version if it's available.
-    # On newer versions of darwin, gcc is llvm-gcc while gcc-4.2 is the plain
-    # one, so we also try that first. If that fails, we fall back to clang
-    # as llvm-gcc is an unsupported dead end.
-    MOZ_PATH_PROGS(CC, $CC gcc-4.2 clang gcc)
-    MOZ_PATH_PROGS(CXX, $CXX g++-4.2 clang++ g++)
-    IS_LLVM_GCC=$($CC -v 2>&1 | grep llvm-gcc)
-    if test -n "$IS_LLVM_GCC"
+    # GCC on darwin is based on gcc 4.2 and we don't support it anymore.
+    MOZ_PATH_PROGS(CC, $CC clang)
+    MOZ_PATH_PROGS(CXX, $CXX clang++)
+    IS_GCC=$($CC -v 2>&1 | grep gcc)
+    if test -n "$IS_GCC"
     then
-      echo llvm-gcc is known to be broken, please use gcc-4.2 or clang.
+      echo gcc is known to be broken on OS X, please use clang.
       exit 1
     fi
     ;;
 esac
 fi
 ])
 
 dnl ============================================================================
deleted file mode 100644
--- a/build/mobile/b2gemulator.py
+++ /dev/null
@@ -1,87 +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/.
-
-
-import os
-import platform
-
-from emulator import Emulator
-
-
-class B2GEmulator(Emulator):
-
-    def __init__(self, homedir=None, noWindow=False, logcat_dir=None, arch="x86",
-                 emulatorBinary=None, res='480x800', userdata=None,
-                 memory='512', partition_size='512'):
-        super(B2GEmulator, self).__init__(noWindow=noWindow, logcat_dir=logcat_dir,
-                                          arch=arch, emulatorBinary=emulatorBinary,
-                                          res=res, userdata=userdata,
-                                          memory=memory, partition_size=partition_size)
-        self.homedir = homedir
-        if self.homedir is not None:
-            self.homedir = os.path.expanduser(homedir)
-
-    def _check_file(self, filePath):
-        if not os.path.exists(filePath):
-            raise Exception(('File not found: %s; did you pass the B2G home '
-                             'directory as the homedir parameter, or set '
-                             'B2G_HOME correctly?') % filePath)
-
-    def _check_for_adb(self, host_dir):
-        if self._default_adb() == 0:
-            return
-        adb_paths = [os.path.join(self.homedir,'glue','gonk','out','host',
-                      host_dir ,'bin','adb'),os.path.join(self.homedir, 'out',
-                      'host', host_dir,'bin','adb'),os.path.join(self.homedir,
-                      'bin','adb')]
-        for option in adb_paths:
-            if os.path.exists(option):
-                self.adb = option
-                return
-        raise Exception('adb not found!')
-
-    def _locate_files(self):
-        if self.homedir is None:
-            self.homedir = os.getenv('B2G_HOME')
-        if self.homedir is None:
-            raise Exception('Must define B2G_HOME or pass the homedir parameter')
-        self._check_file(self.homedir)
-
-        if self.arch not in ("x86", "arm"):
-            raise Exception("Emulator architecture must be one of x86, arm, got: %s" %
-                            self.arch)
-
-        host_dir = "linux-x86"
-        if platform.system() == "Darwin":
-            host_dir = "darwin-x86"
-
-        host_bin_dir = os.path.join("out", "host", host_dir, "bin")
-
-        if self.arch == "x86":
-            binary = os.path.join(host_bin_dir, "emulator-x86")
-            kernel = "prebuilts/qemu-kernel/x86/kernel-qemu"
-            sysdir = "out/target/product/generic_x86"
-            self.tail_args = []
-        else:
-            binary = os.path.join(host_bin_dir, "emulator")
-            kernel = "prebuilts/qemu-kernel/arm/kernel-qemu-armv7"
-            sysdir = "out/target/product/generic"
-            self.tail_args = ["-cpu", "cortex-a8"]
-
-        self._check_for_adb(host_dir)
-
-        if not self.binary:
-            self.binary = os.path.join(self.homedir, binary)
-
-        self._check_file(self.binary)
-
-        self.kernelImg = os.path.join(self.homedir, kernel)
-        self._check_file(self.kernelImg)
-
-        self.sysDir = os.path.join(self.homedir, sysdir)
-        self._check_file(self.sysDir)
-
-        if not self.dataImg:
-            self.dataImg = os.path.join(self.sysDir, 'userdata.img')
-        self._check_file(self.dataImg)
deleted file mode 100644
--- a/build/mobile/devicemanager-utils.py
+++ /dev/null
@@ -1,56 +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/.
-
-import devicemanager
-import sys
-import os
-
-def copy(dm, gre_path):
-    file = sys.argv[2]
-    if len(sys.argv) >= 4:
-        path = sys.argv[3]
-    else:
-        path = gre_path
-    if os.path.isdir(file):
-        dm.pushDir(file, path.replace('\\','/'))
-    else:
-        dm.pushFile(file, path.replace('\\','/'))
-
-def delete(dm, gre_path):
-    file = sys.argv[2]
-    dm.removeFile(file)
-
-def main():
-    ip_addr = os.environ.get("DEVICE_IP")
-    port = os.environ.get("DEVICE_PORT")
-    gre_path = os.environ.get("REMOTE_GRE_PATH").replace('\\','/')
-
-    if ip_addr == None:
-        print "Error: please define the environment variable DEVICE_IP before running this test"
-        sys.exit(1)
-
-    if port == None:
-        print "Error: please define the environment variable DEVICE_PORT before running this test"
-        sys.exit(1)
-
-    if gre_path == None:
-        print "Error: please define the environment variable REMOTE_GRE_PATH before running this test"
-        sys.exit(1)
-
-    dm = devicemanager.DeviceManager(ip_addr, int(port))
-    dm.sendCMD(['cd '+ gre_path], sleep = 1)
-    dm.debug = 0
-
-    if len(sys.argv) < 3:
-        print "usage: python devicemanager-utils.py <cmd> <path> [arg]"
-    cmd = sys.argv[1]
-    if (cmd == 'copy'):
-        sys.exit(copy(dm, gre_path))
-    if (cmd == 'delete'):
-        sys.exit(delete(dm, gre_path))
-    print "unrecognized command. supported commands are copy and delete"
-    sys.exit(-1)
-
-if __name__ == '__main__':
-    main()
deleted file mode 100755
--- a/build/mobile/devicemanager.py
+++ /dev/null
@@ -1,637 +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/.
-
-import hashlib
-import socket
-import os
-import re
-import StringIO
-
-class FileError(Exception):
-  " Signifies an error which occurs while doing a file operation."
-
-  def __init__(self, msg = ''):
-    self.msg = msg
-
-  def __str__(self):
-    return self.msg
-
-class DMError(Exception):
-  "generic devicemanager exception."
-
-  def __init__(self, msg= ''):
-    self.msg = msg
-
-  def __str__(self):
-    return self.msg
-
-def abstractmethod(method):
-  line = method.func_code.co_firstlineno
-  filename = method.func_code.co_filename
-  def not_implemented(*args, **kwargs):
-    raise NotImplementedError('Abstract method %s at File "%s", line %s '
-                              'should be implemented by a concrete class' %
-                              (repr(method), filename,line))
-  return not_implemented
-
-class DeviceManager:
-
-  @abstractmethod
-  def shell(self, cmd, outputfile, env=None, cwd=None, timeout=None):
-    """
-    executes shell command on device
-
-    timeout is specified in seconds, and if no timeout is given, 
-    we will run until the script returns
-    returns:
-    success: Return code from command
-    failure: None
-    """
-
-  @abstractmethod
-  def pushFile(self, localname, destname):
-    """
-    external function
-    returns:
-    success: True
-    failure: False
-    """
-
-  @abstractmethod
-  def mkDir(self, name):
-    """
-    external function
-    returns:
-    success: directory name
-    failure: None
-    """
-
-  def mkDirs(self, filename):
-    """
-    make directory structure on the device
-    WARNING: does not create last part of the path
-    external function
-    returns:
-    success: directory structure that we created
-    failure: None
-    """
-    parts = filename.split('/')
-    name = ""
-    for part in parts:
-        if (part == parts[-1]): break
-        if (part != ""):
-            name += '/' + part
-            if (not self.dirExists(name)):
-                if (self.mkDir(name) == None):
-                    print "failed making directory: " + str(name)
-                    return None
-    return name
-
-  @abstractmethod
-  def pushDir(self, localDir, remoteDir):
-    """
-    push localDir from host to remoteDir on the device
-    external function
-    returns:
-    success: remoteDir
-    failure: None
-    """
-
-  @abstractmethod
-  def dirExists(self, dirname):
-    """
-    external function
-    returns:
-    success: True
-    failure: False
-    """
-
-  @abstractmethod
-  def fileExists(self, filepath):
-    """
-    Because we always have / style paths we make this a lot easier with some
-    assumptions
-    external function
-    returns:
-    success: True
-    failure: False
-    """
-
-  @abstractmethod
-  def listFiles(self, rootdir):
-    """
-    list files on the device, requires cd to directory first
-    external function
-    returns:
-    success: array of filenames, ['file1', 'file2', ...]
-    failure: None
-    """
-
-  @abstractmethod
-  def removeFile(self, filename):
-    """
-    external function
-    returns:
-    success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt"
-    failure: None
-    """
-
-  @abstractmethod
-  def removeDir(self, remoteDir):
-    """
-    does a recursive delete of directory on the device: rm -Rf remoteDir
-    external function
-    returns:
-    success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt"
-    failure: None
-    """
-
-  @abstractmethod
-  def getProcessList(self):
-    """
-    external function
-    returns:
-    success: array of process tuples
-    failure: None
-    """
-
-  @abstractmethod
-  def fireProcess(self, appname, failIfRunning=False):
-    """
-    external function
-    DEPRECATED: Use shell() or launchApplication() for new code
-    returns:
-    success: pid
-    failure: None
-    """
-
-  @abstractmethod
-  def launchProcess(self, cmd, outputFile = "process.txt", cwd = '', env = '', failIfRunning=False):
-    """
-    external function
-    DEPRECATED: Use shell() or launchApplication() for new code
-    returns:
-    success: output filename
-    failure: None
-    """
-
-  def processExist(self, appname):
-    """
-    iterates process list and returns pid if exists, otherwise None
-    external function
-    returns:
-    success: pid
-    failure: None
-    """
-
-    pid = None
-
-    #filter out extra spaces
-    parts = filter(lambda x: x != '', appname.split(' '))
-    appname = ' '.join(parts)
-
-    #filter out the quoted env string if it exists
-    #ex: '"name=value;name2=value2;etc=..." process args' -> 'process args'
-    parts = appname.split('"')
-    if (len(parts) > 2):
-      appname = ' '.join(parts[2:]).strip()
-
-    pieces = appname.split(' ')
-    parts = pieces[0].split('/')
-    app = parts[-1]
-
-    procList = self.getProcessList()
-    if (procList == []):
-      return None
-
-    for proc in procList:
-      procName = proc[1].split('/')[-1]
-      if (procName == app):
-        pid = proc[0]
-        break
-    return pid
-
-
-  @abstractmethod
-  def killProcess(self, appname, forceKill=False):
-    """
-    external function
-    returns:
-    success: True
-    failure: False
-    """
-
-  @abstractmethod
-  def catFile(self, remoteFile):
-    """
-    external function
-    returns:
-    success: filecontents
-    failure: None
-    """
-
-  @abstractmethod
-  def pullFile(self, remoteFile):
-    """
-    external function
-    returns:
-    success: output of pullfile, string
-    failure: None
-    """
-
-  @abstractmethod
-  def getFile(self, remoteFile, localFile = ''):
-    """
-    copy file from device (remoteFile) to host (localFile)
-    external function
-    returns:
-    success: output of pullfile, string
-    failure: None
-    """
-
-  @abstractmethod
-  def getDirectory(self, remoteDir, localDir, checkDir=True):
-    """
-    copy directory structure from device (remoteDir) to host (localDir)
-    external function
-    checkDir exists so that we don't create local directories if the
-    remote directory doesn't exist but also so that we don't call isDir
-    twice when recursing.
-    returns:
-    success: list of files, string
-    failure: None
-    """
-
-  @abstractmethod
-  def isDir(self, remotePath):
-    """
-    external function
-    returns:
-    success: True
-    failure: False
-    Throws a FileError exception when null (invalid dir/filename)
-    """
-
-  @abstractmethod
-  def validateFile(self, remoteFile, localFile):
-    """
-    true/false check if the two files have the same md5 sum
-    external function
-    returns:
-    success: True
-    failure: False
-    """
-
-  @abstractmethod
-  def getRemoteHash(self, filename):
-    """
-    return the md5 sum of a remote file
-    internal function
-    returns:
-    success: MD5 hash for given filename
-    failure: None
-    """
-
-  def getLocalHash(self, filename):
-    """
-    return the md5 sum of a file on the host
-    internal function
-    returns:
-    success: MD5 hash for given filename
-    failure: None
-    """
-
-    file = open(filename, 'rb')
-    if (file == None):
-      return None
-
-    try:
-      mdsum = hashlib.md5()
-    except:
-      return None
-
-    while 1:
-      data = file.read(1024)
-      if not data:
-        break
-      mdsum.update(data)
-
-    file.close()
-    hexval = mdsum.hexdigest()
-    if (self.debug >= 3): print "local hash returned: '" + hexval + "'"
-    return hexval
-
-  @abstractmethod
-  def getDeviceRoot(self):
-    """
-    Gets the device root for the testing area on the device
-    For all devices we will use / type slashes and depend on the device-agent
-    to sort those out.  The agent will return us the device location where we
-    should store things, we will then create our /tests structure relative to
-    that returned path.
-    Structure on the device is as follows:
-    /tests
-          /<fennec>|<firefox>  --> approot
-          /profile
-          /xpcshell
-          /reftest
-          /mochitest
-    external
-    returns:
-    success: path for device root
-    failure: None
-    """
-
-  @abstractmethod
-  def getAppRoot(self):
-    """
-    Either we will have /tests/fennec or /tests/firefox but we will never have
-    both.  Return the one that exists
-    TODO: ensure we can support org.mozilla.firefox
-    external function
-    returns:
-    success: path for app root
-    failure: None
-    """
-
-  def getTestRoot(self, type):
-    """
-    Gets the directory location on the device for a specific test type
-    Type is one of: xpcshell|reftest|mochitest
-    external function
-    returns:
-    success: path for test root
-    failure: None
-    """
-
-    devroot = self.getDeviceRoot()
-    if (devroot == None):
-      return None
-
-    if (re.search('xpcshell', type, re.I)):
-      self.testRoot = devroot + '/xpcshell'
-    elif (re.search('?(i)reftest', type)):
-      self.testRoot = devroot + '/reftest'
-    elif (re.search('?(i)mochitest', type)):
-      self.testRoot = devroot + '/mochitest'
-    return self.testRoot
-
-  def signal(self, processID, signalType, signalAction):
-    """
-    Sends a specific process ID a signal code and action.
-    For Example: SIGINT and SIGDFL to process x
-    """
-    #currently not implemented in device agent - todo
-
-    pass
-
-  def getReturnCode(self, processID):
-    """Get a return code from process ending -- needs support on device-agent"""
-    # TODO: make this real
-
-    return 0
-
-  @abstractmethod
-  def unpackFile(self, file_path, dest_dir=None):
-    """
-    external function
-    returns:
-    success: output of unzip command
-    failure: None
-    """
-
-  @abstractmethod
-  def reboot(self, ipAddr=None, port=30000):
-    """
-    external function
-    returns:
-    success: status from test agent
-    failure: None
-    """
-
-  def validateDir(self, localDir, remoteDir):
-    """
-    validate localDir from host to remoteDir on the device
-    external function
-    returns:
-    success: True
-    failure: False
-    """
-
-    if (self.debug >= 2): print "validating directory: " + localDir + " to " + remoteDir
-    for root, dirs, files in os.walk(localDir):
-      parts = root.split(localDir)
-      for file in files:
-        remoteRoot = remoteDir + '/' + parts[1]
-        remoteRoot = remoteRoot.replace('/', '/')
-        if (parts[1] == ""): remoteRoot = remoteDir
-        remoteName = remoteRoot + '/' + file
-        if (self.validateFile(remoteName, os.path.join(root, file)) <> True):
-            return False
-    return True
-
-  @abstractmethod
-  def getInfo(self, directive=None):
-    """
-    Returns information about the device:
-    Directive indicates the information you want to get, your choices are:
-    os - name of the os
-    id - unique id of the device
-    uptime - uptime of the device
-    uptimemillis - uptime of the device in milliseconds (NOT supported on all
-                   implementations)
-    systime - system time of the device
-    screen - screen resolution
-    memory - memory stats
-    process - list of running processes (same as ps)
-    disk - total, free, available bytes on disk
-    power - power status (charge, battery temp)
-    all - all of them - or call it with no parameters to get all the information
-    returns:
-    success: dict of info strings by directive name
-    failure: None
-    """
-
-  @abstractmethod
-  def installApp(self, appBundlePath, destPath=None):
-    """
-    external function
-    returns:
-    success: output from agent for inst command
-    failure: None
-    """
-
-  @abstractmethod
-  def uninstallAppAndReboot(self, appName, installPath=None):
-    """
-    external function
-    returns:
-    success: True
-    failure: None
-    """
-
-  @abstractmethod
-  def updateApp(self, appBundlePath, processName=None,
-                destPath=None, ipAddr=None, port=30000):
-    """
-    external function
-    returns:
-    success: text status from command or callback server
-    failure: None
-    """
-
-  @abstractmethod
-  def getCurrentTime(self):
-    """
-    external function
-    returns:
-    success: time in ms
-    failure: None
-    """
-
-  def recordLogcat(self):
-    """
-    external function
-    returns:
-    success: file is created in <testroot>/logcat.log
-    failure:
-    """
-    #TODO: spawn this off in a separate thread/process so we can collect all the logcat information
-
-    # Right now this is just clearing the logcat so we can only see what happens after this call.
-    buf = StringIO.StringIO()
-    self.shell(['/system/bin/logcat', '-c'], buf, root=True)
-
-  def getLogcat(self):
-    """
-    external function
-    returns: data from the local file
-    success: file is in 'filename'
-    failure: None
-    """
-    buf = StringIO.StringIO()
-    if self.shell(["/system/bin/logcat", "-d", "dalvikvm:S", "ConnectivityService:S", "WifiMonitor:S", "WifiStateTracker:S", "wpa_supplicant:S", "NetworkStateTracker:S"], buf, root=True) != 0:
-      return None
-
-    return str(buf.getvalue()[0:-1]).rstrip().split('\r')
-
-  @abstractmethod
-  def chmodDir(self, remoteDir):
-    """
-    external function
-    returns:
-    success: True
-    failure: False
-    """
-
-  @staticmethod
-  def _escapedCommandLine(cmd):
-    """ Utility function to return escaped and quoted version of command line """
-    quotedCmd = []
-
-    for arg in cmd:
-      arg.replace('&', '\&')
-
-      needsQuoting = False
-      for char in [ ' ', '(', ')', '"', '&' ]:
-        if arg.find(char) >= 0:
-          needsQuoting = True
-          break
-      if needsQuoting:
-        arg = '\'%s\'' % arg
-
-      quotedCmd.append(arg)
-
-    return " ".join(quotedCmd)
-
-
-class NetworkTools:
-  def __init__(self):
-    pass
-
-  # Utilities to get the local ip address
-  def getInterfaceIp(self, ifname):
-    if os.name != "nt":
-      import fcntl
-      import struct
-      s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-      return socket.inet_ntoa(fcntl.ioctl(
-                              s.fileno(),
-                              0x8915,  # SIOCGIFADDR
-                              struct.pack('256s', ifname[:15])
-                              )[20:24])
-    else:
-      return None
-
-  def getLanIp(self):
-    try:
-      ip = socket.gethostbyname(socket.gethostname())
-    except socket.gaierror:
-      ip = socket.gethostbyname(socket.gethostname() + ".local") # for Mac OS X
-    if ip.startswith("127.") and os.name != "nt":
-      interfaces = ["eth0","eth1","eth2","wlan0","wlan1","wifi0","ath0","ath1","ppp0"]
-      for ifname in interfaces:
-        try:
-          ip = self.getInterfaceIp(ifname)
-          break;
-        except IOError:
-          pass
-    return ip
-
-  # Gets an open port starting with the seed by incrementing by 1 each time
-  def findOpenPort(self, ip, seed):
-    try:
-      s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-      s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-      connected = False
-      if isinstance(seed, basestring):
-        seed = int(seed)
-      maxportnum = seed + 5000 # We will try at most 5000 ports to find an open one
-      while not connected:
-        try:
-          s.bind((ip, seed))
-          connected = True
-          s.close()
-          break
-        except:
-          if seed > maxportnum:
-            print "Could not find open port after checking 5000 ports"
-          raise
-        seed += 1
-    except:
-      print "Socket error trying to find open port"
-
-    return seed
-
-def _pop_last_line(file):
-  '''
-  Utility function to get the last line from a file (shared between ADB and
-  SUT device managers). Function also removes it from the file. Intended to
-  strip off the return code from a shell command.
-  '''
-  bytes_from_end = 1
-  file.seek(0, 2)
-  length = file.tell() + 1
-  while bytes_from_end < length:
-    file.seek((-1)*bytes_from_end, 2)
-    data = file.read()
-
-    if bytes_from_end == length-1 and len(data) == 0: # no data, return None
-      return None
-
-    if data[0] == '\n' or bytes_from_end == length-1:
-      # found the last line, which should have the return value
-      if data[0] == '\n':
-        data = data[1:]
-
-      # truncate off the return code line
-      file.truncate(length - bytes_from_end)
-      file.seek(0,2)
-      file.write('\0')
-
-      return data
-
-    bytes_from_end += 1
-
-  return None
deleted file mode 100644
--- a/build/mobile/devicemanagerADB.py
+++ /dev/null
@@ -1,900 +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/.
-
-import subprocess
-from devicemanager import DeviceManager, DMError, _pop_last_line
-import re
-import os
-import tempfile
-import time
-
-class DeviceManagerADB(DeviceManager):
-
-  def __init__(self, host=None, port=20701, retrylimit=5, packageName='fennec',
-               adbPath='adb', deviceSerial=None, deviceRoot=None):
-    self.host = host
-    self.port = port
-    self.retrylimit = retrylimit
-    self.retries = 0
-    self._sock = None
-    self.useRunAs = False
-    self.haveRoot = False
-    self.useDDCopy = False
-    self.useZip = False
-    self.packageName = None
-    self.tempDir = None
-    self.deviceRoot = deviceRoot
-
-    # the path to adb, or 'adb' to assume that it's on the PATH
-    self.adbPath = adbPath
-
-    # The serial number of the device to use with adb, used in cases
-    # where multiple devices are being managed by the same adb instance.
-    self.deviceSerial = deviceSerial
-
-    if packageName == 'fennec':
-      if os.getenv('USER'):
-        self.packageName = 'org.mozilla.fennec_' + os.getenv('USER')
-      else:
-        self.packageName = 'org.mozilla.fennec_'
-    elif packageName:
-      self.packageName = packageName
-
-    # verify that we can run the adb command. can't continue otherwise
-    self.verifyADB()
-
-    # try to connect to the device over tcp/ip if we have a hostname
-    if self.host:
-      self.connectRemoteADB()
-
-    # verify that we can connect to the device. can't continue
-    self.verifyDevice()
-
-    # set up device root
-    self.setupDeviceRoot()
-
-    # Can we use run-as? (currently not required)
-    try:
-      self.verifyRunAs()
-    except DMError:
-      pass
-
-    # Can we run things as root? (currently not required)
-    useRunAsTmp = self.useRunAs
-    self.useRunAs = False
-    try:
-      self.verifyRoot()
-    except DMError:
-      try:
-        self.checkCmd(["root"])
-        # The root command does not fail even if ADB cannot get
-        # root rights (e.g. due to production builds), so we have
-        # to check again ourselves that we have root now.
-        self.verifyRoot()
-      except DMError:
-        if useRunAsTmp:
-          print "restarting as root failed, but run-as available"
-        else:
-          print "restarting as root failed"
-    self.useRunAs = useRunAsTmp
-
-    # can we use zip to speed up some file operations? (currently not
-    # required)
-    try:
-      self.verifyZip()
-    except DMError:
-      pass
-
-  def __del__(self):
-    if self.host:
-      self.disconnectRemoteADB()
-
-  # external function: executes shell command on device.
-  # timeout is specified in seconds, and if no timeout is given, 
-  # we will run until the script returns
-  # returns:
-  # success: <return code>
-  # failure: None
-  def shell(self, cmd, outputfile, env=None, cwd=None, timeout=None, root=False):
-    # FIXME: this function buffers all output of the command into memory,
-    # always. :(
-
-    # Getting the return code is more complex than you'd think because adb
-    # doesn't actually return the return code from a process, so we have to
-    # capture the output to get it
-    if root:
-      cmdline = "su -c \"%s\"" % self._escapedCommandLine(cmd)
-    else:
-      cmdline = self._escapedCommandLine(cmd)
-    cmdline += "; echo $?"
-
-    # prepend cwd and env to command if necessary
-    if cwd:
-      cmdline = "cd %s; %s" % (cwd, cmdline)
-    if env:
-      envstr = '; '.join(map(lambda x: 'export %s=%s' % (x[0], x[1]), env.iteritems()))
-      cmdline = envstr + "; " + cmdline
-
-    # all output should be in stdout
-    args=[self.adbPath]
-    if self.deviceSerial:
-        args.extend(['-s', self.deviceSerial])
-    args.extend(["shell", cmdline])
-    proc = subprocess.Popen(args,
-                            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-    if timeout:
-        timeout = int(timeout)
-        start_time = time.time()
-        ret_code = proc.poll()
-        while ((time.time() - start_time) <= timeout) and ret_code == None:
-            time.sleep(1)
-            ret_code = proc.poll()
-        if ret_code == None:
-            proc.kill()
-            raise DMError("Timeout exceeded for shell call")
-    (stdout, stderr) = proc.communicate()
-    outputfile.write(stdout.rstrip('\n'))
-
-    lastline = _pop_last_line(outputfile)
-    if lastline:
-      m = re.search('([0-9]+)', lastline)
-      if m:
-        return_code = m.group(1)
-        outputfile.seek(-2, 2)
-        outputfile.truncate() # truncate off the return code
-        return int(return_code)
-
-    return None
-
-  def connectRemoteADB(self):
-    self.checkCmd(["connect", self.host + ":" + str(self.port)])
-
-  def disconnectRemoteADB(self):
-    self.checkCmd(["disconnect", self.host + ":" + str(self.port)])
-
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
-  def pushFile(self, localname, destname):
-    try:
-      if (os.name == "nt"):
-        destname = destname.replace('\\', '/')
-      if (self.useRunAs):
-        remoteTmpFile = self.getTempDir() + "/" + os.path.basename(localname)
-        self.checkCmd(["push", os.path.realpath(localname), remoteTmpFile])
-        if self.useDDCopy:
-          self.checkCmdAs(["shell", "dd", "if=" + remoteTmpFile, "of=" + destname])
-        else:
-          self.checkCmdAs(["shell", "cp", remoteTmpFile, destname])
-        self.checkCmd(["shell", "rm", remoteTmpFile])
-      else:
-        self.checkCmd(["push", os.path.realpath(localname), destname])
-      if (self.isDir(destname)):
-        destname = destname + "/" + os.path.basename(localname)
-      return True
-    except:
-      return False
-
-  # external function
-  # returns:
-  #  success: directory name
-  #  failure: None
-  def mkDir(self, name):
-    try:
-      result = self.runCmdAs(["shell", "mkdir", name]).stdout.read()
-      if 'read-only file system' in result.lower():
-        return None
-      if 'file exists' in result.lower():
-        return name
-      return name
-    except:
-      return None
-
-  # push localDir from host to remoteDir on the device
-  # external function
-  # returns:
-  #  success: remoteDir
-  #  failure: None
-  def pushDir(self, localDir, remoteDir):
-    # adb "push" accepts a directory as an argument, but if the directory
-    # contains symbolic links, the links are pushed, rather than the linked
-    # files; we either zip/unzip or push file-by-file to get around this 
-    # limitation
-    try:
-      if (not self.dirExists(remoteDir)):
-        self.mkDirs(remoteDir+"/x")
-      if (self.useZip):
-        try:
-          localZip = tempfile.mktemp()+".zip"
-          remoteZip = remoteDir + "/adbdmtmp.zip"
-          subprocess.check_output(["zip", "-r", localZip, '.'], cwd=localDir)
-          self.pushFile(localZip, remoteZip)
-          os.remove(localZip)
-          data = self.runCmdAs(["shell", "unzip", "-o", remoteZip, "-d", remoteDir]).stdout.read()
-          self.checkCmdAs(["shell", "rm", remoteZip])
-          if (re.search("unzip: exiting", data) or re.search("Operation not permitted", data)):
-            raise Exception("unzip failed, or permissions error")
-        except:
-          print "zip/unzip failure: falling back to normal push"
-          self.useZip = False
-          self.pushDir(localDir, remoteDir)
-      else:
-        for root, dirs, files in os.walk(localDir, followlinks=True):
-          relRoot = os.path.relpath(root, localDir)
-          for file in files:
-            localFile = os.path.join(root, file)
-            remoteFile = remoteDir + "/"
-            if (relRoot!="."):
-              remoteFile = remoteFile + relRoot + "/"
-            remoteFile = remoteFile + file
-            self.pushFile(localFile, remoteFile)
-          for dir in dirs:
-            targetDir = remoteDir + "/"
-            if (relRoot!="."):
-              targetDir = targetDir + relRoot + "/"
-            targetDir = targetDir + dir
-            if (not self.dirExists(targetDir)):
-              self.mkDir(targetDir)
-      return remoteDir
-    except:
-      print "pushing " + localDir + " to " + remoteDir + " failed"
-      return None
-
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
-  def dirExists(self, dirname):
-    return self.isDir(dirname)
-
-  # Because we always have / style paths we make this a lot easier with some
-  # assumptions
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
-  def fileExists(self, filepath):
-    p = self.runCmd(["shell", "ls", "-a", filepath])
-    data = p.stdout.readlines()
-    if (len(data) == 1):
-      if (data[0].rstrip() == filepath):
-        return True
-    return False
-
-  def removeFile(self, filename):
-    return self.runCmd(["shell", "rm", filename]).stdout.read()
-
-  # does a recursive delete of directory on the device: rm -Rf remoteDir
-  # external function
-  # returns:
-  #  success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt"
-  #  failure: None
-  def removeSingleDir(self, remoteDir):
-    return self.runCmd(["shell", "rmdir", remoteDir]).stdout.read()
-
-  # does a recursive delete of directory on the device: rm -Rf remoteDir
-  # external function
-  # returns:
-  #  success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt"
-  #  failure: None
-  def removeDir(self, remoteDir):
-      out = ""
-      if (self.isDir(remoteDir)):
-          files = self.listFiles(remoteDir.strip())
-          for f in files:
-              if (self.isDir(remoteDir.strip() + "/" + f.strip())):
-                  out += self.removeDir(remoteDir.strip() + "/" + f.strip())
-              else:
-                  out += self.removeFile(remoteDir.strip() + "/" + f.strip())
-          out += self.removeSingleDir(remoteDir.strip())
-      else:
-          out += self.removeFile(remoteDir.strip())
-      return out
-
-  def isDir(self, remotePath):
-      p = self.runCmd(["shell", "ls", "-a", remotePath + '/'])
-
-      data = p.stdout.readlines()
-      if len(data) == 1:
-          res = data[0]
-          if "Not a directory" in res or "No such file or directory" in res:
-              return False
-
-      return True
-
-  def listFiles(self, rootdir):
-      p = self.runCmd(["shell", "ls", "-a", rootdir])
-      data = p.stdout.readlines()
-      data[:] = [item.rstrip('\r\n') for item in data]
-      if (len(data) == 1):
-          if (data[0] == rootdir):
-              return []
-          if (data[0].find("No such file or directory") != -1):
-              return []
-          if (data[0].find("Not a directory") != -1):
-              return []
-          if (data[0].find("Permission denied") != -1):
-              return []
-          if (data[0].find("opendir failed") != -1):
-              return []
-      return data
-
-  # external function
-  # returns:
-  #  success: array of process tuples
-  #  failure: []
-  def getProcessList(self):
-    p = self.runCmd(["shell", "ps"])
-      # first line is the headers
-    p.stdout.readline()
-    proc = p.stdout.readline()
-    ret = []
-    while (proc):
-      els = proc.split()
-      ret.append(list([els[1], els[len(els) - 1], els[0]]))
-      proc =  p.stdout.readline()
-    return ret
-
-  # external function
-  # DEPRECATED: Use shell() or launchApplication() for new code
-  # returns:
-  #  success: pid
-  #  failure: None
-  def fireProcess(self, appname, failIfRunning=False):
-    #strip out env vars
-    parts = appname.split('"');
-    if (len(parts) > 2):
-      parts = parts[2:]
-    return self.launchProcess(parts, failIfRunning)
-
-  # external function
-  # DEPRECATED: Use shell() or launchApplication() for new code
-  # returns:
-  #  success: output filename
-  #  failure: None
-  def launchProcess(self, cmd, outputFile = "process.txt", cwd = '', env = '', failIfRunning=False):
-    if cmd[0] == "am":
-      self.checkCmd(["shell"] + cmd)
-      return outputFile
-
-    acmd = ["shell", "am", "start", "-W"]
-    cmd = ' '.join(cmd).strip()
-    i = cmd.find(" ")
-    # SUT identifies the URL by looking for :\\ -- another strategy to consider
-    re_url = re.compile('^[http|file|chrome|about].*')
-    last = cmd.rfind(" ")
-    uri = ""
-    args = ""
-    if re_url.match(cmd[last:].strip()):
-      args = cmd[i:last].strip()
-      uri = cmd[last:].strip()
-    else:
-      args = cmd[i:].strip()
-    acmd.append("-n")
-    acmd.append(cmd[0:i] + "/.App")
-    if args != "":
-      acmd.append("--es")
-      acmd.append("args")
-      acmd.append(args)
-    if env != '' and env != None:
-      envCnt = 0
-      # env is expected to be a dict of environment variables
-      for envkey, envval in env.iteritems():
-        acmd.append("--es")
-        acmd.append("env" + str(envCnt))
-        acmd.append(envkey + "=" + envval);
-        envCnt += 1
-    if uri != "":
-      acmd.append("-d")
-      acmd.append(''.join(['\'',uri, '\'']));
-    print acmd
-    self.checkCmd(acmd)
-    return outputFile
-
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
-  def killProcess(self, appname, forceKill=False):
-    procs = self.getProcessList()
-    didKillProcess = False
-    for (pid, name, user) in procs:
-      if name == appname:
-         args = ["shell", "kill"]
-         if forceKill:
-           args.append("-9")
-         args.append(pid)
-         p = self.runCmdAs(args)
-         p.communicate()
-         if p.returncode == 0:
-             didKillProcess = True
-
-    return didKillProcess
-
-  # external function
-  # returns:
-  #  success: filecontents
-  #  failure: None
-  def catFile(self, remoteFile):
-    #p = self.runCmd(["shell", "cat", remoteFile])
-    #return p.stdout.read()
-    return self.getFile(remoteFile)
-
-  # external function
-  # returns:
-  #  success: output of pullfile, string
-  #  failure: None
-  def pullFile(self, remoteFile):
-    #return self.catFile(remoteFile)
-    return self.getFile(remoteFile)
-
-  # copy file from device (remoteFile) to host (localFile)
-  # external function
-  # returns:
-  #  success: output of pullfile, string
-  #  failure: None
-  def getFile(self, remoteFile, localFile = 'tmpfile_dm_adb'):
-    # TODO: add debug flags and allow for printing stdout
-    # self.runCmd(["pull", remoteFile, localFile])
-    try:
-
-      # First attempt to pull file regularly
-      outerr = self.runCmd(["pull",  remoteFile, localFile]).communicate()
-
-      # Now check stderr for errors
-      if outerr[1]:
-        errl = outerr[1].splitlines()
-        if (len(errl) == 1):
-          if (((errl[0].find("Permission denied") != -1)
-            or (errl[0].find("does not exist") != -1))
-            and self.useRunAs):
-            # If we lack permissions to read but have run-as, then we should try
-            # to copy the file to a world-readable location first before attempting
-            # to pull it again.
-            remoteTmpFile = self.getTempDir() + "/" + os.path.basename(remoteFile)
-            self.checkCmdAs(["shell", "dd", "if=" + remoteFile, "of=" + remoteTmpFile])
-            self.checkCmdAs(["shell", "chmod", "777", remoteTmpFile])
-            self.runCmd(["pull",  remoteTmpFile, localFile]).stdout.read()
-            # Clean up temporary file
-            self.checkCmdAs(["shell", "rm", remoteTmpFile])
-
-      f = open(localFile)
-      ret = f.read()
-      f.close()
-      return ret
-    except:
-      return None
-
-  # copy directory structure from device (remoteDir) to host (localDir)
-  # external function
-  # checkDir exists so that we don't create local directories if the
-  # remote directory doesn't exist but also so that we don't call isDir
-  # twice when recursing.
-  # returns:
-  #  success: list of files, string
-  #  failure: None
-  def getDirectory(self, remoteDir, localDir, checkDir=True):
-    ret = []
-    p = self.runCmd(["pull", remoteDir, localDir])
-    p.stdout.readline()
-    line = p.stdout.readline()
-    while (line):
-      els = line.split()
-      f = els[len(els) - 1]
-      i = f.find(localDir)
-      if (i != -1):
-        if (localDir[len(localDir) - 1] != '/'):
-          i = i + 1
-        f = f[i + len(localDir):]
-      i = f.find("/")
-      if (i > 0):
-        f = f[0:i]
-      ret.append(f)
-      line =  p.stdout.readline()
-    #the last line is a summary
-    if (len(ret) > 0):
-      ret.pop()
-    return ret
-
-
-
-  # true/false check if the two files have the same md5 sum
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
-  def validateFile(self, remoteFile, localFile):
-    return self.getRemoteHash(remoteFile) == self.getLocalHash(localFile)
-
-  # return the md5 sum of a remote file
-  # internal function
-  # returns:
-  #  success: MD5 hash for given filename
-  #  failure: None
-  def getRemoteHash(self, filename):
-    data = self.runCmd(["shell", "ls", "-l", filename]).stdout.read()
-    return data.split()[3]
-
-  def getLocalHash(self, filename):
-    data = subprocess.Popen(["ls", "-l", filename], stdout=subprocess.PIPE).stdout.read()
-    return data.split()[4]
-
-  # Internal method to setup the device root and cache its value
-  def setupDeviceRoot(self):
-    # if self.deviceRoot is already set, create it if necessary, and use it
-    if self.deviceRoot:
-      if not self.dirExists(self.deviceRoot):
-        if not self.mkDir(self.deviceRoot):
-          raise DMError("Unable to create device root %s" % self.deviceRoot)
-      return
-
-    # /mnt/sdcard/tests is preferred to /data/local/tests, but this can be
-    # over-ridden by creating /data/local/tests
-    testRoot = "/data/local/tests"
-    if (self.dirExists(testRoot)):
-      self.deviceRoot = testRoot
-      return
-
-    for (basePath, subPath) in [('/mnt/sdcard', 'tests'),
-                                ('/data/local', 'tests')]:
-      if self.dirExists(basePath):
-        testRoot = os.path.join(basePath, subPath)
-        if self.mkDir(testRoot):
-          self.deviceRoot = testRoot
-          return
-
-    raise DMError("Unable to set up device root as /mnt/sdcard/tests "
-                  "or /data/local/tests")
-
-  # Gets the device root for the testing area on the device
-  # For all devices we will use / type slashes and depend on the device-agent
-  # to sort those out.  The agent will return us the device location where we
-  # should store things, we will then create our /tests structure relative to
-  # that returned path.
-  # Structure on the device is as follows:
-  # /tests
-  #       /<fennec>|<firefox>  --> approot
-  #       /profile
-  #       /xpcshell
-  #       /reftest
-  #       /mochitest
-  #
-  # external function
-  # returns:
-  #  success: path for device root
-  #  failure: None
-  def getDeviceRoot(self):
-    return self.deviceRoot
-
-  # Gets the temporary directory we are using on this device
-  # base on our device root, ensuring also that it exists.
-  #
-  # internal function
-  # returns:
-  #  success: path for temporary directory
-  #  failure: None
-  def getTempDir(self):
-    # Cache result to speed up operations depending
-    # on the temporary directory.
-    if self.tempDir == None:
-      self.tempDir = self.getDeviceRoot() + "/tmp"
-      if (not self.dirExists(self.tempDir)):
-        return self.mkDir(self.tempDir)
-
-    return self.tempDir
-
-  # Either we will have /tests/fennec or /tests/firefox but we will never have
-  # both.  Return the one that exists
-  # TODO: ensure we can support org.mozilla.firefox
-  # external function
-  # returns:
-  #  success: path for app root
-  #  failure: None
-  def getAppRoot(self, packageName):
-    devroot = self.getDeviceRoot()
-    if (devroot == None):
-      return None
-
-    if (packageName and self.dirExists('/data/data/' + packageName)):
-      self.packageName = packageName
-      return '/data/data/' + packageName
-    elif (self.packageName and self.dirExists('/data/data/' + self.packageName)):
-      return '/data/data/' + self.packageName
-
-    # Failure (either not installed or not a recognized platform)
-    print "devicemanagerADB: getAppRoot failed"
-    return None
-
-  # Gets the directory location on the device for a specific test type
-  # Type is one of: xpcshell|reftest|mochitest
-  # external function
-  # returns:
-  #  success: path for test root
-  #  failure: None
-  def getTestRoot(self, type):
-    devroot = self.getDeviceRoot()
-    if (devroot == None):
-      return None
-
-    if (re.search('xpcshell', type, re.I)):
-      self.testRoot = devroot + '/xpcshell'
-    elif (re.search('?(i)reftest', type)):
-      self.testRoot = devroot + '/reftest'
-    elif (re.search('?(i)mochitest', type)):
-      self.testRoot = devroot + '/mochitest'
-    return self.testRoot
-
-
-  # external function
-  # returns:
-  #  success: status from test agent
-  #  failure: None
-  def reboot(self, wait = False):
-    ret = self.runCmd(["reboot"]).stdout.read()
-    if (not wait):
-      return "Success"
-    countdown = 40
-    while (countdown > 0):
-      countdown
-      try:
-        self.checkCmd(["wait-for-device", "shell", "ls", "/sbin"])
-        return ret
-      except:
-        try:
-          self.checkCmd(["root"])
-        except:
-          time.sleep(1)
-          print "couldn't get root"
-    return "Success"
-
-  # external function
-  # returns:
-  #  success: text status from command or callback server
-  #  failure: None
-  def updateApp(self, appBundlePath, processName=None, destPath=None, ipAddr=None, port=30000):
-    return self.runCmd(["install", "-r", appBundlePath]).stdout.read()
-
-  # external function
-  # returns:
-  #  success: time in ms
-  #  failure: None
-  def getCurrentTime(self):
-    timestr = self.runCmd(["shell", "date", "+%s"]).stdout.read().strip()
-    if (not timestr or not timestr.isdigit()):
-        return None
-    return str(int(timestr)*1000)
-
-  # Returns information about the device:
-  # Directive indicates the information you want to get, your choices are:
-  # os - name of the os
-  # id - unique id of the device
-  # uptime - uptime of the device
-  # systime - system time of the device
-  # screen - screen resolution
-  # memory - memory stats
-  # process - list of running processes (same as ps)
-  # disk - total, free, available bytes on disk
-  # power - power status (charge, battery temp)
-  # all - all of them - or call it with no parameters to get all the information
-  ### Note that uptimemillis is NOT supported, as there is no way to get this
-  ### data from the shell.
-  # returns:
-  #   success: dict of info strings by directive name
-  #   failure: {}
-  def getInfo(self, directive="all"):
-    ret = {}
-    if (directive == "id" or directive == "all"):
-      ret["id"] = self.runCmd(["get-serialno"]).stdout.read()
-    if (directive == "os" or directive == "all"):
-      ret["os"] = self.runCmd(["shell", "getprop", "ro.build.display.id"]).stdout.read()
-    if (directive == "uptime" or directive == "all"):
-      utime = self.runCmd(["shell", "uptime"]).stdout.read()
-      if (not utime):
-        raise DMError("error getting uptime")
-      utime = utime[9:]
-      hours = utime[0:utime.find(":")]
-      utime = utime[utime[1:].find(":") + 2:]
-      minutes = utime[0:utime.find(":")]
-      utime = utime[utime[1:].find(":") +  2:]
-      seconds = utime[0:utime.find(",")]
-      ret["uptime"] = ["0 days " + hours + " hours " + minutes + " minutes " + seconds + " seconds"]
-    if (directive == "process" or directive == "all"):
-      ret["process"] = self.runCmd(["shell", "ps"]).stdout.read()
-    if (directive == "systime" or directive == "all"):
-      ret["systime"] = self.runCmd(["shell", "date"]).stdout.read()
-    print ret
-    return ret
-
-  def runCmd(self, args):
-    # If we are not root but have run-as, and we're trying to execute
-    # a shell command then using run-as is the best we can do
-    finalArgs = [self.adbPath]
-    if self.deviceSerial:
-      finalArgs.extend(['-s', self.deviceSerial])
-    if (not self.haveRoot and self.useRunAs and args[0] == "shell" and args[1] != "run-as"):
-      args.insert(1, "run-as")
-      args.insert(2, self.packageName)
-    finalArgs.extend(args)
-    return subprocess.Popen(finalArgs, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
-  def runCmdAs(self, args):
-    if self.useRunAs:
-      args.insert(1, "run-as")
-      args.insert(2, self.packageName)
-    return self.runCmd(args)
-
-  # timeout is specified in seconds, and if no timeout is given, 
-  # we will run until the script returns
-  def checkCmd(self, args, timeout=None):
-    # If we are not root but have run-as, and we're trying to execute
-    # a shell command then using run-as is the best we can do
-    finalArgs = [self.adbPath]
-    if self.deviceSerial:
-      finalArgs.extend(['-s', self.deviceSerial])
-    if (not self.haveRoot and self.useRunAs and args[0] == "shell" and args[1] != "run-as"):
-      args.insert(1, "run-as")
-      args.insert(2, self.packageName)
-    finalArgs.extend(args)
-    if timeout:
-        timeout = int(timeout)
-        proc = subprocess.Popen(finalArgs)
-        start_time = time.time()
-        ret_code = proc.poll()
-        while ((time.time() - start_time) <= timeout) and ret_code == None:
-            time.sleep(1)
-            ret_code = proc.poll()
-        if ret_code == None:
-            proc.kill()
-            raise DMError("Timeout exceeded for checkCmd call")
-        return ret_code
-    return subprocess.check_call(finalArgs)
-
-  def checkCmdAs(self, args, timeout=None):
-    if (self.useRunAs):
-      args.insert(1, "run-as")
-      args.insert(2, self.packageName)
-    return self.checkCmd(args, timeout)
-
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
-  def chmodDir(self, remoteDir):
-    if (self.isDir(remoteDir)):
-      files = self.listFiles(remoteDir.strip())
-      for f in files:
-        remoteEntry = remoteDir.strip() + "/" + f.strip()
-        if (self.isDir(remoteEntry)):
-          self.chmodDir(remoteEntry)
-        else:
-          self.checkCmdAs(["shell", "chmod", "777", remoteEntry])
-          print "chmod " + remoteEntry
-      self.checkCmdAs(["shell", "chmod", "777", remoteDir])
-      print "chmod " + remoteDir
-    else:
-      self.checkCmdAs(["shell", "chmod", "777", remoteDir.strip()])
-      print "chmod " + remoteDir.strip()
-    return True
-
-  def verifyADB(self):
-    # Check to see if adb itself can be executed.
-    if self.adbPath != 'adb':
-      if not os.access(self.adbPath, os.X_OK):
-        raise DMError("invalid adb path, or adb not executable: %s", self.adbPath)
-
-    try:
-      self.checkCmd(["version"])
-    except os.error, err:
-      raise DMError("unable to execute ADB (%s): ensure Android SDK is installed and adb is in your $PATH" % err)
-    except subprocess.CalledProcessError:
-      raise DMError("unable to execute ADB: ensure Android SDK is installed and adb is in your $PATH")
-
-  def verifyDevice(self):
-    # If there is a device serial number, see if adb is connected to it
-    if self.deviceSerial:
-      deviceStatus = None
-      proc = subprocess.Popen([self.adbPath, "devices"],
-                              stdout=subprocess.PIPE,
-                              stderr=subprocess.STDOUT)
-      for line in proc.stdout:
-        m = re.match('(.+)?\s+(.+)$', line)
-        if m:
-          if self.deviceSerial == m.group(1):
-            deviceStatus = m.group(2)
-      if deviceStatus == None:
-        raise DMError("device not found: %s" % self.deviceSerial)
-      elif deviceStatus != "device":
-        raise DMError("bad status for device %s: %s" % (self.deviceSerial,
-                                                        deviceStatus))
-
-    # Check to see if we can connect to device and run a simple command
-    try:
-      self.checkCmd(["shell", "echo"])
-    except subprocess.CalledProcessError:
-      raise DMError("unable to connect to device: is it plugged in?")
-
-  def verifyRoot(self):
-    # a test to see if we have root privs
-    p = self.runCmd(["shell", "id"])
-    response = p.stdout.readline()
-    response = response.rstrip()
-    response = response.split(' ')
-    if (response[0].find('uid=0') < 0 or response[1].find('gid=0') < 0):
-      print "NOT running as root ", response[0].find('uid=0')
-      raise DMError("not running as root")
-
-    self.haveRoot = True
-
-  def isCpAvailable(self):
-    # Some Android systems may not have a cp command installed,
-    # or it may not be executable by the user.
-    data = self.runCmd(["shell", "cp"]).stdout.read()
-    if (re.search('Usage', data)):
-      return True
-    else:
-      data = self.runCmd(["shell", "dd", "-"]).stdout.read()
-      if (re.search('unknown operand', data)):
-        print "'cp' not found, but 'dd' was found as a replacement"
-        self.useDDCopy = True
-        return True
-      print "unable to execute 'cp' on device; consider installing busybox from Android Market"
-      return False
-
-  def verifyRunAs(self):
-    # If a valid package name is available, and certain other
-    # conditions are met, devicemanagerADB can execute file operations
-    # via the "run-as" command, so that pushed files and directories 
-    # are created by the uid associated with the package, more closely
-    # echoing conditions encountered by Fennec at run time.
-    # Check to see if run-as can be used here, by verifying a 
-    # file copy via run-as.
-    self.useRunAs = False
-    devroot = self.getDeviceRoot()
-    if (self.packageName and self.isCpAvailable() and devroot):
-      tmpDir = self.getTempDir()
-
-      # The problem here is that run-as doesn't cause a non-zero exit code
-      # when failing because of a non-existent or non-debuggable package :(
-      runAsOut = self.runCmd(["shell", "run-as", self.packageName, "mkdir", devroot + "/sanity"]).communicate()[0]
-      if runAsOut.startswith("run-as:") and ("not debuggable" in runAsOut or
-                                             "is unknown" in runAsOut):
-        raise DMError("run-as failed sanity check")
-
-      tmpfile = tempfile.NamedTemporaryFile()
-      self.checkCmd(["push", tmpfile.name, tmpDir + "/tmpfile"])
-      if self.useDDCopy:
-        self.checkCmd(["shell", "run-as", self.packageName, "dd", "if=" + tmpDir + "/tmpfile", "of=" + devroot + "/sanity/tmpfile"])
-      else:
-        self.checkCmd(["shell", "run-as", self.packageName, "cp", tmpDir + "/tmpfile", devroot + "/sanity"])
-      if (self.fileExists(devroot + "/sanity/tmpfile")):
-        print "will execute commands via run-as " + self.packageName
-        self.useRunAs = True
-      self.checkCmd(["shell", "rm", devroot + "/tmp/tmpfile"])
-      self.checkCmd(["shell", "run-as", self.packageName, "rm", "-r", devroot + "/sanity"])
-
-  def isUnzipAvailable(self):
-    data = self.runCmdAs(["shell", "unzip"]).stdout.read()
-    if (re.search('Usage', data)):
-      return True
-    else:
-      return False
-
-  def isLocalZipAvailable(self):
-    try:
-      subprocess.check_call(["zip", "-?"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-    except:
-      return False
-    return True
-
-  def verifyZip(self):
-    # If "zip" can be run locally, and "unzip" can be run remotely, then pushDir
-    # can use these to push just one file per directory -- a significant
-    # optimization for large directories.
-    self.useZip = False
-    if (self.isUnzipAvailable() and self.isLocalZipAvailable()):
-      print "will use zip to push directories"
-      self.useZip = True
-    else:
-      raise DMError("zip not available")
deleted file mode 100644
--- a/build/mobile/devicemanagerSUT.py
+++ /dev/null
@@ -1,1240 +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/.
-
-import select
-import socket
-import SocketServer
-import time
-import os
-import re
-import posixpath
-import subprocess
-from threading import Thread
-import StringIO
-from devicemanager import DeviceManager, FileError, NetworkTools, _pop_last_line
-import errno
-from distutils.version import StrictVersion
-
-class AgentError(Exception):
-  "SUTAgent-specific exception."
-
-  def __init__(self, msg= '', fatal = False):
-    self.msg = msg
-    self.fatal = fatal
-
-  def __str__(self):
-    return self.msg
-
-class DeviceManagerSUT(DeviceManager):
-  host = ''
-  port = 0
-  debug = 2
-  retries = 0
-  tempRoot = os.getcwd()
-  base_prompt = '$>'
-  base_prompt_re = '\$\>'
-  prompt_sep = '\x00'
-  prompt_regex = '.*(' + base_prompt_re + prompt_sep + ')'
-  agentErrorRE = re.compile('^##AGENT-WARNING##\ ?(.*)')
-
-  # TODO: member variable to indicate error conditions.
-  # This should be set to a standard error from the errno module.
-  # So, for example, when an error occurs because of a missing file/directory,
-  # before returning, the function would do something like 'self.error = errno.ENOENT'.
-  # The error would be set where appropriate--so sendCMD() could set socket errors,
-  # pushFile() and other file-related commands could set filesystem errors, etc.
-
-  def __init__(self, host, port = 20701, retrylimit = 5, deviceRoot = None):
-    self.host = host
-    self.port = port
-    self.retrylimit = retrylimit
-    self.retries = 0
-    self._sock = None
-    self.deviceRoot = deviceRoot
-    if self.getDeviceRoot() == None:
-      raise BaseException("Failed to connect to SUT Agent and retrieve the device root.")
-    try:
-      verstring = self.runCmds([{ 'cmd': 'ver' }])
-      self.agentVersion = re.sub('SUTAgentAndroid Version ', '', verstring)
-    except AgentError, err:
-      raise BaseException("Failed to get SUTAgent version")
-
-  def _cmdNeedsResponse(self, cmd):
-    """ Not all commands need a response from the agent:
-        * rebt obviously doesn't get a response
-        * uninstall performs a reboot to ensure starting in a clean state and
-          so also doesn't look for a response
-    """
-    noResponseCmds = [re.compile('^rebt'),
-                      re.compile('^uninst .*$'),
-                      re.compile('^pull .*$')]
-
-    for c in noResponseCmds:
-      if (c.match(cmd)):
-        return False
-
-    # If the command is not in our list, then it gets a response
-    return True
-
-  def _stripPrompt(self, data):
-    '''
-    internal function
-    take a data blob and strip instances of the prompt '$>\x00'
-    '''
-    promptre = re.compile(self.prompt_regex + '.*')
-    retVal = []
-    lines = data.split('\n')
-    for line in lines:
-      foundPrompt = False
-      try:
-        while (promptre.match(line)):
-          foundPrompt = True
-          pieces = line.split(self.prompt_sep)
-          index = pieces.index('$>')
-          pieces.pop(index)
-          line = self.prompt_sep.join(pieces)
-      except(ValueError):
-        pass
-
-      # we don't want to append lines that are blank after stripping the
-      # prompt (those are basically "prompts")
-      if not foundPrompt or line:
-        retVal.append(line)
-
-    return '\n'.join(retVal)
-
-  def _shouldCmdCloseSocket(self, cmd):
-    """ Some commands need to close the socket after they are sent:
-    * rebt
-    * uninst
-    * quit
-    """
-    socketClosingCmds = [re.compile('^quit.*'),
-                         re.compile('^rebt.*'),
-                         re.compile('^uninst .*$')]
-
-    for c in socketClosingCmds:
-      if (c.match(cmd)):
-        return True
-
-    return False
-
-  def sendCmds(self, cmdlist, outputfile, timeout = None):
-    '''
-    a wrapper for _doCmds that loops up to self.retrylimit iterations.
-    this allows us to move the retry logic outside of the _doCmds() to make it
-    easier for debugging in the future.
-    note that since cmdlist is a list of commands, they will all be retried if
-    one fails.  this is necessary in particular for pushFile(), where we don't want
-    to accidentally send extra data if a failure occurs during data transmission.
-    '''
-    if timeout:
-      raise NotImplementedError("'timeout' parameter is not yet supported")
-    while self.retries < self.retrylimit:
-      try:
-        self._doCmds(cmdlist, outputfile, timeout)
-        return
-      except AgentError, err:
-        # re-raise error if it's fatal (i.e. the device got the command but
-        # couldn't execute it). retry otherwise
-        if err.fatal:
-          raise err
-        if self.debug >= 2:
-          print err
-        self.retries += 1
-
-    raise AgentError("unable to connect to %s after %s attempts" % (self.host, self.retrylimit))
-
-  def runCmds(self, cmdlist, timeout = None):
-    '''
-    similar to sendCmds, but just returns any output as a string instead of
-    writing to a file. this is normally what you want to call to send a set
-    of commands to the agent
-    '''
-    outputfile = StringIO.StringIO()
-    self.sendCmds(cmdlist, outputfile, timeout)
-    outputfile.seek(0)
-    return outputfile.read()
-
-  def _doCmds(self, cmdlist, outputfile, timeout):
-    promptre = re.compile(self.prompt_regex + '$')
-    shouldCloseSocket = False
-    recvGuard = 1000
-
-    if not self._sock:
-      try:
-        if self.debug >= 1:
-          print "reconnecting socket"
-        self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-      except socket.error, msg:
-        self._sock = None
-        raise AgentError("unable to create socket: "+str(msg))
-
-      try:
-        self._sock.connect((self.host, int(self.port)))
-        self._sock.recv(1024)
-      except socket.error, msg:
-        self._sock.close()
-        self._sock = None
-        raise AgentError("unable to connect socket: "+str(msg))
-
-    for cmd in cmdlist:
-      cmdline = '%s\r\n' % cmd['cmd']
-
-      try:
-        sent = self._sock.send(cmdline)
-        if sent != len(cmdline):
-          raise AgentError("ERROR: our cmd was %s bytes and we "
-                           "only sent %s" % (len(cmdline), sent))
-        if cmd.get('data'):
-          sent = self._sock.send(cmd['data'])
-          if sent != len(cmd['data']):
-              raise AgentError("ERROR: we had %s bytes of data to send, but "
-                               "only sent %s" % (len(cmd['data']), sent))
-
-        if (self.debug >= 4): print "sent cmd: " + str(cmd['cmd'])
-      except socket.error, msg:
-        self._sock.close()
-        self._sock = None
-        if self.debug >= 1:
-          print "Error sending data to socket. cmd="+str(cmd['cmd'])+"; err="+str(msg)
-        return False
-
-      # Check if the command should close the socket
-      shouldCloseSocket = self._shouldCmdCloseSocket(cmd['cmd'])
-
-      # Handle responses from commands
-      if (self._cmdNeedsResponse(cmd['cmd'])):
-        found = False
-        loopguard = 0
-        data = ""
-
-        while (found == False and (loopguard < recvGuard)):
-          temp = ''
-          if (self.debug >= 4): print "recv'ing..."
-
-          # Get our response
-          try:
-             # Wait up to a second for socket to become ready for reading...
-            if select.select([self._sock], [], [], 1)[0]:
-                temp = self._sock.recv(1024)
-            if (self.debug >= 4): print "response: " + str(temp)
-          except socket.error, err:
-            self._sock.close()
-            self._sock = None
-            # This error shows up with we have our tegra rebooted.
-            if err[0] == errno.ECONNRESET:
-              raise AgentError("Automation error: Error receiving data from socket (possible reboot). cmd=%s; err=%s" % (cmd, err))
-            raise AgentError("Error receiving data from socket. cmd=%s; err=%s" % (cmd, err))
-
-          data += temp
-
-          # If something goes wrong in the agent it will send back a string that
-          # starts with '##AGENT-WARNING##'
-          errorMatch = self.agentErrorRE.match(data)
-          if errorMatch:
-            raise AgentError("Agent Error processing command '%s'; err='%s'" %
-                             (cmd['cmd'], errorMatch.group(1)), fatal=True)
-
-          for line in data.splitlines():
-            if promptre.match(line):
-              found = True
-              data = self._stripPrompt(data)
-              break
-
-          # periodically flush data to output file to make sure it doesn't get
-          # too big/unwieldly
-          if len(data) > 1024:
-              outputfile.write(data[0:1024])
-              data = data[1024:]
-
-          # If we violently lose the connection to the device, this loop tends to spin,
-          # this guard prevents that
-          if (temp == ''):
-            loopguard += 1
-
-        # Write any remaining data to outputfile
-        outputfile.write(data)
-
-    if shouldCloseSocket:
-      try:
-        self._sock.close()
-        self._sock = None
-      except:
-        self._sock = None
-        raise AgentError("Error closing socket")
-
-  # external function: executes shell command on device
-  # returns:
-  # success: <return code>
-  # failure: None
-  def shell(self, cmd, outputfile, env=None, cwd=None, timeout=None, root=False):
-    cmdline = self._escapedCommandLine(cmd)
-    if env:
-      cmdline = '%s %s' % (self.formatEnvString(env), cmdline)
-
-    haveExecSu = (StrictVersion(self.agentVersion) >= StrictVersion('1.13'))
-
-    # Depending on agent version we send one of the following commands here:
-    # * exec (run as normal user)
-    # * execsu (run as privileged user)
-    # * execcwd (run as normal user from specified directory)
-    # * execcwdsu (run as privileged user from specified directory)
-
-    cmd = "exec"
-    if cwd:
-      cmd += "cwd"
-    if root and haveExecSu:
-      cmd += "su"
-
-    try:
-      if cwd:
-        self.sendCmds([{ 'cmd': '%s %s %s' % (cmd, cwd, cmdline) }], outputfile, timeout)
-      else:
-        if (not root) or haveExecSu:
-          self.sendCmds([{ 'cmd': '%s %s' % (cmd, cmdline) }], outputfile, timeout)
-        else:
-          # need to manually inject su -c for backwards compatibility (this may
-          # not work on ICS or above!!)
-          # (FIXME: this backwards compatibility code is really ugly and should
-          # be deprecated at some point in the future)
-          self.sendCmds([ { 'cmd': '%s su -c "%s"' % (cmd, cmdline) }], outputfile,
-                          timeout)
-    except AgentError:
-      return None
-
-    # dig through the output to get the return code
-    lastline = _pop_last_line(outputfile)
-    if lastline:
-      m = re.search('return code \[([0-9]+)\]', lastline)
-      if m:
-        return int(m.group(1))
-
-    # woops, we couldn't find an end of line/return value
-    return None
-
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
-  def pushFile(self, localname, destname):
-    if (os.name == "nt"):
-      destname = destname.replace('\\', '/')
-
-    if (self.debug >= 3): print "in push file with: " + localname + ", and: " + destname
-    if (self.dirExists(destname)):
-      if (not destname.endswith('/')):
-        destname = destname + '/'
-      destname = destname + os.path.basename(localname)
-    if (self.validateFile(destname, localname) == True):
-      if (self.debug >= 3): print "files are validated"
-      return True
-
-    if self.mkDirs(destname) == None:
-      print "unable to make dirs: " + destname
-      return False
-
-    if (self.debug >= 3): print "sending: push " + destname
-
-    filesize = os.path.getsize(localname)
-    f = open(localname, 'rb')
-    data = f.read()
-    f.close()
-
-    try:
-      retVal = self.runCmds([{ 'cmd': 'push ' + destname + ' ' + str(filesize),
-                               'data': data }])
-    except AgentError, e:
-      print "error pushing file: %s" % e.msg
-      return False
-
-    if (self.debug >= 3): print "push returned: " + str(retVal)
-
-    validated = False
-    if (retVal):
-      retline = retVal.strip()
-      if (retline == None):
-        # Then we failed to get back a hash from agent, try manual validation
-        validated = self.validateFile(destname, localname)
-      else:
-        # Then we obtained a hash from push
-        localHash = self.getLocalHash(localname)
-        if (str(localHash) == str(retline)):
-          validated = True
-    else:
-      # We got nothing back from sendCMD, try manual validation
-      validated = self.validateFile(destname, localname)
-
-    if (validated):
-      if (self.debug >= 3): print "Push File Validated!"
-      return True
-    else:
-      if (self.debug >= 2): print "Push File Failed to Validate!"
-      return False
-
-  # external function
-  # returns:
-  #  success: directory name
-  #  failure: None
-  def mkDir(self, name):
-    if (self.dirExists(name)):
-      return name
-    else:
-      try:
-        retVal = self.runCmds([{ 'cmd': 'mkdr ' + name }])
-      except AgentError:
-        retVal = None
-      return retVal
-
-  # push localDir from host to remoteDir on the device
-  # external function
-  # returns:
-  #  success: remoteDir
-  #  failure: None
-  def pushDir(self, localDir, remoteDir):
-    if (self.debug >= 2): print "pushing directory: %s to %s" % (localDir, remoteDir)
-    for root, dirs, files in os.walk(localDir, followlinks=True):
-      parts = root.split(localDir)
-      for file in files:
-        remoteRoot = remoteDir + '/' + parts[1]
-        if (remoteRoot.endswith('/')):
-          remoteName = remoteRoot + file
-        else:
-          remoteName = remoteRoot + '/' + file
-        if (parts[1] == ""): remoteRoot = remoteDir
-        if (self.pushFile(os.path.join(root, file), remoteName) == False):
-          # retry once
-          self.removeFile(remoteName)
-          if (self.pushFile(os.path.join(root, file), remoteName) == False):
-            return None
-    return remoteDir
-
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
-  def dirExists(self, dirname):
-    match = ".*" + dirname.replace('^', '\^') + "$"
-    dirre = re.compile(match)
-    try:
-      data = self.runCmds([ { 'cmd': 'cd ' + dirname }, { 'cmd': 'cwd' }])
-    except AgentError:
-      return False
-
-    found = False
-    for d in data.splitlines():
-      if (dirre.match(d)):
-        found = True
-
-    return found
-
-  # Because we always have / style paths we make this a lot easier with some
-  # assumptions
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
-  def fileExists(self, filepath):
-    s = filepath.split('/')
-    containingpath = '/'.join(s[:-1])
-    listfiles = self.listFiles(containingpath)
-    for f in listfiles:
-      if (f == s[-1]):
-        return True
-    return False
-
-  # list files on the device, requires cd to directory first
-  # external function
-  # returns:
-  #  success: array of filenames, ['file1', 'file2', ...]
-  #  failure: []
-  def listFiles(self, rootdir):
-    rootdir = rootdir.rstrip('/')
-    if (self.dirExists(rootdir) == False):
-      return []
-    try:
-      data = self.runCmds([{ 'cmd': 'cd ' + rootdir }, { 'cmd': 'ls' }])
-    except AgentError:
-      return []
-
-    files = filter(lambda x: x, data.splitlines())
-    if len(files) == 1 and files[0] == '<empty>':
-      # special case on the agent: empty directories return just the string "<empty>"
-      return []
-    return files
-
-  # external function
-  # returns:
-  #  success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt"
-  #  failure: None
-  def removeFile(self, filename):
-    if (self.debug>= 2): print "removing file: " + filename
-    try:
-      retVal = self.runCmds([{ 'cmd': 'rm ' + filename }])
-    except AgentError:
-      return None
-
-    return retVal
-
-  # does a recursive delete of directory on the device: rm -Rf remoteDir
-  # external function
-  # returns:
-  #  success: output of telnet, i.e. "removing file: /mnt/sdcard/tests/test.txt"
-  #  failure: None
-  def removeDir(self, remoteDir):
-    try:
-      retVal = self.runCmds([{ 'cmd': 'rmdr ' + remoteDir }])
-    except AgentError:
-      return None
-
-    return retVal
-
-  # external function
-  # returns:
-  #  success: array of process tuples
-  #  failure: []
-  def getProcessList(self):
-    try:
-      data = self.runCmds([{ 'cmd': 'ps' }])
-    except AgentError:
-      return []
-
-    files = []
-    for line in data.splitlines():
-      if line:
-        pidproc = line.strip().split()
-        if (len(pidproc) == 2):
-          files += [[pidproc[0], pidproc[1]]]
-        elif (len(pidproc) == 3):
-          #android returns <userID> <procID> <procName>
-          files += [[pidproc[1], pidproc[2], pidproc[0]]]
-    return files
-
-  # external function
-  # DEPRECATED: Use shell() or launchApplication() for new code
-  # returns:
-  #  success: pid
-  #  failure: None
-  def fireProcess(self, appname, failIfRunning=False):
-    if (not appname):
-      if (self.debug >= 1): print "WARNING: fireProcess called with no command to run"
-      return None
-
-    if (self.debug >= 2): print "FIRE PROC: '" + appname + "'"
-
-    if (self.processExist(appname) != None):
-      print "WARNING: process %s appears to be running already\n" % appname
-      if (failIfRunning):
-        return None
-
-    try:
-      self.runCmds([{ 'cmd': 'exec ' + appname }])
-    except AgentError:
-      return None
-
-    # The 'exec' command may wait for the process to start and end, so checking
-    # for the process here may result in process = None.
-    process = self.processExist(appname)
-    if (self.debug >= 4): print "got pid: %s for process: %s" % (process, appname)
-
-    return process
-
-  # external function
-  # DEPRECATED: Use shell() or launchApplication() for new code
-  # returns:
-  #  success: output filename
-  #  failure: None
-  def launchProcess(self, cmd, outputFile = "process.txt", cwd = '', env = '', failIfRunning=False):
-    if not cmd:
-      if (self.debug >= 1): print "WARNING: launchProcess called without command to run"
-      return None
-
-    cmdline = subprocess.list2cmdline(cmd)
-    if (outputFile == "process.txt" or outputFile == None):
-      outputFile = self.getDeviceRoot();
-      if outputFile is None:
-        return None
-      outputFile += "/process.txt"
-      cmdline += " > " + outputFile
-
-    # Prepend our env to the command
-    cmdline = '%s %s' % (self.formatEnvString(env), cmdline)
-
-    if self.fireProcess(cmdline, failIfRunning) is None:
-      return None
-    return outputFile
-
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
-  def killProcess(self, appname, forceKill=False):
-    if forceKill:
-      print "WARNING: killProcess(): forceKill parameter unsupported on SUT"
-    try:
-      self.runCmds([{ 'cmd': 'kill ' + appname }])
-    except AgentError:
-      return False
-
-    return True
-
-  # external function
-  # returns:
-  #  success: tmpdir, string
-  #  failure: None
-  def getTempDir(self):
-    try:
-      data = self.runCmds([{ 'cmd': 'tmpd' }])
-    except AgentError:
-      return None
-
-    return data.strip()
-
-  # external function
-  # returns:
-  #  success: filecontents
-  #  failure: None
-  def catFile(self, remoteFile):
-    try:
-      data = self.runCmds([{ 'cmd': 'cat ' + remoteFile }])
-    except AgentError:
-      return None
-
-    return data
-
-  # external function
-  # returns:
-  #  success: output of pullfile, string
-  #  failure: None
-  def pullFile(self, remoteFile):
-    """Returns contents of remoteFile using the "pull" command.
-    The "pull" command is different from other commands in that DeviceManager
-    has to read a certain number of bytes instead of just reading to the
-    next prompt.  This is more robust than the "cat" command, which will be
-    confused if the prompt string exists within the file being catted.
-    However it means we can't use the response-handling logic in sendCMD().
-    """
-
-    def err(error_msg):
-        err_str = 'error returned from pull: %s' % error_msg
-        print err_str
-        self._sock = None
-        raise FileError(err_str)
-
-    # FIXME: We could possibly move these socket-reading functions up to
-    # the class level if we wanted to refactor sendCMD().  For now they are
-    # only used to pull files.
-
-    def uread(to_recv, error_msg):
-      """ unbuffered read """
-      try:
-        data = self._sock.recv(to_recv)
-        if not data:
-          err(error_msg)
-          return None
-        return data
-      except:
-        err(error_msg)
-        return None
-
-    def read_until_char(c, buffer, error_msg):
-      """ read until 'c' is found; buffer rest """
-      while not '\n' in buffer:
-        data = uread(1024, error_msg)
-        if data == None:
-          err(error_msg)
-          return ('', '', '')
-        buffer += data
-      return buffer.partition(c)
-
-    def read_exact(total_to_recv, buffer, error_msg):
-      """ read exact number of 'total_to_recv' bytes """
-      while len(buffer) < total_to_recv:
-        to_recv = min(total_to_recv - len(buffer), 1024)
-        data = uread(to_recv, error_msg)
-        if data == None:
-          return None
-        buffer += data
-      return buffer
-
-    prompt = self.base_prompt + self.prompt_sep
-    buffer = ''
-
-    # expected return value:
-    # <filename>,<filesize>\n<filedata>
-    # or, if error,
-    # <filename>,-1\n<error message>
-    try:
-      # just send the command first, we read the response inline below
-      self.runCmds([{ 'cmd': 'pull ' + remoteFile }])
-    except AgentError:
-      return None
-
-    # read metadata; buffer the rest
-    metadata, sep, buffer = read_until_char('\n', buffer, 'could not find metadata')
-    if not metadata:
-      return None
-    if self.debug >= 3:
-      print 'metadata: %s' % metadata
-
-    filename, sep, filesizestr = metadata.partition(',')
-    if sep == '':
-      err('could not find file size in returned metadata')
-      return None
-    try:
-        filesize = int(filesizestr)
-    except ValueError:
-      err('invalid file size in returned metadata')
-      return None
-
-    if filesize == -1:
-      # read error message
-      error_str, sep, buffer = read_until_char('\n', buffer, 'could not find error message')
-      if not error_str:
-        return None
-      # prompt should follow
-      read_exact(len(prompt), buffer, 'could not find prompt')
-      print "DeviceManager: error pulling file '%s': %s" % (remoteFile, error_str)
-      return None
-
-    # read file data
-    total_to_recv = filesize + len(prompt)
-    buffer = read_exact(total_to_recv, buffer, 'could not get all file data')
-    if buffer == None:
-      return None
-    if buffer[-len(prompt):] != prompt:
-      err('no prompt found after file data--DeviceManager may be out of sync with agent')
-      return buffer
-    return buffer[:-len(prompt)]
-
-  # copy file from device (remoteFile) to host (localFile)
-  # external function
-  # returns:
-  #  success: output of pullfile, string
-  #  failure: None
-  def getFile(self, remoteFile, localFile = ''):
-    if localFile == '':
-      localFile = os.path.join(self.tempRoot, "temp.txt")
-
-    try:
-      retVal = self.pullFile(remoteFile)
-    except:
-      return None
-
-    if (retVal is None):
-      return None
-
-    fhandle = open(localFile, 'wb')
-    fhandle.write(retVal)
-    fhandle.close()
-    if not self.validateFile(remoteFile, localFile):
-      print 'failed to validate file when downloading %s!' % remoteFile
-      return None
-    return retVal
-
-  # copy directory structure from device (remoteDir) to host (localDir)
-  # external function
-  # checkDir exists so that we don't create local directories if the
-  # remote directory doesn't exist but also so that we don't call isDir
-  # twice when recursing.
-  # returns:
-  #  success: list of files, string
-  #  failure: None
-  def getDirectory(self, remoteDir, localDir, checkDir=True):
-    if (self.debug >= 2): print "getting files in '" + remoteDir + "'"
-    if checkDir:
-      try:
-        is_dir = self.isDir(remoteDir)
-      except FileError:
-        return None
-      if not is_dir:
-        return None
-
-    filelist = self.listFiles(remoteDir)
-    if (self.debug >= 3): print filelist
-    if not os.path.exists(localDir):
-      os.makedirs(localDir)
-
-    for f in filelist:
-      if f == '.' or f == '..':
-        continue
-      remotePath = remoteDir + '/' + f
-      localPath = os.path.join(localDir, f)
-      try:
-        is_dir = self.isDir(remotePath)
-      except FileError:
-        print 'isdir failed on file "%s"; continuing anyway...' % remotePath
-        continue
-      if is_dir:
-        if (self.getDirectory(remotePath, localPath, False) == None):
-          print 'failed to get directory "%s"' % remotePath
-          return None
-      else:
-        # It's sometimes acceptable to have getFile() return None, such as
-        # when the agent encounters broken symlinks.
-        # FIXME: This should be improved so we know when a file transfer really
-        # failed.
-        if self.getFile(remotePath, localPath) == None:
-          print 'failed to get file "%s"; continuing anyway...' % remotePath
-    return filelist
-
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
-  #  Throws a FileError exception when null (invalid dir/filename)
-  def isDir(self, remotePath):
-    try:
-      data = self.runCmds([{ 'cmd': 'isdir ' + remotePath }])
-    except AgentError:
-      # normally there should be no error here; a nonexistent file/directory will
-      # return the string "<filename>: No such file or directory".
-      # However, I've seen AGENT-WARNING returned before.
-      return False
-
-    retVal = data.strip()
-    if not retVal:
-      raise FileError('isdir returned null')
-    return retVal == 'TRUE'
-
-  # true/false check if the two files have the same md5 sum
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
-  def validateFile(self, remoteFile, localFile):
-    remoteHash = self.getRemoteHash(remoteFile)
-    localHash = self.getLocalHash(localFile)
-
-    if (remoteHash == None):
-      return False
-
-    if (remoteHash == localHash):
-      return True
-
-    return False
-
-  # return the md5 sum of a remote file
-  # internal function
-  # returns:
-  #  success: MD5 hash for given filename
-  #  failure: None
-  def getRemoteHash(self, filename):
-    try:
-      data = self.runCmds([{ 'cmd': 'hash ' + filename }])
-    except AgentError:
-      return None
-
-    retVal = None
-    if data:
-      retVal = data.strip()
-    if self.debug >= 3:
-      print "remote hash returned: '%s'" % retVal
-    return retVal
-
-  # Gets the device root for the testing area on the device
-  # For all devices we will use / type slashes and depend on the device-agent
-  # to sort those out.  The agent will return us the device location where we
-  # should store things, we will then create our /tests structure relative to
-  # that returned path.
-  # Structure on the device is as follows:
-  # /tests
-  #       /<fennec>|<firefox>  --> approot
-  #       /profile
-  #       /xpcshell
-  #       /reftest
-  #       /mochitest
-  #
-  # external function
-  # returns:
-  #  success: path for device root
-  #  failure: None
-  def getDeviceRoot(self):
-    if self.deviceRoot:
-      deviceRoot = self.deviceRoot
-    else:
-      try:
-        data = self.runCmds([{ 'cmd': 'testroot' }])
-      except:
-        return None
-
-      deviceRoot = data.strip() + '/tests'
-
-    if (not self.dirExists(deviceRoot)):
-      if (self.mkDir(deviceRoot) == None):
-        return None
-
-    self.deviceRoot = deviceRoot
-    return self.deviceRoot
-
-  def getAppRoot(self, packageName):
-    try:
-      data = self.runCmds([{ 'cmd': 'getapproot '+packageName }])
-    except:
-      return None
-
-    return data.strip()
-
-  # external function
-  # returns:
-  #  success: output of unzip command
-  #  failure: None
-  def unpackFile(self, file_path, dest_dir=None):
-    devroot = self.getDeviceRoot()
-    if (devroot == None):
-      return None
-
-    # if no dest_dir is passed in just set it to file_path's folder
-    if not dest_dir:
-      dest_dir = posixpath.dirname(file_path)
-
-    if dest_dir[-1] != '/':
-      dest_dir += '/'
-
-    try:
-      data = self.runCmds([{ 'cmd': 'unzp %s %s' % (file_path, dest_dir)}])
-    except AgentError:
-      return None
-
-    return data
-
-  # external function
-  # returns:
-  #  success: status from test agent
-  #  failure: None
-  def reboot(self, ipAddr=None, port=30000):
-    cmd = 'rebt'
-
-    if (self.debug > 3): print "INFO: sending rebt command"
-
-    if (ipAddr is not None):
-    #create update.info file:
-      try:
-        destname = '/data/data/com.mozilla.SUTAgentAndroid/files/update.info'
-        data = "%s,%s\rrebooting\r" % (ipAddr, port)
-        self.runCmds([{ 'cmd': 'push %s %s' % (destname, len(data)),
-                        'data': data }])
-      except AgentError:
-        return None
-
-      ip, port = self.getCallbackIpAndPort(ipAddr, port)
-      cmd += " %s %s" % (ip, port)
-      # Set up our callback server
-      callbacksvr = callbackServer(ip, port, self.debug)
-
-    try:
-      status = self.runCmds([{ 'cmd': cmd }])
-    except AgentError:
-      return None
-
-    if (ipAddr is not None):
-      status = callbacksvr.disconnect()
-
-    if (self.debug > 3): print "INFO: rebt- got status back: " + str(status)
-    return status
-
-  # Returns information about the device:
-  # Directive indicates the information you want to get, your choices are:
-  # os - name of the os
-  # id - unique id of the device
-  # uptime - uptime of the device
-  # uptimemillis - uptime of the device in milliseconds (SUTAgent 1.11+)
-  # systime - system time of the device
-  # screen - screen resolution
-  # rotation - rotation of the device (in degrees)
-  # memory - memory stats
-  # process - list of running processes (same as ps)
-  # disk - total, free, available bytes on disk
-  # power - power status (charge, battery temp)
-  # all - all of them - or call it with no parameters to get all the information
-  # returns:
-  #   success: dict of info strings by directive name
-  #   failure: {}
-  def getInfo(self, directive=None):
-    data = None
-    result = {}
-    collapseSpaces = re.compile('  +')
-
-    directives = ['os','id','uptime','uptimemillis','systime','screen',
-                  'rotation','memory','process','disk','power']
-    if (directive in directives):
-      directives = [directive]
-
-    for d in directives:
-      try:
-        data = self.runCmds([{ 'cmd': 'info ' + d }])
-      except AgentError:
-        return result
-
-      if (data is None):
-        continue
-      data = collapseSpaces.sub(' ', data)
-      result[d] = data.split('\n')
-
-    # Get rid of any 0 length members of the arrays
-    for k, v in result.iteritems():
-      result[k] = filter(lambda x: x != '', result[k])
-
-    # Format the process output
-    if 'process' in result:
-      proclist = []
-      for l in result['process']:
-        if l:
-          proclist.append(l.split('\t'))
-      result['process'] = proclist
-
-    if (self.debug >= 3): print "results: " + str(result)
-    return result
-
-  """
-  Installs the application onto the device
-  Application bundle - path to the application bundle on the device
-  Destination - destination directory of where application should be
-                installed to (optional)
-  Returns None for success, or output if known failure
-  """
-  # external function
-  # returns:
-  #  success: output from agent for inst command
-  #  failure: None
-  def installApp(self, appBundlePath, destPath=None):
-    cmd = 'inst ' + appBundlePath
-    if destPath:
-      cmd += ' ' + destPath
-    try:
-      data = self.runCmds([{ 'cmd': cmd }])
-    except AgentError:
-      return None
-
-    f = re.compile('Failure')
-    for line in data.split():
-      if (f.match(line)):
-        return data
-    return None
-
-  """
-  Uninstalls the named application from device and causes a reboot.
-  Takes an optional argument of installation path - the path to where the application
-  was installed.
-  Returns True, but it doesn't mean anything other than the command was sent,
-  the reboot happens and we don't know if this succeeds or not.
-  """
-  # external function
-  # returns:
-  #  success: True
-  #  failure: None
-  def uninstallAppAndReboot(self, appName, installPath=None):
-    cmd = 'uninst ' + appName
-    if installPath:
-      cmd += ' ' + installPath
-    try:
-      data = self.runCmds([{ 'cmd': cmd }])
-    except AgentError:
-      return None
-
-    if (self.debug > 3): print "uninstallAppAndReboot: " + str(data)
-    return True
-
-  """
-  Updates the application on the device.
-  Application bundle - path to the application bundle on the device
-  Process name of application - used to end the process if the applicaiton is
-                                currently running
-  Destination - Destination directory to where the application should be
-                installed (optional)
-  ipAddr - IP address to await a callback ping to let us know that the device has updated
-           properly - defaults to current IP.
-  port - port to await a callback ping to let us know that the device has updated properly
-         defaults to 30000, and counts up from there if it finds a conflict
-  Returns True if succeeds, False if not
-  """
-  # external function
-  # returns:
-  #  success: text status from command or callback server
-  #  failure: None
-  def updateApp(self, appBundlePath, processName=None, destPath=None, ipAddr=None, port=30000):
-    status = None
-    cmd = 'updt '
-    if (processName == None):
-      # Then we pass '' for processName
-      cmd += "'' " + appBundlePath
-    else:
-      cmd += processName + ' ' + appBundlePath
-
-    if (destPath):
-      cmd += " " + destPath
-
-    if (ipAddr is not None):
-      ip, port = self.getCallbackIpAndPort(ipAddr, port)
-      cmd += " %s %s" % (ip, port)
-      # Set up our callback server
-      callbacksvr = callbackServer(ip, port, self.debug)
-
-    if (self.debug >= 3): print "INFO: updateApp using command: " + str(cmd)
-
-    try:
-      status = self.runCmds([{ 'cmd': cmd }])
-    except AgentError:
-      return None
-
-    if ipAddr is not None:
-      status = callbacksvr.disconnect()
-
-    if (self.debug >= 3): print "INFO: updateApp: got status back: " + str(status)
-
-    return status
-
-  """
-    return the current time on the device
-  """
-  # external function
-  # returns:
-  #  success: time in ms
-  #  failure: None
-  def getCurrentTime(self):
-    try:
-      data = self.runCmds([{ 'cmd': 'clok' }])
-    except AgentError:
-      return None
-
-    return data.strip()
-
-  """
-    Connect the ipaddress and port for a callback ping.  Defaults to current IP address
-    And ports starting at 30000.
-    NOTE: the detection for current IP address only works on Linux!
-  """
-  def getCallbackIpAndPort(self, aIp, aPort):
-    ip = aIp
-    nettools = NetworkTools()
-    if (ip == None):
-      ip = nettools.getLanIp()
-    if (aPort != None):
-      port = nettools.findOpenPort(ip, aPort)
-    else:
-      port = nettools.findOpenPort(ip, 30000)
-    return ip, port
-
-  """
-    Returns a properly formatted env string for the agent.
-    Input - env, which is either None, '', or a dict
-    Output - a quoted string of the form: '"envvar1=val1,envvar2=val2..."'
-    If env is None or '' return '' (empty quoted string)
-  """
-  def formatEnvString(self, env):
-    if (env == None or env == ''):
-      return ''
-
-    retVal = '"%s"' % ','.join(map(lambda x: '%s=%s' % (x[0], x[1]), env.iteritems()))
-    if (retVal == '""'):
-      return ''
-
-    return retVal
-
-  """
-    adjust the screen resolution on the device, REBOOT REQUIRED
-    NOTE: this only works on a tegra ATM
-    success: True
-    failure: False
-
-    supported resolutions: 640x480, 800x600, 1024x768, 1152x864, 1200x1024, 1440x900, 1680x1050, 1920x1080
-  """
-  def adjustResolution(self, width=1680, height=1050, type='hdmi'):
-    if self.getInfo('os')['os'][0].split()[0] != 'harmony-eng':
-      if (self.debug >= 2): print "WARNING: unable to adjust screen resolution on non Tegra device"
-      return False
-
-    results = self.getInfo('screen')
-    parts = results['screen'][0].split(':')
-    if (self.debug >= 3): print "INFO: we have a current resolution of %s, %s" % (parts[1].split()[0], parts[2].split()[0])
-
-    #verify screen type is valid, and set it to the proper value (https://bugzilla.mozilla.org/show_bug.cgi?id=632895#c4)
-    screentype = -1
-    if (type == 'hdmi'):
-      screentype = 5
-    elif (type == 'vga' or type == 'crt'):
-      screentype = 3
-    else:
-      return False
-
-    #verify we have numbers
-    if not (isinstance(width, int) and isinstance(height, int)):
-      return False
-
-    if (width < 100 or width > 9999):
-      return False
-
-    if (height < 100 or height > 9999):
-      return False
-
-    if (self.debug >= 3): print "INFO: adjusting screen resolution to %s, %s and rebooting" % (width, height)
-    try:
-      self.runCmds([{ 'cmd': "exec setprop persist.tegra.dpy%s.mode.width %s" % (screentype, width) }])
-      self.runCmds([{ 'cmd': "exec setprop persist.tegra.dpy%s.mode.height %s" % (screentype, height) }])
-    except AgentError:
-      return False
-
-    return True
-
-  # external function
-  # returns:
-  #  success: True
-  #  failure: False
-  def chmodDir(self, remoteDir):
-    try:
-      self.runCmds([{ 'cmd': "chmod "+remoteDir }])
-    except AgentError:
-      return False
-    return True
-
-gCallbackData = ''
-
-class myServer(SocketServer.TCPServer):
-  allow_reuse_address = True
-
-class callbackServer():
-  def __init__(self, ip, port, debuglevel):
-    global gCallbackData
-    if (debuglevel >= 1): print "DEBUG: gCallbackData is: %s on port: %s" % (gCallbackData, port)
-    gCallbackData = ''
-    self.ip = ip
-    self.port = port
-    self.connected = False
-    self.debug = debuglevel
-    if (self.debug >= 3): print "Creating server with " + str(ip) + ":" + str(port)
-    self.server = myServer((ip, port), self.myhandler)
-    self.server_thread = Thread(target=self.server.serve_forever)
-    self.server_thread.setDaemon(True)
-    self.server_thread.start()
-
-  def disconnect(self, step = 60, timeout = 600):
-    t = 0
-    if (self.debug >= 3): print "Calling disconnect on callback server"
-    while t < timeout:
-      if (gCallbackData):
-        # Got the data back
-        if (self.debug >= 3): print "Got data back from agent: " + str(gCallbackData)
-        break
-      else:
-        if (self.debug >= 0): print '.',
-      time.sleep(step)
-      t += step
-
-    try:
-      if (self.debug >= 3): print "Shutting down server now"
-      self.server.shutdown()
-    except:
-      if (self.debug >= 1): print "Unable to shutdown callback server - check for a connection on port: " + str(self.port)
-
-    #sleep 1 additional step to ensure not only we are online, but all our services are online
-    time.sleep(step)
-    return gCallbackData
-
-  class myhandler(SocketServer.BaseRequestHandler):
-    def handle(self):
-      global gCallbackData
-      gCallbackData = self.request.recv(1024)
-      #print "Callback Handler got data: " + str(gCallbackData)
-      self.request.send("OK")
-
deleted file mode 100644
--- a/build/mobile/droid.py
+++ /dev/null
@@ -1,83 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this file,
-# You can obtain one at http://mozilla.org/MPL/2.0/.
-
-from devicemanagerADB import DeviceManagerADB
-from devicemanagerSUT import DeviceManagerSUT
-import StringIO
-
-class DroidMixin(object):
-  """Mixin to extend DeviceManager with Android-specific functionality"""
-
-  def launchApplication(self, appName, activityName, intent, url=None,
-                        extras=None):
-    """
-    Launches an Android application
-    returns:
-    success: True
-    failure: False
-    """
-    # only one instance of an application may be running at once
-    if self.processExist(appName):
-      return False
-
-    acmd = [ "am", "start", "-W", "-n", "%s/%s" % (appName, activityName)]
-
-    if intent:
-      acmd.extend(["-a", intent])
-
-    if extras:
-      for (key, val) in extras.iteritems():
-        if type(val) is int:
-          extraTypeParam = "--ei"
-        elif type(val) is bool:
-          extraTypeParam = "--ez"
-        else:
-          extraTypeParam = "--es"
-        acmd.extend([extraTypeParam, str(key), str(val)])
-
-    if url:
-      acmd.extend(["-d", url])
-
-    # shell output not that interesting and debugging logs should already
-    # show what's going on here... so just create an empty memory buffer
-    # and ignore
-    shellOutput = StringIO.StringIO()
-    if self.shell(acmd, shellOutput) == 0:
-      return True
-
-    return False
-
-  def launchFennec(self, appName, intent="android.intent.action.VIEW",
-                   mozEnv=None, extraArgs=None, url=None):
-    """
-    Convenience method to launch Fennec on Android with various debugging
-    arguments
-    WARNING: FIXME: This would go better in mozrunner. Please do not
-    use this method if you are not comfortable with it going away sometime
-    in the near future
-    returns:
-    success: True
-    failure: False
-    """
-    extras = {}
-
-    if mozEnv:
-      # mozEnv is expected to be a dictionary of environment variables: Fennec
-      # itself will set them when launched
-      for (envCnt, (envkey, envval)) in enumerate(mozEnv.iteritems()):
-        extras["env" + str(envCnt)] = envkey + "=" + envval
-
-    # Additional command line arguments that fennec will read and use (e.g.
-    # with a custom profile)
-    if extraArgs:
-      extras['args'] = " ".join(extraArgs)
-
-    return self.launchApplication(appName, ".App", intent, url=url,
-                                  extras=extras)
-
-class DroidADB(DeviceManagerADB, DroidMixin):
-  pass
-
-class DroidSUT(DeviceManagerSUT, DroidMixin):
-  pass
deleted file mode 100644
--- a/build/mobile/emulator.py
+++ /dev/null
@@ -1,296 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-from abc import abstractmethod
-import datetime
-from mozprocess import ProcessHandlerMixin
-import multiprocessing
-import os
-import re
-import shutil
-import socket
-import subprocess
-from telnetlib import Telnet
-import tempfile
-import time
-
-from emulator_battery import EmulatorBattery
-
-
-class LogcatProc(ProcessHandlerMixin):
-    """Process handler for logcat which saves all output to a logfile.
-    """
-
-    def __init__(self, logfile, cmd, **kwargs):
-        self.logfile = logfile
-        kwargs.setdefault('processOutputLine', []).append(self.log_output)
-        ProcessHandlerMixin.__init__(self, cmd, **kwargs)
-
-    def log_output(self, line):
-        f = open(self.logfile, 'a')
-        f.write(line + "\n")
-        f.flush()
-
-
-class Emulator(object):
-
-    deviceRe = re.compile(r"^emulator-(\d+)(\s*)(.*)$")
-
-    def __init__(self, noWindow=False, logcat_dir=None, arch="x86",
-                 emulatorBinary=None, res='480x800', userdata=None,
-                 memory='512', partition_size='512'):
-        self.port = None
-        self._emulator_launched = False
-        self.proc = None
-        self.local_port = None
-        self.telnet = None
-        self._tmp_userdata = None
-        self._adb_started = False
-        self.logcat_dir = logcat_dir
-        self.logcat_proc = None
-        self.arch = arch
-        self.binary = emulatorBinary
-        self.memory = str(memory)
-        self.partition_size = str(partition_size)
-        self.res = res
-        self.battery = EmulatorBattery(self)
-        self.noWindow = noWindow
-        self.dataImg = userdata
-        self.copy_userdata = self.dataImg is None
-
-    def __del__(self):
-        if self.telnet:
-            self.telnet.write('exit\n')
-            self.telnet.read_all()
-
-    @property
-    def args(self):
-        qemuArgs = [self.binary,
-                    '-kernel', self.kernelImg,
-                    '-sysdir', self.sysDir,
-                    '-data', self.dataImg]
-        if self.noWindow:
-            qemuArgs.append('-no-window')
-        qemuArgs.extend(['-memory', self.memory,
-                         '-partition-size', self.partition_size,
-                         '-verbose',
-                         '-skin', self.res,
-                         '-gpu', 'on',
-                         '-qemu'] + self.tail_args)
-        return qemuArgs
-
-    @property
-    def is_running(self):
-        if self._emulator_launched:
-            return self.proc is not None and self.proc.poll() is None
-        else:
-            return self.port is not None
-
-    def _default_adb(self):
-        adb = subprocess.Popen(['which', 'adb'],
-                               stdout=subprocess.PIPE,
-                               stderr=subprocess.STDOUT)
-        retcode = adb.wait()
-        if retcode == 0:
-            self.adb = adb.stdout.read().strip() # remove trailing newline
-        return retcode
-
-    def _check_for_adb(self):
-        if not os.path.exists(self.adb):
-            if self._default_adb() != 0:
-                raise Exception('adb not found!')
-
-    def _run_adb(self, args):
-        args.insert(0, self.adb)
-        adb = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-        retcode = adb.wait()
-        if retcode:
-            raise Exception('adb terminated with exit code %d: %s'
-            % (retcode, adb.stdout.read()))
-        return adb.stdout.read()
-
-    def _get_telnet_response(self, command=None):
-        output = []
-        assert(self.telnet)
-        if command is not None:
-            self.telnet.write('%s\n' % command)
-        while True:
-            line = self.telnet.read_until('\n')
-            output.append(line.rstrip())
-            if line.startswith('OK'):
-                return output
-            elif line.startswith('KO:'):
-                raise Exception('bad telnet response: %s' % line)
-
-    def _run_telnet(self, command):
-        if not self.telnet:
-            self.telnet = Telnet('localhost', self.port)
-            self._get_telnet_response()
-        return self._get_telnet_response(command)
-
-    def close(self):
-        if self.is_running and self._emulator_launched:
-            self.proc.terminate()
-            self.proc.wait()
-        if self._adb_started:
-            self._run_adb(['kill-server'])
-            self._adb_started = False
-        if self.proc:
-            retcode = self.proc.poll()
-            self.proc = None
-            if self._tmp_userdata:
-                os.remove(self._tmp_userdata)
-                self._tmp_userdata = None
-            return retcode
-        if self.logcat_proc:
-            self.logcat_proc.kill()
-        return 0
-
-    def _get_adb_devices(self):
-        offline = set()
-        online = set()
-        output = self._run_adb(['devices'])
-        for line in output.split('\n'):
-            m = self.deviceRe.match(line)
-            if m:
-                if m.group(3) == 'offline':
-                    offline.add(m.group(1))
-                else:
-                    online.add(m.group(1))
-        return (online, offline)
-
-    def restart(self):
-        if not self._emulator_launched:
-            return
-        self.close()
-        self.start()
-
-    def start_adb(self):
-        result = self._run_adb(['start-server'])
-        # We keep track of whether we've started adb or not, so we know
-        # if we need to kill it.
-        if 'daemon started successfully' in result:
-            self._adb_started = True
-        else:
-            self._adb_started = False
-
-    def connect(self):
-        self._check_for_adb()
-        self.start_adb()
-
-        online, offline = self._get_adb_devices()
-        now = datetime.datetime.now()
-        while online == set([]):
-            time.sleep(1)
-            if datetime.datetime.now() - now > datetime.timedelta(seconds=60):
-                raise Exception('timed out waiting for emulator to be available')
-            online, offline = self._get_adb_devices()
-        self.port = int(list(online)[0])
-
-    @abstractmethod
-    def _locate_files(self):
-        pass
-
-    def start(self):
-        self._locate_files()
-        self.start_adb()
-
-        qemu_args = self.args[:]
-        if self.copy_userdata:
-            # Make a copy of the userdata.img for this instance of the emulator
-            # to use.
-            self._tmp_userdata = tempfile.mktemp(prefix='emulator')
-            shutil.copyfile(self.dataImg, self._tmp_userdata)
-            qemu_args[qemu_args.index('-data') + 1] = self._tmp_userdata
-
-        original_online, original_offline = self._get_adb_devices()
-
-        self.proc = subprocess.Popen(qemu_args,
-                                     stdout=subprocess.PIPE,
-                                     stderr=subprocess.PIPE)
-
-        online, offline = self._get_adb_devices()
-        now = datetime.datetime.now()
-        while online - original_online == set([]):
-            time.sleep(1)
-            if datetime.datetime.now() - now > datetime.timedelta(seconds=60):
-                raise Exception('timed out waiting for emulator to start')
-            online, offline = self._get_adb_devices()
-        self.port = int(list(online - original_online)[0])
-        self._emulator_launched = True
-
-        if self.logcat_dir:
-            self.save_logcat()
-
-        # setup DNS fix for networking
-        self._run_adb(['-s', 'emulator-%d' % self.port,
-                       'shell', 'setprop', 'net.dns1', '10.0.2.3'])
-
-    def _save_logcat_proc(self, filename, cmd):
-        self.logcat_proc = LogcatProc(filename, cmd)
-        self.logcat_proc.run()
-        self.logcat_proc.waitForFinish()
-        self.logcat_proc = None
-
-    def rotate_log(self, srclog, index=1):
-        """ Rotate a logfile, by recursively rotating logs further in the sequence,
-            deleting the last file if necessary.
-        """
-        destlog = os.path.join(self.logcat_dir, 'emulator-%d.%d.log' % (self.port, index))
-        if os.path.exists(destlog):
-            if index == 3:
-                os.remove(destlog)
-            else:
-                self.rotate_log(destlog, index + 1)
-        shutil.move(srclog, destlog)
-
-    def save_logcat(self):
-        """ Save the output of logcat to a file.
-        """
-        filename = os.path.join(self.logcat_dir, "emulator-%d.log" % self.port)
-        if os.path.exists(filename):
-            self.rotate_log(filename)
-        cmd = [self.adb, '-s', 'emulator-%d' % self.port, 'logcat']
-
-        # We do this in a separate process because we call mozprocess's
-        # waitForFinish method to process logcat's output, and this method
-        # blocks.
-        proc = multiprocessing.Process(target=self._save_logcat_proc, args=(filename, cmd))
-        proc.daemon = True
-        proc.start()
-
-    def setup_port_forwarding(self, remote_port):
-        """ Set up TCP port forwarding to the specified port on the device,
-            using any availble local port, and return the local port.
-        """
-
-        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        s.bind(("", 0))
-        local_port = s.getsockname()[1]
-        s.close()
-
-        self._run_adb(['-s', 'emulator-%d' % self.port, 'forward',
-                       'tcp:%d' % local_port,
-                       'tcp:%d' % remote_port])
-
-        self.local_port = local_port
-
-        return local_port
-
-    def wait_for_port(self, timeout=300):
-        assert(self.local_port)
-        starttime = datetime.datetime.now()
-        while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout):
-            try:
-                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-                sock.connect(('localhost', self.local_port))
-                data = sock.recv(16)
-                sock.close()
-                if '"from"' in data:
-                    return True
-            except:
-                import traceback
-                print traceback.format_exc()
-            time.sleep(1)
-        return False
deleted file mode 100644
--- a/build/mobile/emulator_battery.py
+++ /dev/null
@@ -1,52 +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/.
-
-class EmulatorBattery(object):
-
-    def __init__(self, emulator):
-        self.emulator = emulator
-
-    def get_state(self):
-        status = {}
-        state = {}
-
-        response = self.emulator._run_telnet('power display')
-        for line in response:
-            if ':' in line:
-                field, value = line.split(':')
-                value = value.strip()
-                if value == 'true':
-                    value = True
-                elif value == 'false':
-                    value = False
-                elif field == 'capacity':
-                    value = float(value)
-                status[field] = value
-
-        state['level'] = status.get('capacity', 0.0) / 100
-        if status.get('AC') == 'online':
-            state['charging'] = True
-        else:
-            state['charging'] = False
-
-        return state
-
-    def get_charging(self):
-        return self.get_state()['charging']
-
-    def get_level(self):
-        return self.get_state()['level']
-
-    def set_level(self, level):
-        self.emulator._run_telnet('power capacity %d' % (level * 100))
-
-    def set_charging(self, charging):
-        if charging:
-            cmd = 'power ac on'
-        else:
-            cmd = 'power ac off'
-        self.emulator._run_telnet(cmd)
-
-    charging = property(get_charging, set_charging)
-    level = property(get_level, set_level)
--- a/build/mobile/sutagent/android/AndroidManifest.xml
+++ b/build/mobile/sutagent/android/AndroidManifest.xml
@@ -12,18 +12,17 @@
                   android:label="@string/app_name">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
         <receiver android:name=".SUTStartupIntentReceiver">
             <intent-filter>
-                 <action android:value="android.intent.action.BOOT_COMPLETED" android:name="android.intent.action.BOOT_COMPLETED"/>
-                 <category android:value="android.intent.category.HOME" android:name="android.intent.category.HOME"/>
+                 <action android:name="android.intent.action.MEDIA_MOUNTED"></action>
             </intent-filter>
         </receiver>
         <service android:name=".service.ASMozStub">
 		    <intent-filter>
 			    <action android:name="com.mozilla.SUTAgentAndroid.service.LISTENER_SERVICE" />
 		    </intent-filter>
 	    </service>
 	</application>
@@ -53,9 +52,9 @@
 <uses-permission android:name="android.permission.STATUS_BAR"></uses-permission>
 <uses-permission android:name="android.permission.VIBRATE"></uses-permission>
 <uses-permission android:name="android.permission.SET_TIME"></uses-permission>
 <uses-permission android:name="android.permission.SET_TIME_ZONE"></uses-permission>
 
 <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"></uses-permission>
 <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"></uses-permission>
 
-</manifest> 
\ No newline at end of file
+</manifest> 
--- a/build/mobile/sutagent/android/watcher/WatcherService.java
+++ b/build/mobile/sutagent/android/watcher/WatcherService.java
@@ -186,19 +186,25 @@ public class WatcherService extends Serv
             {
             sComp = e.toString();
             }
         return (sRet);
         }
 
     private void handleCommand(Intent intent)
         {
-        String sCmd = intent.getStringExtra("command");
+        // Note: intent can be null "if the service is being restarted after its process
+        // has gone away". In this case, we will consider that to be equivalent to a start
+        // http://developer.android.com/reference/android/app/Service.html#onStartCommand%28android.content.Intent,%20int,%20int%29
 
-//        Debug.waitForDebugger();
+        String sCmd = "start";
+        if (intent != null)
+            {
+            sCmd = intent.getStringExtra("command");
+            }
 
         if (sCmd != null)
             {
             if (sCmd.equalsIgnoreCase("updt"))
                 {
                 String sPkgName = intent.getStringExtra("pkgName");
                 String sPkgFile = intent.getStringExtra("pkgFile");
                 String sOutFile = intent.getStringExtra("outFile");
--- a/build/pymake/pymake/process.py
+++ b/build/pymake/pymake/process.py
@@ -259,18 +259,18 @@ class PythonJob(Job):
             print >>sys.stderr, e
             return e.exitcode
         except:
             e = sys.exc_info()[1]
             if isinstance(e, SystemExit) and (e.code == 0 or e.code is None):
                 pass # sys.exit(0) is not a failure
             else:
                 print >>sys.stderr, e
-                print >>sys.stderr, traceback.print_exc()
-                return (e.code if isinstance(e.code, int) else 1)
+                traceback.print_exc()
+                return -127
         finally:
             os.environ.clear()
             os.environ.update(oldenv)
         return 0
 
 def job_runner(job):
     """
     Run a job. Called in a Process pool.
new file mode 100644
--- /dev/null
+++ b/build/pymake/tests/native-command-raise.mk
@@ -0,0 +1,9 @@
+#T gmake skip
+#T returncode: 2
+#T grep-for: "Exception: info-exception"
+
+CMD = %pycmd asplode_raise
+PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir
+
+all:
+	@$(CMD) info-exception
--- a/build/pymake/tests/pycmd.py
+++ b/build/pymake/tests/pycmd.py
@@ -25,8 +25,11 @@ def convertasplode(arg):
 
 def asplode(args):
   arg0 = convertasplode(args[0])
   sys.exit(arg0)
 
 def asplode_return(args):
   arg0 = convertasplode(args[0])
   return arg0
+
+def asplode_raise(args):
+  raise Exception(args[0])
--- a/caps/include/nsScriptSecurityManager.h
+++ b/caps/include/nsScriptSecurityManager.h
@@ -387,17 +387,17 @@ private:
 
     // GetScriptSecurityManager is the only call that can make one
     nsScriptSecurityManager();
     virtual ~nsScriptSecurityManager();
 
     static JSBool
     CheckObjectAccess(JSContext *cx, JSHandleObject obj,
                       JSHandleId id, JSAccessMode mode,
-                      jsval *vp);
+                      JSMutableHandleValue vp);
     
     // Decides, based on CSP, whether or not eval() and stuff can be executed.
     static JSBool
     ContentSecurityPolicyPermitsJSAction(JSContext *cx);
 
     // Returns null if a principal cannot be found; generally callers
     // should error out at that point.
     static nsIPrincipal* doGetObjectPrincipal(JSObject *obj);
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -527,35 +527,35 @@ nsScriptSecurityManager::ContentSecurity
 
     return evalOK;
 }
 
 
 JSBool
 nsScriptSecurityManager::CheckObjectAccess(JSContext *cx, JSHandleObject obj,
                                            JSHandleId id, JSAccessMode mode,
-                                           jsval *vp)
+                                           JSMutableHandleValue vp)
 {
     // Get the security manager
     nsScriptSecurityManager *ssm =
         nsScriptSecurityManager::GetScriptSecurityManager();
 
     NS_ASSERTION(ssm, "Failed to get security manager service");
     if (!ssm)
         return JS_FALSE;
 
     // Get the object being accessed.  We protect these cases:
     // 1. The Function.prototype.caller property's value, which might lead
     //    an attacker up a call-stack to a function or another object from
     //    a different trust domain.
     // 2. A user-defined getter or setter function accessible on another
     //    trust domain's window or document object.
-    // *vp can be a primitive, in that case, we use obj as the target