merge with m-c 1900e3edd32d
authorDoug Turner <dougt@dougt.org>
Wed, 30 Nov 2011 22:30:21 -0800
changeset 83654 d71c91775f9b698140e7ed2fddd1f1b15f8aa64e
parent 83653 56fe51d0023cdd815c5ca3c03a0f8d2fc3e87ae6 (current diff)
parent 82632 1900e3edd32da17ec4b1af6ce3726a8eed4d8eea (diff)
child 83655 ec36c2b415cc8aeb82b3c6773fe82925630729b6
push idunknown
push userunknown
push dateunknown
milestone11.0a1
merge with m-c 1900e3edd32d
Makefile.in
browser/devtools/webconsole/test/browser/browser_webconsole_storage_create_display.js
browser/devtools/webconsole/test/browser/browser_webconsole_storage_iteration.js
browser/devtools/webconsole/test/browser/browser_webconsole_storage_record_entry.js
browser/devtools/webconsole/test/browser/browser_webconsole_storage_record_many_entries.js
dom/plugins/base/android/ANPSurface.cpp
dom/plugins/base/android/Makefile.in
dom/plugins/base/nsNPAPIPluginInstance.cpp
dom/plugins/base/nsNPAPIPluginInstance.h
dom/plugins/base/nsPluginInstanceOwner.cpp
dom/plugins/base/nsPluginInstanceOwner.h
mobile/xul/installer/package-manifest.in
other-licenses/skia-npapi/ANPCanvas.cpp
other-licenses/skia-npapi/ANPPaint.cpp
other-licenses/skia-npapi/ANPPath.cpp
other-licenses/skia-npapi/ANPTypeface.cpp
toolkit/Makefile.in
toolkit/components/places/History.cpp
toolkit/components/places/nsNavHistory.cpp
toolkit/crashreporter/google-breakpad/src/common/md5.c
toolkit/library/Makefile.in
toolkit/mozapps/webapps/Makefile.in
toolkit/mozapps/webapps/OpenWebapps.idl
toolkit/mozapps/webapps/OpenWebapps.js
toolkit/mozapps/webapps/OpenWebapps.jsm
toolkit/mozapps/webapps/OpenWebapps.manifest
toolkit/themes/gnomestripe/global/icons/webconsole.png
toolkit/themes/gnomestripe/global/webConsole.css
toolkit/themes/gnomestripe/global/webConsole_networkPanel.css
toolkit/themes/pinstripe/global/icons/webconsole.png
toolkit/themes/pinstripe/global/webConsole.css
toolkit/themes/pinstripe/global/webConsole_networkPanel.css
toolkit/themes/winstripe/global/icons/webconsole.png
toolkit/themes/winstripe/global/webConsole.css
toolkit/themes/winstripe/global/webConsole_networkPanel.css
widget/src/android/AndroidBridge.h
--- a/accessible/src/html/nsHTMLTableAccessible.cpp
+++ b/accessible/src/html/nsHTMLTableAccessible.cpp
@@ -1409,16 +1409,19 @@ nsHTMLTableAccessible::IsProbablyForLayo
 
   if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
     // Role attribute is present, but overridden roles have already been dealt with.
     // Only landmarks and other roles that don't override the role from native
     // markup are left to deal with here.
     RETURN_LAYOUT_ANSWER(false, "Has role attribute, weak role, and role is table");
   }
 
+  if (mContent->Tag() != nsGkAtoms::table)
+    RETURN_LAYOUT_ANSWER(true, "table built by CSS display:table style");
+
   // Check if datatable attribute has "0" value.
   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::datatable,
                             NS_LITERAL_STRING("0"), eCaseMatters)) {
     RETURN_LAYOUT_ANSWER(true, "Has datatable = 0 attribute, it's for layout");
   }
 
   // Check for legitimate data table attributes.
   nsAutoString summary;
--- a/accessible/src/msaa/CAccessibleText.cpp
+++ b/accessible/src/msaa/CAccessibleText.cpp
@@ -171,16 +171,18 @@ CAccessibleText::get_characterExtents(lo
 
 STDMETHODIMP
 CAccessibleText::get_nSelections(long *aNSelections)
 {
 __try {
   *aNSelections = 0;
 
   nsRefPtr<nsHyperTextAccessible> textAcc(do_QueryObject(this));
+  if (textAcc->IsDefunct())
+    return E_FAIL;
 
   PRInt32 selCount = 0;
   nsresult rv = textAcc->GetSelectionCount(&selCount);
   if (NS_FAILED(rv))
     return GetHRESULT(rv);
 
   *aNSelections = selCount;
   return S_OK;
--- a/accessible/tests/mochitest/table/test_layoutguess.html
+++ b/accessible/tests/mochitest/table/test_layoutguess.html
@@ -93,16 +93,19 @@
       testAttrs("table21.3", attr, true);
       testAttrs("table21.4", attr, true);
       testAttrs("table21.5", attr, true);
       testAttrs("table21.6", attr, true);
 
       // layout table having datatable="0" attribute and containing data table structure (tfoot element)
       testAttrs("table22", attr, true);
 
+      // css table with non-table tag
+      testAttrs("table23", attr, true);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
@@ -112,16 +115,21 @@
      title="Don't treat tables that have a landmark role as layout table">
     Mozilla Bug 495388
   </a>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=690222"
      title="Data table elements used to determine layout-guess attribute shouldn't be picked from nested tables">
     Mozilla Bug 690222
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=693948"
+     title="Expose layout-guess: true object attribute on CSS table accessible">
+    Mozilla Bug 693948
+  </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <!-- Table with role of grid -->
   <table id="table1" role="grid">
@@ -426,10 +434,19 @@
   <!-- layout table with datatable="0" and tfoot element-->
   <table id="table22" datatable="0">
     <tfoot>
       <tr>
         <td>Cell1</td><td>cell2</td>
       </tr>
     </tfoot>
   </table>
+
+  <!-- css table with noon-table tag -->
+  <div id="table23" style="display:table;">
+    <div style="display:table-row;">
+      <div style="display:table-cell;">Row 1, column 1</div>
+      <div style="display:table-cell;">Row 1, column 2</div>
+      <div style="display:table-cell;">Row 1, column 3</div>
+    </div>
+  </div>
 </body>
 </html>
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -49,17 +49,17 @@
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 <?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/places/places.css" type="text/css"?>
-<?xml-stylesheet href="chrome://global/skin/webConsole.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/devtools/webconsole.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
 
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/baseMenuOverlay.xul"?>
 <?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
 
 # All DTD information is stored in a separate file so that it can be shared by
 # hiddenWindow.xul.
--- a/browser/base/content/highlighter.css
+++ b/browser/base/content/highlighter.css
@@ -7,20 +7,20 @@
   top: 0;
   left: 0;
 }
 
 #highlighter-veil-container {
   overflow: hidden;
 }
 
-#highlighter-veil-container:not([locked]) > .highlighter-veil,
-#highlighter-veil-container:not([locked]) > #highlighter-veil-middlebox,
-#highlighter-veil-container:not([locked]) > #highlighter-veil-middlebox > .highlighter-veil,
-#highlighter-veil-container:not([locked]) > #highlighter-veil-middlebox > #highlighter-veil-transparentbox {
+#highlighter-veil-container:not([disable-transitions]) > .highlighter-veil,
+#highlighter-veil-container:not([disable-transitions]) > #highlighter-veil-middlebox,
+#highlighter-veil-container:not([disable-transitions]) > #highlighter-veil-middlebox > .highlighter-veil,
+#highlighter-veil-container:not([disable-transitions]) > #highlighter-veil-middlebox > #highlighter-veil-transparentbox {
   -moz-transition-property: width, height;
   -moz-transition-duration: 0.1s;
   -moz-transition-timing-function: linear;
 }
 
 #highlighter-veil-bottombox,
 #highlighter-veil-rightbox {
   -moz-box-flex: 1;
@@ -46,28 +46,39 @@
 /*
  * Node Infobar
  */
 
 #highlighter-nodeinfobar-container {
   position: absolute;
 }
 
-#highlighter-nodeinfobar-container:not([locked]) {
+#highlighter-nodeinfobar-container:not([disable-transitions]) {
   -moz-transition-property: top, left;
   -moz-transition-duration: 0.1s;
   -moz-transition-timing-function: linear;
 }
 
 #highlighter-nodeinfobar {
   display: block;
   white-space: nowrap;
   direction: ltr;
 }
 
+#highlighter-nodeinfobar-container[locked] > #highlighter-nodeinfobar {
+  pointer-events: auto;
+}
+
+#highlighter-nodeinfobar-id,
+.highlighter-nodeinfobar-class,
+#highlighter-nodeinfobar-tagname {
+  -moz-user-select: text;
+  cursor: text;
+}
+
 .highlighter-nodeinfobar-arrow {
   display: none;
 }
 
 #highlighter-nodeinfobar-container[position="top"]:not([hide-arrow]) > #highlighter-nodeinfobar-arrow-bottom {
   display: block;
 }
 
@@ -78,19 +89,11 @@
 #highlighter-nodeinfobar-container[disabled] {
   visibility: hidden;
 }
 
 #highlighter-nodeinfobar-id:empty {
   display: none;
 }
 
-#highlighter-nodeinfobar-id::before {
-  content: "#";
-}
-
-.highlighter-nodeinfobar-class::before {
-  content: ".";
-}
-
 #highlighter-nodeinfobar-tagname {
   text-transform: lowercase;
 }
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -854,25 +854,25 @@ nsContextMenu.prototype = {
     canvas.width = video.videoWidth;
     canvas.height = video.videoHeight;
     var ctxDraw = canvas.getContext("2d");
     ctxDraw.drawImage(video, 0, 0);
     saveImageURL(canvas.toDataURL("image/jpeg", ""), name, "SaveImageTitle", true, false, document.documentURIObject);
   },
 
   fullScreenVideo: function () {
-    let video = this.target;
+    let video = this.target;
     if (document.mozFullScreenEnabled)
       video.mozRequestFullScreen();
     else {
-      // Fallback for the legacy full-screen video implementation.
-      video.pause();
-      openDialog("chrome://browser/content/fullscreen-video.xhtml",
-                  "", "chrome,centerscreen,dialog=no", video);
-    }
+      // Fallback for the legacy full-screen video implementation.
+      video.pause();
+      openDialog("chrome://browser/content/fullscreen-video.xhtml",
+                  "", "chrome,centerscreen,dialog=no", video);
+    }
   },
 
   // Change current window to the URL of the background image.
   viewBGImage: function(e) {
     urlSecurityCheck(this.bgImageURL,
                      this.browser.contentPrincipal,
                      Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
     var doc = this.target.ownerDocument;
--- a/browser/build.mk
+++ b/browser/build.mk
@@ -50,16 +50,32 @@ tier_app_dirs += $(MOZ_BRANDING_DIRECTOR
 ifdef MOZ_SERVICES_SYNC
 tier_app_dirs += services
 endif
 
 tier_app_dirs += browser
 # Never add other tier_app_dirs after browser. They won't get packaged
 # properly on mac.
 
+################################################
+# Parallel build on Windows with GNU make check
+
+default::
+ifeq (,$(findstring pymake,$(MAKE)))
+ifeq ($(HOST_OS_ARCH),WINNT)
+ifneq (1,$(NUMBER_OF_PROCESSORS))
+	@echo $(if $(findstring -j,$(value MAKEFLAGS)), \
+$(error You are using GNU make to build Firefox with -jN on Windows. \
+This will randomly deadlock. To compile a parallel build on Windows \
+run "python -OO build/pymake/make.py -f client.mk build". \
+See https://developer.mozilla.org/en/pymake for more details.))
+endif
+endif
+endif
+
 installer:
 	@$(MAKE) -C browser/installer installer
 
 package:
 	@$(MAKE) -C browser/installer
 
 package-compare:
 	@$(MAKE) -C browser/installer package-compare
--- a/browser/components/migration/src/ChromeProfileMigrator.js
+++ b/browser/components/migration/src/ChromeProfileMigrator.js
@@ -474,33 +474,33 @@ ChromeProfileMigrator.prototype = {
     return result;
   },
 
   /*
    * Whether we support migration of Chrome
    *
    * @return true if supported
    */
-  sourceExists: function Chrome_sourceExists()
+  get sourceExists()
   {
     let result = this.getMigrateData(null, false);
     return result != 0;
   },
 
   // Although Chrome supports multi-profiles, there is no way
   // to get profile lists.
   sourceHasMultipleProfiles: false,
   sourceProfiles: null,
 
   /*
    * Return home page URL
    *
    * @return  home page URL
    */
-  sourceHomePageURL: function Chrome_sourceHomePageURL()
+  get sourceHomePageURL()
   {
     try  {
       if (this._homepageURL)
         return this._homepageURL;
 
       if (!this._paths.prefs)
         this.getMigrateData(null, false);
 
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1527,39 +1527,39 @@ ContentPermissionPrompt.prototype = {
       },
     };
 
     var message;
     var secondaryActions = [];
 
     // Different message/options if it is a local file
     if (requestingURI.schemeIs("file")) {
-      message = browserBundle.formatStringFromName("geolocation.fileWantsToKnow",
+      message = browserBundle.formatStringFromName("geolocation.shareWithFile",
                                                    [requestingURI.path], 1);
     } else {
-      message = browserBundle.formatStringFromName("geolocation.siteWantsToKnow",
+      message = browserBundle.formatStringFromName("geolocation.shareWithSite",
                                                    [requestingURI.host], 1);
 
       // Don't offer to "always/never share" in PB mode
       var inPrivateBrowsing = Cc["@mozilla.org/privatebrowsing;1"].
                               getService(Ci.nsIPrivateBrowsingService).
                               privateBrowsingEnabled;
 
       if (!inPrivateBrowsing) {
         secondaryActions.push({
-          label: browserBundle.GetStringFromName("geolocation.alwaysShare"),
-          accessKey: browserBundle.GetStringFromName("geolocation.alwaysShare.accesskey"),
+          label: browserBundle.GetStringFromName("geolocation.alwaysShareLocation"),
+          accessKey: browserBundle.GetStringFromName("geolocation.alwaysShareLocation.accesskey"),
           callback: function () {
             Services.perms.add(requestingURI, "geo", Ci.nsIPermissionManager.ALLOW_ACTION);
             request.allow();
           }
         });
         secondaryActions.push({
-          label: browserBundle.GetStringFromName("geolocation.neverShare"),
-          accessKey: browserBundle.GetStringFromName("geolocation.neverShare.accesskey"),
+          label: browserBundle.GetStringFromName("geolocation.neverShareLocation"),
+          accessKey: browserBundle.GetStringFromName("geolocation.neverShareLocation.accesskey"),
           callback: function () {
             Services.perms.add(requestingURI, "geo", Ci.nsIPermissionManager.DENY_ACTION);
             request.cancel();
           }
         });
       }
     }
 
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -354,17 +354,17 @@ SessionStoreService.prototype = {
           if (lastSessionCrashed) {
             this._recentCrashes = (this._initialState.session &&
                                    this._initialState.session.recentCrashes || 0) + 1;
             
             if (this._needsRestorePage(this._initialState, this._recentCrashes)) {
               // replace the crashed session with a restore-page-only session
               let pageData = {
                 url: "about:sessionrestore",
-                formdata: { "#sessionData": JSON.stringify(this._initialState) }
+                formdata: { "#sessionData": this._initialState }
               };
               this._initialState = { windows: [{ tabs: [{ entries: [pageData] }] }] };
             }
           }
 
           // Load the session start time from the previous state
           this._sessionStartTime = this._initialState.session &&
                                    this._initialState.session.startTime ||
@@ -2135,20 +2135,27 @@ SessionStoreService.prototype = {
     for (var i = 0; i < aContent.frames.length; i++) {
       if (aData.children && aData.children[i])
         this._updateTextAndScrollDataForFrame(aWindow, aContent.frames[i],
                                               aData.children[i], aUpdateFormData,
                                               aFullData, aIsPinned);
     }
     var isHTTPS = this._getURIFromString((aContent.parent || aContent).
                                          document.location.href).schemeIs("https");
-    if (aFullData || this._checkPrivacyLevel(isHTTPS, aIsPinned) ||
-        aContent.top.document.location.href == "about:sessionrestore") {
+    let isAboutSR = aContent.top.document.location.href == "about:sessionrestore";
+    if (aFullData || this._checkPrivacyLevel(isHTTPS, aIsPinned) || isAboutSR) {
       if (aFullData || aUpdateFormData) {
         let formData = this._collectFormDataForFrame(aContent.document);
+
+        // We want to avoid saving data for about:sessionrestore as a string.
+        // Since it's stored in the form as stringified JSON, stringifying further
+        // causes an explosion of escape characters. cf. bug 467409
+        if (formData && isAboutSR)
+          formData["#sessionData"] = JSON.parse(formData["#sessionData"]);
+
         if (formData)
           aData.formdata = formData;
         else if (aData.formdata)
           delete aData.formdata;
       }
       
       // designMode is undefined e.g. for XUL documents (as about:config)
       if ((aContent.document.designMode || "") == "on") {
@@ -2976,18 +2983,19 @@ SessionStoreService.prototype = {
    * @param aTabData
    *        Array of tab data
    * @param aIdMap
    *        Hash for ensuring unique frame IDs
    */
   restoreHistory:
     function sss_restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap) {
     var _this = this;
-    while (aTabs.length > 0 && (!aTabs[0].linkedBrowser.__SS_tabStillLoading || !aTabs[0].parentNode)) {
-      aTabs.shift(); // this tab got removed before being completely restored
+    // if the tab got removed before being completely restored, then skip it
+    while (aTabs.length > 0 && !(this._canRestoreTabHistory(aTabs[0]))) {
+      aTabs.shift();
       aTabData.shift();
     }
     if (aTabs.length == 0) {
       // At this point we're essentially ready for consumers to read/write data
       // via the sessionstore API so we'll send the SSWindowStateReady event.
       this._setWindowStateReady(aWindow);
       return; // no more tabs to restore
     }
@@ -3374,16 +3382,23 @@ SessionStoreService.prototype = {
 
         let node = key.charAt(0) == "#" ? aDocument.getElementById(key.slice(1)) :
                                           XPathHelper.resolve(aDocument, key);
         if (!node)
           continue;
 
         let eventType;
         let value = aData[key];
+
+        // for about:sessionrestore we saved the field as JSON to avoid nested
+        // instances causing humongous sessionstore.js files. cf. bug 467409
+        if (aURL == "about:sessionrestore" && typeof value == "object") {
+          value = JSON.stringify(value);
+        }
+
         if (typeof value == "string" && node.type != "file") {
           if (node.value == value)
             continue; // don't dispatch an input event for no change
 
           node.value = value;
           eventType = "input";
         }
         else if (typeof value == "boolean") {
@@ -3992,16 +4007,30 @@ SessionStoreService.prototype = {
     // store this tab's data.
     return aTabState.entries.length &&
            !(aTabState.entries.length == 1 &&
              aTabState.entries[0].url == "about:blank" &&
              !aTabState.userTypedValue);
   },
 
   /**
+   * Determine if we can restore history into this tab.
+   * This will be false when a tab has been removed (usually between
+   * restoreHistoryPrecursor && restoreHistory) or if the tab is still marked
+   * as loading.
+   *
+   * @param aTab
+   * @returns boolean
+   */
+  _canRestoreTabHistory: function sss__canRestoreTabHistory(aTab) {
+    return aTab.parentNode && aTab.linkedBrowser &&
+           aTab.linkedBrowser.__SS_tabStillLoading;
+  },
+
+  /**
    * This is going to take a state as provided at startup (via
    * nsISessionStartup.state) and split it into 2 parts. The first part
    * (defaultState) will be a state that should still be restored at startup,
    * while the second part (state) is a state that should be saved for later.
    * defaultState will be comprised of windows with only pinned tabs, extracted
    * from state. It will contain the cookies that go along with the history
    * entries in those tabs. It will also contain window position information.
    *
--- a/browser/components/sessionstore/test/browser/Makefile.in
+++ b/browser/components/sessionstore/test/browser/Makefile.in
@@ -95,16 +95,17 @@ include $(topsrcdir)/config/rules.mk
 	browser_464620_a.html \
 	browser_464620_b.js \
 	browser_464620_b.html \
 	browser_464620_xd.html \
 	browser_465215.js \
 	browser_465223.js \
 	browser_466937.js \
 	browser_466937_sample.html \
+	browser_467409-backslashplosion.js \
 	browser_477657.js \
 	browser_480148.js \
 	browser_480893.js \
 	browser_483330.js \
 	browser_485482.js \
 	browser_485482_sample.html \
 	browser_485563.js \
 	browser_490040.js \
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser/browser_467409-backslashplosion.js
@@ -0,0 +1,90 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test Summary:
+// 1.  Open about:sessionrestore via setBrowserState where formdata is a JS object, not a string
+// 1a. Check that #sessionData on the page is readable after JSON.parse (skipped, checking formdata is sufficient)
+// 1b. Check that there are no backslashes in the formdata
+// 1c. Check that formdata (via getBrowserState) doesn't require JSON.parse
+//
+// 2.  Use the current state (currently about:sessionrestore with data) and then open than in a new instance of about:sessionrestore
+// 2a. Check that there are no backslashes in the formdata
+// 2b. Check that formdata (via getBrowserState) doesn't require JSON.parse
+//
+// 3.  [backwards compat] Use a stringified state as formdata when opening about:sessionrestore
+// 3a. Make sure there are nodes in the tree on about:sessionrestore (skipped, checking formdata is sufficient)
+// 3b. Check that there are no backslashes in the formdata
+// 3c. Check that formdata (via getBrowserState) doesn't require JSON.parse
+
+function test() {
+  waitForExplicitFinish();
+
+  let blankState = { windows: [{ tabs: [{ entries: [{ url: "about:blank" }] }]}]};
+  let crashState = { windows: [{ tabs: [{ entries: [{ url: "about:mozilla" }] }]}]};
+
+  let pagedata = { url: "about:sessionrestore",
+                   formdata: { "#sessionData": crashState } };
+  let state = { windows: [{ tabs: [{ entries: [pagedata] }] }] };
+
+  // test1 calls test2 calls test3 calls finish
+  test1(state);
+
+
+  function test1(aState) {
+    waitForBrowserState(aState, function() {
+      checkState("test1", test2);
+    });
+  }
+
+  function test2(aState) {
+    let pagedata2 = { url: "about:sessionrestore",
+                      formdata: { "#sessionData": aState } };
+    let state2 = { windows: [{ tabs: [{ entries: [pagedata2] }] }] };
+
+    waitForBrowserState(state2, function() {
+      checkState("test2", test3);
+    });
+  }
+
+  function test3(aState) {
+    let pagedata3 = { url: "about:sessionrestore",
+                      formdata: { "#sessionData": JSON.stringify(crashState) } };
+    let state3 = { windows: [{ tabs: [{ entries: [pagedata3] }] }] };
+    waitForBrowserState(state3, function() {
+      // In theory we should do inspection of the treeview on about:sessionrestore,
+      // but we don't actually need to. If we fail tests in checkState then
+      // about:sessionrestore won't be able to turn the form value into a usable page.
+      checkState("test3", function() waitForBrowserState(blankState, finish));
+    });
+  }
+
+  function checkState(testName, callback) {
+    let curState = JSON.parse(ss.getBrowserState());
+    let formdata = curState.windows[0].tabs[0].entries[0].formdata;
+
+    ok(formdata["#sessionData"], testName + ": we have form data for about:sessionrestore");
+
+    let sessionData_raw = JSON.stringify(formdata["#sessionData"]);
+    ok(!/\\/.test(sessionData_raw), testName + ": #sessionData contains no backslashes");
+    info(sessionData_raw);
+
+    let gotError = false;
+    try {
+      JSON.parse(formdata["#sessionData"]);
+    }
+    catch (e) {
+      info(testName + ": got error: " + e);
+      gotError = true;
+    }
+    ok(gotError, testName + ": attempting to JSON.parse form data threw error");
+
+    // Panorama sticks JSON into extData, which we stringify causing the
+    // naive backslash check to fail. extData doesn't matter in the grand
+    // scheme here, so we'll delete the extData so doesn't end up in future states.
+    delete curState.windows[0].extData;
+    delete curState.windows[0].tabs[0].extData;
+    callback(curState);
+  }
+
+}
+
--- a/browser/components/sessionstore/test/browser/browser_590563.js
+++ b/browser/components/sessionstore/test/browser/browser_590563.js
@@ -7,17 +7,17 @@ function test() {
       tabs: [
         { entries: [{ url: "about:robots" }], hidden: true },
         { entries: [{ url: "about:blank" }], hidden: false }
       ]
     }]
   };
   let pageData = {
     url: "about:sessionrestore",
-    formdata: { "#sessionData": "(" + JSON.stringify(oldState) + ")" }
+    formdata: { "#sessionData": oldState }
   };
   let state = { windows: [{ tabs: [{ entries: [pageData] }] }] };
 
   waitForExplicitFinish();
 
   newWindowWithState(state, function (win) {
     registerCleanupFunction(function () win.close());
 
--- a/browser/components/tabview/groupitems.js
+++ b/browser/components/tabview/groupitems.js
@@ -1086,31 +1086,31 @@ GroupItem.prototype = Utils.extend(new I
   //   options - An optional object with settings for this call. See below.
   //
   // Possible options: 
   //   dontArrange - don't rearrange the remaining items
   //   dontClose - don't close the group even if it normally would
   //   immediately - don't animate
   remove: function GroupItem_remove(a, options) {
     try {
-      var $el;
-      var item;
+      let $el;
+      let item;
 
       if (a.isAnItem) {
         item = a;
         $el = iQ(item.container);
       } else {
         $el = iQ(a);
         item = Items.item($el);
       }
 
       if (!options)
         options = {};
 
-      var index = this._children.indexOf(item);
+      let index = this._children.indexOf(item);
       if (index != -1)
         this._children.splice(index, 1);
 
       if (item == this._activeTab || !this._activeTab) {
         if (this._children.length > 0)
           this._activeTab = this._children[0];
         else
           this._activeTab = null;
@@ -1137,17 +1137,19 @@ GroupItem.prototype = Utils.extend(new I
         item.setResizable(true, options.immediately);
 
       // if a blank tab is selected while restoring a tab the blank tab gets
       // removed. we need to keep the group alive for the restored tab.
       if (item.isRemovedAfterRestore)
         options.dontClose = true;
 
       let closed = options.dontClose ? false : this.closeIfEmpty();
-      if (closed) {
+      if (closed ||
+          (this._children.length == 0 && !gBrowser.selectedTab.pinned &&
+           !item.isDragging)) {
         this._makeLastActiveGroupItemActive();
       } else if (!options.dontArrange) {
         this.arrange({animate: !options.immediately});
         this._unfreezeItemSize({dontArrange: true});
       }
 
       this._sendToSubscribers("childRemoved",{ groupItemId: this.id, item: item });
     } catch(e) {
--- a/browser/components/tabview/tabitems.js
+++ b/browser/components/tabview/tabitems.js
@@ -255,19 +255,25 @@ TabItem.prototype = Utils.extend(new Ite
   // Loads the tabItems thumbnail.
   loadThumbnail: function TabItem_loadThumbnail(tabData) {
     Utils.assert(tabData, "invalid or missing argument <tabData>");
 
     let self = this;
 
     function TabItem_loadThumbnail_callback(error, imageData) {
       // we could have been unlinked while waiting for the thumbnail to load
-      if (error || !imageData || !self.tab)
+      if (!self.tab)
         return;
 
+      if (error || !imageData) {
+        // paint the canvas to avoid leaving traces when dragging tab over it
+        self.tabCanvas.paint();
+        return;
+      }
+
       self._sendToSubscribers("loadedCachedImageData");
 
       // If we have a cached image, then show it if the loaded URL matches
       // what the cache is from, OR the loaded URL is blank, which means
       // that the page hasn't loaded yet.
       let currentUrl = self.tab.linkedBrowser.currentURI.spec;
       if (tabData.url == currentUrl || currentUrl == "about:blank")
         self.showCachedData(tabData, imageData);
--- a/browser/components/tabview/test/Makefile.in
+++ b/browser/components/tabview/test/Makefile.in
@@ -159,16 +159,17 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_bug673729.js \
                  browser_tabview_bug677310.js \
                  browser_tabview_bug679853.js \
                  browser_tabview_bug681599.js \
                  browser_tabview_bug685476.js \
                  browser_tabview_bug685692.js \
                  browser_tabview_bug686654.js \
                  browser_tabview_bug697390.js \
+                 browser_tabview_bug705621.js \
                  browser_tabview_click_group.js \
                  browser_tabview_dragdrop.js \
                  browser_tabview_exit_button.js \
                  browser_tabview_expander.js \
                  browser_tabview_firstrun_pref.js \
                  browser_tabview_group.js \
                  browser_tabview_launch.js \
                  browser_tabview_multiwindow_search.js \
--- a/browser/components/tabview/test/browser_tabview_apptabs.js
+++ b/browser/components/tabview/test/browser_tabview_apptabs.js
@@ -44,16 +44,20 @@ function onTabViewWindowLoaded() {
   // create a second group and make sure it gets the icon too
   box.offset(box.width + 20, 0);
   let groupItemTwo = new contentWindow.GroupItem([],
       { bounds: box, title: "test2" });
   is(contentWindow.GroupItems.groupItems.length, 3, "we now have three groups");
   is(appTabCount(groupItemTwo), 1,
       "there's an app tab icon in the second group");
 
+  // When the tab was pinned, the last active group with an item got the focus.
+  // Therefore, switching the focus back to group item one.
+  contentWindow.UI.setActive(groupItemOne);
+
   // unpin the tab, make sure the icon goes away and the TabItem comes on
   gBrowser.unpinTab(xulTab);
   is(groupItemOne._children.length, 1, "the app tab's TabItem is back");
   is(appTabCount(groupItemOne), 0, "the icon is gone from group one");
   is(appTabCount(groupItemTwo), 0, "the icon is gone from group 2");
 
   // pin the tab again
   gBrowser.pinTab(xulTab);
--- a/browser/components/tabview/test/browser_tabview_bug595965.js
+++ b/browser/components/tabview/test/browser_tabview_bug595965.js
@@ -99,16 +99,20 @@ function onTabViewShown(win) {
   is(tray.css("-moz-column-count"), 1,
      "$appTabTray column count is 1");
 
   is(appTabCount(groupItem), 1, "there's now one app tab icon");
 
   ok(!trayContainer.hasClass("appTabTrayContainerTruncated"),
      "$appTabTray container does not have .appTabTrayContainerTruncated");
 
+  // When the tab was pinned, the last active group with an item got the focus.
+  // Therefore, switching the focus back to group item one.
+  contentWindow.UI.setActive(groupItem);
+
   // unpin the last remaining tab
   gBrowser.unpinTab(xulTabs[0]);
 
   is(parseInt(trayContainer.css("width")), 0,
      "$appTabTray container is not visible");
 
   is(appTabCount(groupItem), 0, "there are no app tab icons");
 
new file mode 100644
--- /dev/null
+++ b/browser/components/tabview/test/browser_tabview_bug705621.js
@@ -0,0 +1,30 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+  waitForExplicitFinish();
+
+  newWindowWithTabView(function(win) {
+    registerCleanupFunction(function() {
+      win.close();
+    });
+
+    let cw = win.TabView.getContentWindow();
+
+    let groupItemOne = cw.GroupItems.groupItems[0];
+    is(groupItemOne.getChildren().length, 1, "Group one has 1 tab item");
+
+    let groupItemTwo = createGroupItemWithBlankTabs(win, 300, 300, 40, 1);
+    is(groupItemTwo.getChildren().length, 1, "Group two has 2 tab items");
+
+    whenTabViewIsHidden(function() {
+      executeSoon(function() { 
+        win.gBrowser.removeTab(win.gBrowser.selectedTab);
+        is(cw.UI.getActiveTab(), groupItemOne.getChild(0), "TabItem in Group one is selected");
+        finish();
+      });
+    }, win);
+    groupItemTwo.getChild(0).zoomIn();
+  });
+}
+
--- a/browser/components/tabview/ui.js
+++ b/browser/components/tabview/ui.js
@@ -779,17 +779,17 @@ let UI = {
         // if not closing the last tab
         if (gBrowser.tabs.length > 1) {
           // Don't return to TabView if there are any app tabs
           for (let a = 0; a < gBrowser._numPinnedTabs; a++) {
             if (!gBrowser.tabs[a].closing)
               return;
           }
 
-          var groupItem = GroupItems.getActiveGroupItem();
+          let groupItem = GroupItems.getActiveGroupItem();
 
           // 1) Only go back to the TabView tab when there you close the last
           // tab of a groupItem.
           let closingLastOfGroup = (groupItem && 
               groupItem._children.length == 1 && 
               groupItem._children[0].tab == tab);
 
           // 2) When a blank tab is active while restoring a closed tab the
--- a/browser/devtools/highlighter/inspector.jsm
+++ b/browser/devtools/highlighter/inspector.jsm
@@ -142,19 +142,26 @@ Highlighter.prototype = {
     stack.appendChild(this.highlighterContainer);
 
     // The veil will make the whole page darker except
     // for the region of the selected box.
     this.buildVeil(this.veilContainer);
 
     this.buildInfobar(controlsBox);
 
+    if (!this.IUI.store.getValue(this.winID, "inspecting")) {
+      this.veilContainer.setAttribute("locked", true);
+      this.nodeInfo.container.setAttribute("locked", true);
+    }
+
     this.browser.addEventListener("resize", this, true);
     this.browser.addEventListener("scroll", this, true);
 
+    this.transitionDisabler = null;
+
     this.handleResize();
   },
 
   /**
    * Build the veil:
    *
    * <vbox id="highlighter-veil-container">
    *   <box id="highlighter-veil-topbox" class="highlighter-veil"/>
@@ -482,30 +489,30 @@ Highlighter.prototype = {
    * Update node information (tagName#id.class) 
    */
   updateInfobar: function Highlighter_updateInfobar()
   {
     // Tag name
     this.nodeInfo.tagNameLabel.textContent = this.node.tagName;
 
     // ID
-    this.nodeInfo.idLabel.textContent = this.node.id;
+    this.nodeInfo.idLabel.textContent = this.node.id ? "#" + this.node.id : "";
 
     // Classes
     let classes = this.nodeInfo.classesBox;
     while (classes.hasChildNodes()) {
       classes.removeChild(classes.firstChild);
     }
 
     if (this.node.className) {
       let fragment = this.chromeDoc.createDocumentFragment();
       for (let i = 0; i < this.node.classList.length; i++) {
         let classLabel = this.chromeDoc.createElement("label");
         classLabel.className = "highlighter-nodeinfobar-class plain";
-        classLabel.textContent = this.node.classList[i];
+        classLabel.textContent = "." + this.node.classList[i];
         fragment.appendChild(classLabel);
       }
       classes.appendChild(fragment);
     }
   },
 
   /**
    * Move the Infobar to the right place in the highlighter.
@@ -660,31 +667,53 @@ Highlighter.prototype = {
     switch (aEvent.type) {
       case "click":
         this.handleClick(aEvent);
         break;
       case "mousemove":
         this.handleMouseMove(aEvent);
         break;
       case "resize":
+        this.brieflyDisableTransitions();
         this.handleResize(aEvent);
         break;
       case "dblclick":
       case "mousedown":
       case "mouseup":
         aEvent.stopPropagation();
         aEvent.preventDefault();
         break;
       case "scroll":
+        this.brieflyDisableTransitions();
         this.highlight();
         break;
     }
   },
 
   /**
+   * Disable the CSS transitions for a short time to avoid laggy animations
+   * during scrolling or resizing.
+   */
+  brieflyDisableTransitions: function Highlighter_brieflyDisableTransitions()
+  {
+   if (this.transitionDisabler) {
+     this.IUI.win.clearTimeout(this.transitionDisabler);
+   } else {
+     this.veilContainer.setAttribute("disable-transitions", "true");
+     this.nodeInfo.container.setAttribute("disable-transitions", "true");
+   }
+   this.transitionDisabler =
+     this.IUI.win.setTimeout(function() {
+       this.veilContainer.removeAttribute("disable-transitions");
+       this.nodeInfo.container.removeAttribute("disable-transitions");
+       this.transitionDisabler = null;
+     }.bind(this), 500);
+  },
+
+  /**
    * Handle clicks.
    *
    * @param nsIDOMEvent aEvent
    *        The DOM event.
    */
   handleClick: function Highlighter_handleClick(aEvent)
   {
     // Stop inspection when the user clicks on a node.
@@ -958,16 +987,18 @@ InspectorUI.prototype = {
   },
 
   /**
    * Initialize highlighter.
    */
   initializeHighlighter: function IUI_initializeHighlighter()
   {
     this.highlighter = new Highlighter(this);
+    this.browser.addEventListener("keypress", this, true);
+    this.highlighter.highlighterContainer.addEventListener("keypress", this, true);
     this.highlighterReady();
   },
 
   /**
    * Initialize the InspectorStore.
    */
   initializeStore: function IUI_initializeStore()
   {
@@ -1081,20 +1112,16 @@ InspectorUI.prototype = {
   startInspecting: function IUI_startInspecting()
   {
     // if currently editing an attribute value, starting
     // "live inspection" mode closes the editor
     if (this.treePanel && this.treePanel.editingContext)
       this.treePanel.closeEditor();
 
     this.inspectToolbutton.checked = true;
-    // Attach event listeners to content window and child windows to enable
-    // highlighting and click to stop inspection.
-    this.browser.addEventListener("keypress", this, true);
-    this.highlighter.highlighterContainer.addEventListener("keypress", this, true);
     this.highlighter.attachInspectListeners();
 
     this.inspecting = true;
     this.toolsDim(true);
     this.highlighter.veilContainer.removeAttribute("locked");
     this.highlighter.nodeInfo.container.removeAttribute("locked");
   },
 
--- a/browser/devtools/highlighter/test/browser_inspector_infobar.js
+++ b/browser/devtools/highlighter/test/browser_inspector_infobar.js
@@ -19,19 +19,19 @@ function test()
   let style = "body{width:100%;height: 100%} div {position: absolute;height: 100px;width: 500px}#bottom {bottom: 0px}#vertical {height: 100%}";
   let html = "<style>" + style + "</style><div id=vertical></div><div id=top class='class1 class2'></div><div id=bottom></div>"
 
   content.location = "data:text/html," + encodeURIComponent(html);
 
   function setupInfobarTest()
   {
     nodes = [
-      {node: doc.querySelector("#top"), position: "bottom", tag: "DIV", id: "top", classes: "class1 class2"},
-      {node: doc.querySelector("#vertical"), position: "overlap", tag: "DIV", id: "vertical", classes: ""},
-      {node: doc.querySelector("#bottom"), position: "top", tag: "DIV", id: "bottom", classes: ""},
+      {node: doc.querySelector("#top"), position: "bottom", tag: "DIV", id: "#top", classes: ".class1 .class2"},
+      {node: doc.querySelector("#vertical"), position: "overlap", tag: "DIV", id: "#vertical", classes: ""},
+      {node: doc.querySelector("#bottom"), position: "top", tag: "DIV", id: "#bottom", classes: ""},
       {node: doc.querySelector("body"), position: "overlap", tag: "BODY", id: "", classes: ""},
     ]
 
     for (let i = 0; i < nodes.length; i++) {
       ok(nodes[i].node, "node " + i + " found");
     }
 
     Services.obs.addObserver(runTests,
--- a/browser/devtools/highlighter/test/browser_inspector_tab_switch.js
+++ b/browser/devtools/highlighter/test/browser_inspector_tab_switch.js
@@ -182,20 +182,28 @@ function inspectorFocusTab2()
 
   // Make sure the inspector is still open.
   ok(!InspectorUI.inspecting, "Inspector is not highlighting");
   ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
   ok(!InspectorUI.isSidebarOpen, "Inspector Sidebar is not open");
   is(InspectorUI.store.length, 2, "Inspector.store.length is 2");
   isnot(InspectorUI.selection, div, "selection does not match the div element");
 
-  // Switch back to tab 1.
-  Services.obs.addObserver(inspectorSecondFocusTab1,
-    InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
-  gBrowser.selectedTab = tab1;
+  // Make sure keybindings still sork
+  EventUtils.synthesizeKey("VK_RETURN", { });
+
+  executeSoon(function() {
+    ok(InspectorUI.inspecting, "Inspector is highlighting");
+    InspectorUI.toggleInspection();
+
+    // Switch back to tab 1.
+    Services.obs.addObserver(inspectorSecondFocusTab1,
+      InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
+    gBrowser.selectedTab = tab1;
+  });
 }
 
 function inspectorSecondFocusTab1()
 {
   Services.obs.removeObserver(inspectorSecondFocusTab1,
     InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
 
   ok(InspectorUI.inspecting, "Inspector is highlighting");
--- a/browser/devtools/webconsole/HUDService.jsm
+++ b/browser/devtools/webconsole/HUDService.jsm
@@ -24,16 +24,17 @@
  *   David Dahl <ddahl@mozilla.com> (original author)
  *   Rob Campbell <rcampbell@mozilla.com>
  *   Johnathan Nightingale <jnightingale@mozilla.com>
  *   Patrick Walton <pcwalton@mozilla.com>
  *   Julian Viereck <jviereck@mozilla.com>
  *   Mihai Șucan <mihai.sucan@gmail.com>
  *   Michael Ratcliffe <mratcliffe@mozilla.com>
  *   Joe Walker <jwalker@mozilla.com>
+ *   Sonny Piers <sonny.piers@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -279,16 +280,19 @@ const ERRORS = { LOG_MESSAGE_MISSING_ARG
                  CANNOT_GET_HUD: "Cannot getHeads Up Display with provided ID",
                  MISSING_ARGS: "Missing arguments",
                  LOG_OUTPUT_FAILED: "Log Failure: Could not append messageNode to outputNode",
 };
 
 // The indent of a console group in pixels.
 const GROUP_INDENT = 12;
 
+// The pref prefix for webconsole filters
+const PREFS_PREFIX = "devtools.webconsole.filter.";
+
 /**
  * Implements the nsIStreamListener and nsIRequestObserver interface. Used
  * within the HS_httpObserverFactory function to get the response body of
  * requests.
  *
  * The code is mostly based on code listings from:
  *
  *   http://www.softwareishard.com/blog/firebug/
@@ -1785,19 +1789,28 @@ HUD_SERVICE.prototype =
    * @returns void
    */
   registerDisplay: function HS_registerDisplay(aHUDId)
   {
     // register a display DOM node Id with the service.
     if (!aHUDId){
       throw new Error(ERRORS.MISSING_ARGS);
     }
-    this.filterPrefs[aHUDId] = this.defaultFilterPrefs;
-    // init storage objects:
-    this.storage.createDisplay(aHUDId);
+    this.filterPrefs[aHUDId] = {
+      network: Services.prefs.getBoolPref(PREFS_PREFIX + "network"),
+      networkinfo: Services.prefs.getBoolPref(PREFS_PREFIX + "networkinfo"),
+      csserror: Services.prefs.getBoolPref(PREFS_PREFIX + "csserror"),
+      cssparser: Services.prefs.getBoolPref(PREFS_PREFIX + "cssparser"),
+      exception: Services.prefs.getBoolPref(PREFS_PREFIX + "exception"),
+      jswarn: Services.prefs.getBoolPref(PREFS_PREFIX + "jswarn"),
+      error: Services.prefs.getBoolPref(PREFS_PREFIX + "error"),
+      info: Services.prefs.getBoolPref(PREFS_PREFIX + "info"),
+      warn: Services.prefs.getBoolPref(PREFS_PREFIX + "warn"),
+      log: Services.prefs.getBoolPref(PREFS_PREFIX + "log"),
+    };
   },
 
   /**
    * When a display is being destroyed, unregister it first
    *
    * @param string aHUDId
    *        The ID of a HUD.
    * @returns void
@@ -1834,19 +1847,16 @@ HUD_SERVICE.prototype =
     }
 
     if (hud.jsterm) {
       hud.jsterm.autocompletePopup.destroy();
     }
 
     delete this.hudReferences[aHUDId];
 
-    // remove the related storage object
-    this.storage.removeDisplay(aHUDId);
-
     for (let windowID in this.windowIds) {
       if (this.windowIds[windowID] == aHUDId) {
         delete this.windowIds[windowID];
       }
     }
 
     this.unregisterActiveContext(aHUDId);
 
@@ -1877,20 +1887,16 @@ HUD_SERVICE.prototype =
    * @returns void
    */
   wakeup: function HS_wakeup()
   {
     if (Object.keys(this.hudReferences).length > 0) {
       return;
     }
 
-    this.storage = new ConsoleStorage();
-    this.defaultFilterPrefs = this.storage.defaultDisplayPrefs;
-    this.defaultGlobalConsolePrefs = this.storage.defaultGlobalConsolePrefs;
-
     // begin observing HTTP traffic
     this.startHTTPObservation();
 
     HUDWindowObserver.init();
     HUDConsoleObserver.init();
     ConsoleAPIObserver.init();
   },
 
@@ -1906,20 +1912,17 @@ HUD_SERVICE.prototype =
     delete this.httpObserver;
 
     Services.obs.removeObserver(this.httpResponseExaminer,
                                 "http-on-examine-response");
 
     this.openRequests = {};
     this.openResponseHeaders = {};
 
-    // delete the storage as it holds onto channels
-    delete this.storage;
     delete this.defaultFilterPrefs;
-    delete this.defaultGlobalConsolePrefs;
 
     delete this.lastFinishedRequestCallback;
 
     HUDWindowObserver.uninit();
     HUDConsoleObserver.uninit();
     ConsoleAPIObserver.shutdown();
   },
 
@@ -3933,40 +3936,40 @@ HeadsUpDisplay.prototype = {
     toolbarButton.setAttribute("tooltip", this.getStr("tip" + name));
     toolbarButton.setAttribute("category", aDescriptor.category);
     toolbarButton.setAttribute("hudId", this.hudId);
     toolbarButton.classList.add("webconsole-filter-button");
 
     let menuPopup = this.makeXULNode("menupopup");
     toolbarButton.appendChild(menuPopup);
 
-    let allChecked = true;
+    let someChecked = false;
     for (let i = 0; i < aDescriptor.severities.length; i++) {
       let severity = aDescriptor.severities[i];
       let menuItem = this.makeXULNode("menuitem");
       menuItem.setAttribute("label", this.getStr("btn" + severity.name));
       menuItem.setAttribute("type", "checkbox");
       menuItem.setAttribute("autocheck", "false");
       menuItem.setAttribute("hudId", this.hudId);
 
       let prefKey = severity.prefKey;
       menuItem.setAttribute("prefKey", prefKey);
 
       let checked = this.filterPrefs[prefKey];
       menuItem.setAttribute("checked", checked);
-      if (!checked) {
-        allChecked = false;
+      if (checked) {
+        someChecked = true;
       }
 
       menuItem.addEventListener("command", toggleFilter, false);
 
       menuPopup.appendChild(menuItem);
     }
 
-    toolbarButton.setAttribute("checked", allChecked);
+    toolbarButton.setAttribute("checked", someChecked);
   },
 
   /**
    * Creates the close button on the toolbar.
    *
    * @param nsIDOMNode aParent
    *        The toolbar to attach the close button to.
    * @return void
@@ -6326,185 +6329,16 @@ HeadsUpDisplayUICommands = {
         HUDService.saveRequestAndResponseBodies = checked;
         break;
       }
     }
   },
 
 };
 
-//////////////////////////////////////////////////////////////////////////
-// ConsoleStorage
-//////////////////////////////////////////////////////////////////////////
-
-var prefs = Services.prefs;
-
-const GLOBAL_STORAGE_INDEX_ID = "GLOBAL_CONSOLE";
-const PREFS_PREFIX = "devtools.webconsole.filter.";
-const PREFS = { network: PREFS_PREFIX + "network",
-                networkinfo: PREFS_PREFIX + "networkinfo",
-                csserror: PREFS_PREFIX + "csserror",
-                cssparser: PREFS_PREFIX + "cssparser",
-                exception: PREFS_PREFIX + "exception",
-                jswarn: PREFS_PREFIX + "jswarn",
-                error: PREFS_PREFIX + "error",
-                info: PREFS_PREFIX + "info",
-                warn: PREFS_PREFIX + "warn",
-                log: PREFS_PREFIX + "log",
-              };
-
-function ConsoleStorage()
-{
-  this.sequencer = null;
-  this.consoleDisplays = {};
-  // each display will have an index that tracks each ConsoleEntry
-  this.displayIndexes = {};
-  this.globalStorageIndex = [];
-  this.globalDisplay = {};
-  this.createDisplay(GLOBAL_STORAGE_INDEX_ID);
-  // TODO: need to create a method that truncates the message
-  // see bug 570543
-
-  this.defaultDisplayPrefs = {
-    network: prefs.getBoolPref(PREFS.network),
-    networkinfo: prefs.getBoolPref(PREFS.networkinfo),
-    csserror: prefs.getBoolPref(PREFS.csserror),
-    cssparser: prefs.getBoolPref(PREFS.cssparser),
-    exception: prefs.getBoolPref(PREFS.exception),
-    jswarn: prefs.getBoolPref(PREFS.jswarn),
-    error: prefs.getBoolPref(PREFS.error),
-    info: prefs.getBoolPref(PREFS.info),
-    warn: prefs.getBoolPref(PREFS.warn),
-    log: prefs.getBoolPref(PREFS.log),
-  };
-}
-
-ConsoleStorage.prototype = {
-
-  sequenceId: function CS_sequencerId()
-  {
-    if (!this.sequencer) {
-      this.sequencer = this.createSequencer();
-    }
-    return this.sequencer.next();
-  },
-
-  createSequencer: function CS_createSequencer()
-  {
-    function sequencer(aInt) {
-      while(1) {
-        aInt++;
-        yield aInt;
-      }
-    }
-    return sequencer(-1);
-  },
-
-  globalStore: function CS_globalStore(aIndex)
-  {
-    return this.displayStore(GLOBAL_CONSOLE_DOM_NODE_ID);
-  },
-
-  displayStore: function CS_displayStore(aId)
-  {
-    var self = this;
-    var idx = -1;
-    var id = aId;
-    var aLength = self.displayIndexes[id].length;
-
-    function displayStoreGenerator(aInt, aLength)
-    {
-      // create a generator object to iterate through any of the display stores
-      // from any index-starting-point
-      while(1) {
-        // throw if we exceed the length of displayIndexes?
-        aInt++;
-        var indexIt = self.displayIndexes[id];
-        var index = indexIt[aInt];
-        if (aLength < aInt) {
-          // try to see if we have more entries:
-          var newLength = self.displayIndexes[id].length;
-          if (newLength > aLength) {
-            aLength = newLength;
-          }
-          else {
-            throw new StopIteration();
-          }
-        }
-        var entry = self.consoleDisplays[id][index];
-        yield entry;
-      }
-    }
-
-    return displayStoreGenerator(-1, aLength);
-  },
-
-  recordEntries: function CS_recordEntries(aHUDId, aConfigArray)
-  {
-    var len = aConfigArray.length;
-    for (var i = 0; i < len; i++){
-      this.recordEntry(aHUDId, aConfigArray[i]);
-    }
-  },
-
-
-  recordEntry: function CS_recordEntry(aHUDId, aConfig)
-  {
-    var id = this.sequenceId();
-
-    this.globalStorageIndex[id] = { hudId: aHUDId };
-
-    var displayStorage = this.consoleDisplays[aHUDId];
-
-    var displayIndex = this.displayIndexes[aHUDId];
-
-    if (displayStorage && displayIndex) {
-      var entry = new ConsoleEntry(aConfig, id);
-      displayIndex.push(entry.id);
-      displayStorage[entry.id] = entry;
-      return entry;
-    }
-    else {
-      throw new Error("Cannot get displayStorage or index object for id " + aHUDId);
-    }
-  },
-
-  getEntry: function CS_getEntry(aId)
-  {
-    var display = this.globalStorageIndex[aId];
-    var storName = display.hudId;
-    return this.consoleDisplays[storName][aId];
-  },
-
-  updateEntry: function CS_updateEntry(aUUID)
-  {
-    // update an individual entry
-    // TODO: see bug 568634
-  },
-
-  createDisplay: function CS_createdisplay(aId)
-  {
-    if (!this.consoleDisplays[aId]) {
-      this.consoleDisplays[aId] = {};
-      this.displayIndexes[aId] = [];
-    }
-  },
-
-  removeDisplay: function CS_removeDisplay(aId)
-  {
-    try {
-      delete this.consoleDisplays[aId];
-      delete this.displayIndexes[aId];
-    }
-    catch (ex) {
-      Cu.reportError("Could not remove console display for id " + aId);
-    }
-  }
-};
-
 /**
  * A Console log entry
  *
  * @param JSObject aConfig, object literal with ConsolEntry properties
  * @param integer aId
  * @returns void
  */
 
--- a/browser/devtools/webconsole/NetworkPanel.xhtml
+++ b/browser/devtools/webconsole/NetworkPanel.xhtml
@@ -44,17 +44,17 @@
    - the terms of any one of the MPL, the GPL or the LGPL.
    -
    - ***** END LICENSE BLOCK ***** -->
 
 
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
-  <link rel="stylesheet" href="chrome://global/skin/webConsole_networkPanel.css" type="text/css"/>
+  <link rel="stylesheet" href="chrome://browser/skin/devtools/webconsole_networkpanel.css" type="text/css"/>
 </head>
 <body role="application">
 <table id="header">
   <tr>
     <th class="property-name"
         scope="row">&networkPanel.requestURL;:</th>
     <td class="property-value"
         id="headUrl"></td>
--- a/browser/devtools/webconsole/test/browser/Makefile.in
+++ b/browser/devtools/webconsole/test/browser/Makefile.in
@@ -17,16 +17,17 @@
 # Portions created by the Initial Developer are Copyright (C) 2010
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #  David Dahl <ddahl@mozilla.com>
 #  Patrick Walton <pcwalton@mozilla.com>
 #  Mihai Șucan <mihai.sucan@gmail.com>
 #  Rob Campbell <rcampbell@mozilla.com>
+#  Sonny Piers <sonny.piers@gmail.com>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either of the GNU General Public License Version 2 or later (the "GPL"),
 # or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
@@ -76,20 +77,16 @@ include $(topsrcdir)/config/rules.mk
 	browser_webconsole_log_node_classes.js \
 	browser_webconsole_network_panel.js \
 	browser_webconsole_jsterm.js \
 	browser_webconsole_null_and_undefined_output.js \
 	browser_webconsole_output_order.js \
 	browser_webconsole_property_panel.js \
 	browser_webconsole_property_provider.js \
 	browser_webconsole_registries.js \
-	browser_webconsole_storage_create_display.js \
-	browser_webconsole_storage_iteration.js \
-	browser_webconsole_storage_record_entry.js \
-	browser_webconsole_storage_record_many_entries.js \
 	browser_webconsole_bug_587617_output_copy.js \
 	browser_webconsole_bug_585237_line_limit.js \
 	browser_webconsole_bug_581231_close_button.js \
 	browser_webconsole_bug_582201_duplicate_errors.js \
 	browser_webconsole_bug_580454_timestamp_l10n.js \
 	browser_webconsole_netlogging.js \
 	browser_webconsole_bug_583816_tab_focus.js \
 	browser_webconsole_bug_594477_clickable_output.js \
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_622303_persistent_filters.js
+++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_622303_persistent_filters.js
@@ -1,59 +1,116 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
   let prefService = Services.prefs;
-  let prefs = [
-    "network",
-    "networkinfo",
-    "csserror",
-    "cssparser",
-    "exception",
-    "jswarn",
-    "error",
-    "warn",
-    "info",
-    "log"
-  ];
 
-  //Set all prefs to true
-  prefs.forEach(function(pref) {
-    prefService.setBoolPref("devtools.webconsole.filter." + pref, true);
-  });
+  let prefs = {
+    "net": [
+      "network",
+      "networkinfo"
+    ],
+    "css": [
+      "csserror",
+      "cssparser"
+    ],
+    "js": [
+      "exception",
+      "jswarn"
+    ],
+    "logging": [
+       "error",
+       "warn",
+       "info",
+       "log"
+    ]
+  };
+
+  // Set all prefs to true
+  for (let category in prefs) {
+    prefs[category].forEach(function(pref) {
+      prefService.setBoolPref("devtools.webconsole.filter." + pref, true);
+    });
+  }
 
   addTab("about:blank");
   openConsole();
-  
+
   let hud = HUDService.getHudByWindow(content);
-  let hudId = HUDService.getHudIdByWindow(content);
+
+  // Check if the filters menuitems exists and are checked
+  for (let category in prefs) {
+    let button = hud.HUDBox.querySelector(".webconsole-filter-button[category=\""
+                                           + category + "\"]");
+    ok(isChecked(button), "main button for " + category + " category is checked");
 
-  //Check if the filters menuitems exists and is checked
-  prefs.forEach(function(pref) {
-    let checked = hud.HUDBox.querySelector("menuitem[prefKey=" + pref + "]").
-      getAttribute("checked");
-    is(checked, "true", "menuitem for " + pref + " exists and is checked");
-  });
-  
-  //Set all prefs to false
-  prefs.forEach(function(pref) {
-    HUDService.setFilterState(hudId, pref, false);
-  });
+    prefs[category].forEach(function(pref) {
+      let menuitem = hud.HUDBox.querySelector("menuitem[prefKey=" + pref + "]");
+      ok(isChecked(menuitem), "menuitem for " + pref + " is checked");
+    });
+  }
+
+  // Set all prefs to false
+  for (let category in prefs) {
+    prefs[category].forEach(function(pref) {
+      HUDService.setFilterState(hud.hudId, pref, false);
+    });
+  }
 
   //Re-init the console
   closeConsole();
   openConsole();
 
   hud = HUDService.getHudByWindow(content);
-  
-  //Check if filters menuitems exists and are unchecked
-  prefs.forEach(function(pref) {
-    let checked = hud.HUDBox.querySelector("menuitem[prefKey=" + pref + "]").
-      getAttribute("checked");
-    is(checked, "false", "menuitem for " + pref + " exists and is not checked");
-    prefService.clearUserPref("devtools.webconsole.filter." + pref);
-  });
-  
+
+  // Check if the filter button and menuitems are unchecked
+  for (let category in prefs) {
+    let button = hud.HUDBox.querySelector(".webconsole-filter-button[category=\""
+                                           + category + "\"]");
+    ok(isUnchecked(button), "main button for " + category + " category is not checked");
+
+    prefs[category].forEach(function(pref) {
+      let menuitem = hud.HUDBox.querySelector("menuitem[prefKey=" + pref + "]");
+      ok(isUnchecked(menuitem), "menuitem for " + pref + " is not checked");
+    });
+  }
+
+  // Set first pref in each category to true
+  for (let category in prefs) {
+    HUDService.setFilterState(hud.hudId, prefs[category][0], true);
+  }
+
+  // Re-init the console
+  closeConsole();
+  openConsole();
+
+  hud = HUDService.getHudByWindow(content);
+
+  // Check the main category button is checked and first menuitem is checked
+  for (let category in prefs) {
+    let button = hud.HUDBox.querySelector(".webconsole-filter-button[category=\""
+                                           + category + "\"]");
+    ok(isChecked(button), category  + " button is checked when first pref is true");
+
+    let pref = prefs[category][0];
+    let menuitem = hud.HUDBox.querySelector("menuitem[prefKey=" + pref + "]");
+    ok(isChecked(menuitem), "first " + category + " menuitem is checked");
+  }
+
+  // Clear prefs
+  for (let category in prefs) {
+    prefs[category].forEach(function(pref) {
+      prefService.clearUserPref("devtools.webconsole.filter." + pref);
+    });
+  }
+
   gBrowser.removeCurrentTab();
-  
   finish();
 }
+
+function isChecked(aNode) {
+  return aNode.getAttribute("checked") === "true";
+}
+
+function isUnchecked(aNode) {
+  return aNode.getAttribute("checked") === "false";
+}
deleted file mode 100644
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_storage_create_display.js
+++ /dev/null
@@ -1,73 +0,0 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is DevTools test code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  David Dahl <ddahl@mozilla.com>
- *  Patrick Walton <pcwalton@mozilla.com>
- *  Julian Viereck <jviereck@mozilla.com>
- *  Mihai Sucan <mihai.sucan@gmail.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-// Tests that the console message store is initialized properly.
-
-const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//browser/test-console.html";
-
-function test() {
-  addTab(TEST_URI);
-  browser.addEventListener("DOMContentLoaded", testStorageCreateDisplay,
-                           false);
-}
-
-function testStorageCreateDisplay() {
-  browser.removeEventListener("DOMContentLoaded", testStorageCreateDisplay,
-                              false);
-
-  openConsole();
-
-  let cs = HUDService.storage;
-
-  ok(typeof cs.consoleDisplays == "object",
-     "consoledisplays exist");
-  ok(typeof cs.displayIndexes == "object",
-     "console indexes exist");
-  cs.createDisplay("foo");
-  ok(typeof cs.consoleDisplays["foo"] == "object",
-     "foo display exists");
-  ok(typeof cs.displayIndexes["foo"] == "object",
-     "foo index exists");
-
-  cs.removeDisplay("foo");
-
-  finishTest();
-}
-
deleted file mode 100644
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_storage_iteration.js
+++ /dev/null
@@ -1,92 +0,0 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is DevTools test code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  David Dahl <ddahl@mozilla.com>
- *  Patrick Walton <pcwalton@mozilla.com>
- *  Julian Viereck <jviereck@mozilla.com>
- *  Mihai Sucan <mihai.sucan@gmail.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-// Test that the iterator API of the console message store works.
-
-const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//browser/test-console.html";
-
-function test() {
-  addTab(TEST_URI);
-  browser.addEventListener("DOMContentLoaded", testStorageIteration, false);
-}
-
-function testStorageIteration() {
-  browser.removeEventListener("DOMContentLoaded", testStorageIteration,
-                              false);
-
-  openConsole();
-
-  let cs = HUDService.storage;
-
-  // Must have enough entries present to avoid exhausting the iterators below.
-  cs.createDisplay("foo");
-  for (let i = 0; i < 300; i++) {
-    cs.recordEntry("foo", { logLevel: "network", message: "foo" });
-  }
-
-  var id = "foo";
-  var it = cs.displayStore(id);
-  var entry = it.next();
-  var entry2 = it.next();
-
-  let entries = [];
-  for (var i = 0; i < 100; i++) {
-    let _entry = it.next();
-    entries.push(_entry);
-  }
-
-  ok(entries.length == 100, "entries length == 100");
-
-  let entries2 = [];
-
-  for (var i = 0; i < 100; i++){
-    let _entry = it.next();
-    entries2.push(_entry);
-  }
-
-  ok(entries[0].id != entries2[0].id,
-     "two distinct pages of log entries");
-
-  cs.removeDisplay("foo");
-  cs = null;
-  
-  finishTest();
-}
-
deleted file mode 100644
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_storage_record_entry.js
+++ /dev/null
@@ -1,81 +0,0 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is DevTools test code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  David Dahl <ddahl@mozilla.com>
- *  Patrick Walton <pcwalton@mozilla.com>
- *  Julian Viereck <jviereck@mozilla.com>
- *  Mihai Sucan <mihai.sucan@gmail.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-// Tests that the recordEntry() method of the console store works.
-
-const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//browser/test-console.html";
-
-function test() {
-  addTab(TEST_URI);
-  browser.addEventListener("DOMContentLoaded", testStorageRecordEntry,
-                              false);
-}
-
-function testStorageRecordEntry() {
-  browser.removeEventListener("DOMContentLoaded", testStorageRecordEntry,
-                              false);
-  openConsole();
-
-  let cs = HUDService.storage;
-
-  cs.createDisplay("foo");
-
-  var config = {
-    logLevel: "network",
-    message: "HumminaHummina!",
-    activity: {
-      stage: "barStage",
-      data: "bar bar bar bar"
-    }
-  };
-  var entry = cs.recordEntry("foo", config);
-  var res = entry.id;
-  ok(entry.id != null, "Entry.id is: " + res);
-  ok(cs.displayIndexes["foo"].length == 1,
-     "We added one entry.");
-  entry = cs.getEntry(res);
-  ok(entry.id > -1,
-     "We got an entry through the global interface");
-
-  cs.removeDisplay("foo");
-  cs = null;
-  finishTest();
-}
-
deleted file mode 100644
--- a/browser/devtools/webconsole/test/browser/browser_webconsole_storage_record_many_entries.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/* vim:set ts=2 sw=2 sts=2 et: */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (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.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is DevTools test code.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  David Dahl <ddahl@mozilla.com>
- *  Patrick Walton <pcwalton@mozilla.com>
- *  Julian Viereck <jviereck@mozilla.com>
- *  Mihai Sucan <mihai.sucan@gmail.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-// Test that the recordManyEntries() method of the console store works.
-
-const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test//browser/test-console.html";
-
-function test() {
-  addTab(TEST_URI);
-  browser.addEventListener("DOMContentLoaded", testStorageRecordManyEntries,
-                           false);
-}
-
-function testStorageRecordManyEntries() {
-  browser.removeEventListener("DOMContentLoaded",
-                              testStorageRecordManyEntries, false);
-
-  openConsole();
-
-  let cs = HUDService.storage;
-
-  cs.createDisplay("foo");
-
-  var configArr = [];
-
-  for (var i = 0; i < 1000; i++){
-    let config = {
-      logLevel: "network",
-      message: "HumminaHummina!",
-      activity: {
-        stage: "barStage",
-        data: "bar bar bar bar"
-      }
-    };
-    configArr.push(config);
-  }
-
-  cs.recordEntries("foo", configArr);
-  ok(cs.displayIndexes["foo"].length == 1000,
-     "1000 entries in foo now");
-
-  cs.removeDisplay("foo");
-
-  finishTest();
-}
-
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -129,16 +129,17 @@
 @BINPATH@/components/content_htmldoc.xpt
 @BINPATH@/components/content_html.xpt
 @BINPATH@/components/content_xslt.xpt
 @BINPATH@/components/content_xtf.xpt
 @BINPATH@/components/cookie.xpt
 @BINPATH@/components/directory.xpt
 @BINPATH@/components/docshell.xpt
 @BINPATH@/components/dom.xpt
+@BINPATH@/components/dom_apps.xpt
 @BINPATH@/components/dom_base.xpt
 @BINPATH@/components/dom_battery.xpt
 @BINPATH@/components/dom_canvas.xpt
 @BINPATH@/components/dom_core.xpt
 @BINPATH@/components/dom_css.xpt
 @BINPATH@/components/dom_events.xpt
 @BINPATH@/components/dom_geolocation.xpt
 @BINPATH@/components/dom_notification.xpt
@@ -370,16 +371,18 @@
 @BINPATH@/components/nsPrompter.manifest
 @BINPATH@/components/nsPrompter.js
 #ifdef MOZ_SERVICES_SYNC
 @BINPATH@/components/SyncComponents.manifest
 @BINPATH@/components/Weave.js
 #endif
 @BINPATH@/components/TelemetryPing.js
 @BINPATH@/components/TelemetryPing.manifest
+@BINPATH@/components/Webapps.js
+@BINPATH@/components/Webapps.manifest
 
 ; Modules
 @BINPATH@/modules/*
 
 ; Safe Browsing
 @BINPATH@/components/nsSafebrowsingApplication.manifest
 @BINPATH@/components/nsSafebrowsingApplication.js
 @BINPATH@/components/nsURLClassifier.manifest
--- a/browser/installer/removed-files.in
+++ b/browser/installer/removed-files.in
@@ -247,16 +247,17 @@ res/fonts/mathfontCMEX10.properties
 res/fonts/mathfontCMSY10.properties
 res/fonts/mathfontMTExtra.properties
 res/fonts/mathfontMath1.properties
 res/fonts/mathfontMath2.properties
 res/fonts/mathfontMath4.properties
 res/fonts/mathfontPUA.properties
 res/fonts/pangoFontEncoding.properties
 res/forms.css
+res/full-screen-override.css
 res/hiddenWindow.html
 res/html.css
 res/html/gopher-audio.gif
 res/html/gopher-binary.gif
 res/html/gopher-find.gif
 res/html/gopher-image.gif
 res/html/gopher-menu.gif
 res/html/gopher-movie.gif
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -239,28 +239,26 @@ pu.notifyButton.label=Details…
 pu.notifyButton.accesskey=D
 # LOCALIZATION NOTE %S will be replaced by the short name of the application.
 puNotifyText=%S has been updated
 puAlertTitle=%S Updated
 puAlertText=Click here for details
 
 # Geolocation UI
 
-# LOCALIZATION NOTE (geolocation.shareLocation geolocation.dontShareLocation geolocation.alwaysShare geolocation.neverShare): 
+# LOCALIZATION NOTE (geolocation.shareLocation geolocation.alwaysShareLocation geolocation.neverShareLocation):
 #If you're having trouble with the word Share, please use Allow and Block in your language.
 geolocation.shareLocation=Share Location
 geolocation.shareLocation.accesskey=a
-geolocation.dontShareLocation=Don't Share
-geolocation.dontShareLocation.accesskey=o
-geolocation.alwaysShare=Always Share
-geolocation.alwaysShare.accesskey=A
-geolocation.neverShare=Never Share
-geolocation.neverShare.accesskey=N
-geolocation.siteWantsToKnow=%S wants to know your location.
-geolocation.fileWantsToKnow=The file %S wants to know your location.
+geolocation.alwaysShareLocation=Always Share Location
+geolocation.alwaysShareLocation.accesskey=A
+geolocation.neverShareLocation=Never Share Location
+geolocation.neverShareLocation.accesskey=N
+geolocation.shareWithSite=Would you like to share your location with %S?
+geolocation.shareWithFile=Would you like to share your location with the file %S?
 # LOCALIZATION NOTE (geolocation.learnMore): Use the unicode ellipsis char, \u2026,
 # or use "..." if \u2026 doesn't suit traditions in your locale.
 geolocation.learnMore=Learn More…
 
 # Phishing/Malware Notification Bar.
 # LOCALIZATION NOTE (notAForgery, notAnAttack)
 # The two button strings will never be shown at the same time, so
 # it's okay for them to have the same access key
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -1954,17 +1954,17 @@ panel[dimmed="true"] {
   border-left-style: solid;
   border-top-left-radius: .3em;
   margin-left: 1em;
 }
 
 /* Highlighter */
 
 .highlighter-veil {
-  background-color: rgba(0, 0, 0, 0.5);
+  background-color: rgba(25, 25, 25, 0.5);
 }
 
 #highlighter-closebutton {
   list-style-image: url("moz-icon://stock/gtk-close?size=menu");
   margin-top: 0;
   margin-bottom: 0;
 }
 
rename from toolkit/themes/gnomestripe/global/webConsole.css
rename to browser/themes/gnomestripe/devtools/webconsole.css
--- a/toolkit/themes/gnomestripe/global/webConsole.css
+++ b/browser/themes/gnomestripe/devtools/webconsole.css
@@ -65,17 +65,17 @@
 
 .webconsole-timestamp {
   color: GrayText;
   margin-top: 0;
   margin-bottom: 0;
 }
 
 .hud-msg-node {
-  list-style-image: url(chrome://global/skin/icons/webconsole.png);
+  list-style-image: url(chrome://browser/skin/devtools/webconsole.png);
   -moz-image-region: rect(0, 1px, 0, 0);
 }
 
 .webconsole-msg-icon {
   margin: 3px 4px;
   width: 8px;
   height: 8px;
 }
@@ -157,17 +157,17 @@
 
 .webconsole-filter-button {
   margin: 0 3px;
 }
 
 .webconsole-filter-button > .toolbarbutton-menubutton-button,
 .webconsole-filter-button:not([type="menu-button"]) {
   -moz-box-orient: horizontal;
-  list-style-image: url("chrome://global/skin/icons/webconsole.png");
+  list-style-image: url("chrome://browser/skin/devtools/webconsole.png");
 }
 
 /* Network styles */
 .webconsole-filter-button[category="net"] {
   -moz-image-region: rect(0, 8px, 8px, 0);
 }
 
 .webconsole-msg-network > .webconsole-msg-icon-container {
rename from toolkit/themes/gnomestripe/global/icons/webconsole.png
rename to browser/themes/gnomestripe/devtools/webconsole.png
rename from toolkit/themes/gnomestripe/global/webConsole_networkPanel.css
rename to browser/themes/gnomestripe/devtools/webconsole_networkpanel.css
--- a/browser/themes/gnomestripe/jar.mn
+++ b/browser/themes/gnomestripe/jar.mn
@@ -81,16 +81,19 @@ browser.jar:
   skin/classic/browser/tabview/edit-light.png         (tabview/edit-light.png)
   skin/classic/browser/tabview/search.png             (tabview/search.png)
   skin/classic/browser/tabview/stack-expander.png     (tabview/stack-expander.png)
   skin/classic/browser/tabview/tabview.png            (tabview/tabview.png)
   skin/classic/browser/tabview/tabview.css            (tabview/tabview.css)
   skin/classic/browser/devtools/arrows.png            (devtools/arrows.png)
   skin/classic/browser/devtools/goto-mdn.png          (devtools/goto-mdn.png)
   skin/classic/browser/devtools/csshtmltree.css       (devtools/csshtmltree.css)
+  skin/classic/browser/devtools/webconsole.css                  (devtools/webconsole.css)
+  skin/classic/browser/devtools/webconsole_networkpanel.css     (devtools/webconsole_networkpanel.css)
+  skin/classic/browser/devtools/webconsole.png                  (devtools/webconsole.png)
   skin/classic/browser/devtools/gcli.css              (devtools/gcli.css)
   skin/classic/browser/devtools/breadcrumbs/ltr-end-pressed.png              (devtools/breadcrumbs/ltr-end-pressed.png)
   skin/classic/browser/devtools/breadcrumbs/ltr-end-selected-pressed.png     (devtools/breadcrumbs/ltr-end-selected-pressed.png)
   skin/classic/browser/devtools/breadcrumbs/ltr-end-selected.png             (devtools/breadcrumbs/ltr-end-selected.png)
   skin/classic/browser/devtools/breadcrumbs/ltr-end.png                      (devtools/breadcrumbs/ltr-end.png)
   skin/classic/browser/devtools/breadcrumbs/ltr-middle-pressed.png           (devtools/breadcrumbs/ltr-middle-pressed.png)
   skin/classic/browser/devtools/breadcrumbs/ltr-middle-selected-pressed.png  (devtools/breadcrumbs/ltr-middle-selected-pressed.png)
   skin/classic/browser/devtools/breadcrumbs/ltr-middle-selected.png          (devtools/breadcrumbs/ltr-middle-selected.png)
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -2685,17 +2685,17 @@ panel[dimmed="true"] {
   border-top-left-radius: .3em;
   margin-left: 1em;
 }
 
 
 /* Highlighter */
 
 .highlighter-veil {
-  background-color: rgba(0, 0, 0, 0.5);
+  background-color: rgba(25, 25, 25, 0.5);
 }
 
 #highlighter-closebutton {
   list-style-image: url("chrome://browser/skin/devtools/toolbarbutton-close.png");
   -moz-image-region: rect(0, 16px, 16px, 0);
   min-width: 16px;
   width: 16px;
   margin: 0 4px;
rename from toolkit/themes/pinstripe/global/webConsole.css
rename to browser/themes/pinstripe/devtools/webconsole.css
--- a/toolkit/themes/pinstripe/global/webConsole.css
+++ b/browser/themes/pinstripe/devtools/webconsole.css
@@ -32,17 +32,17 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-%include shared.inc
+%include ../shared.inc
 
 .hud-box {
   border-bottom: 1px solid #aaa;
   text-shadow: none;
 }
 
 .hud-box.animated {
   -moz-transition: height 100ms;
@@ -68,17 +68,17 @@
 
 .webconsole-timestamp {
   color: GrayText;
   margin-top: 0;
   margin-bottom: 0;
 }
 
 .hud-msg-node {
-  list-style-image: url(chrome://global/skin/icons/webconsole.png);
+  list-style-image: url(chrome://browser/skin/devtools/webconsole.png);
   -moz-image-region: rect(0, 1px, 0, 0);
 }
 
 .webconsole-msg-icon {
   margin: 3px 4px;
   width: 8px;
   height: 8px;
 }
@@ -182,17 +182,17 @@
   box-shadow: @roundButtonPressedShadow@;
   background: #d0d0d0;
 }
 
 .webconsole-filter-button > .toolbarbutton-menubutton-button {
   -moz-appearance: none;
   margin: 0;
   padding: 1px 0;
-  list-style-image: url("chrome://global/skin/icons/webconsole.png");
+  list-style-image: url(chrome://browser/skin/devtools/webconsole.png);
   -moz-box-orient: horizontal;
 }
 
 .webconsole-filter-button:hover:active,
 .webconsole-clear-console-button:hover:active {
   text-shadow: @loweredShadow@;
   box-shadow: @roundButtonPressedShadow@;
   background: @roundButtonPressedBackground@;
rename from toolkit/themes/pinstripe/global/icons/webconsole.png
rename to browser/themes/pinstripe/devtools/webconsole.png
rename from toolkit/themes/pinstripe/global/webConsole_networkPanel.css
rename to browser/themes/pinstripe/devtools/webconsole_networkpanel.css
--- a/browser/themes/pinstripe/jar.mn
+++ b/browser/themes/pinstripe/jar.mn
@@ -122,16 +122,19 @@ browser.jar:
   skin/classic/browser/tabview/stack-expander.png           (tabview/stack-expander.png)
   skin/classic/browser/tabview/tabview.png                  (tabview/tabview.png)
   skin/classic/browser/tabview/tabview.css                  (tabview/tabview.css)
   skin/classic/browser/devtools/arrows.png                  (devtools/arrows.png)
   skin/classic/browser/devtools/goto-mdn.png                (devtools/goto-mdn.png)
   skin/classic/browser/devtools/csshtmltree.css             (devtools/csshtmltree.css)
   skin/classic/browser/devtools/gcli.css                    (devtools/gcli.css)
   skin/classic/browser/devtools/toolbarbutton-close.png     (devtools/toolbarbutton-close.png)
+* skin/classic/browser/devtools/webconsole.css                  (devtools/webconsole.css)
+  skin/classic/browser/devtools/webconsole_networkpanel.css     (devtools/webconsole_networkpanel.css)
+  skin/classic/browser/devtools/webconsole.png                  (devtools/webconsole.png)
   skin/classic/browser/devtools/breadcrumbs/ltr-end-pressed.png              (devtools/breadcrumbs/ltr-end-pressed.png)
   skin/classic/browser/devtools/breadcrumbs/ltr-end-selected-pressed.png     (devtools/breadcrumbs/ltr-end-selected-pressed.png)
   skin/classic/browser/devtools/breadcrumbs/ltr-end-selected.png             (devtools/breadcrumbs/ltr-end-selected.png)
   skin/classic/browser/devtools/breadcrumbs/ltr-end.png                      (devtools/breadcrumbs/ltr-end.png)
   skin/classic/browser/devtools/breadcrumbs/ltr-middle-pressed.png           (devtools/breadcrumbs/ltr-middle-pressed.png)
   skin/classic/browser/devtools/breadcrumbs/ltr-middle-selected-pressed.png  (devtools/breadcrumbs/ltr-middle-selected-pressed.png)
   skin/classic/browser/devtools/breadcrumbs/ltr-middle-selected.png          (devtools/breadcrumbs/ltr-middle-selected.png)
   skin/classic/browser/devtools/breadcrumbs/ltr-middle.png                   (devtools/breadcrumbs/ltr-middle.png)
--- a/browser/themes/winstripe/browser-aero.css
+++ b/browser/themes/winstripe/browser-aero.css
@@ -70,16 +70,21 @@
     border: 0;
     -moz-border-end: 1px solid #A9B7C9;
     min-width: 0;
     width: 3px;
     background-color: transparent;
     -moz-margin-start: -3px;
     position: relative;
   }
+
+  .menu-accel,
+  .menu-iconic-accel {
+    color: graytext;
+  }
 }
 
 @media all and (-moz-windows-compositor) {
   /* These should be hidden w/ glass enabled. Windows draws its own buttons. */
   .titlebar-button {
     display: none;
   }
 
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -2620,17 +2620,17 @@ panel[dimmed="true"] {
   border-left-style: solid;
   border-top-left-radius: .3em;
   margin-left: 1em;
 }
 
 /* Highlighter */
 
 .highlighter-veil {
-  background-color: rgba(0, 0, 0, 0.5);
+  background-color: rgba(25, 25, 25, 0.5);
 }
 
 #highlighter-closebutton {
   list-style-image: url("chrome://browser/skin/devtools/toolbarbutton-close.png");
   -moz-image-region: rect(0, 16px, 16px, 0);
   min-width: 16px;
   width: 16px;
   -moz-appearance: none;
rename from toolkit/themes/winstripe/global/webConsole.css
rename to browser/themes/winstripe/devtools/webconsole.css
--- a/toolkit/themes/winstripe/global/webConsole.css
+++ b/browser/themes/winstripe/devtools/webconsole.css
@@ -64,17 +64,17 @@
 
 .webconsole-timestamp {
   color: GrayText;
   margin-top: 0;
   margin-bottom: 0;
 }
 
 .hud-msg-node {
-  list-style-image: url(chrome://global/skin/icons/webconsole.png);
+  list-style-image: url(chrome://browser/skin/devtools/webconsole.png);
   -moz-image-region: rect(0, 1px, 0, 0);
 }
 
 .webconsole-msg-icon {
   margin: 3px 4px;
   width: 8px;
   height: 8px;
 }
@@ -151,17 +151,17 @@
 }
 
 .webconsole-clear-console-button > .toolbarbutton-icon {
   display: none;
 }
 
 .webconsole-filter-button > .toolbarbutton-menubutton-button {
   -moz-box-orient: horizontal;
-  list-style-image: url("chrome://global/skin/icons/webconsole.png");
+  list-style-image: url("chrome://browser/skin/devtools/webconsole.png");
 }
 
 .webconsole-filter-button > .toolbarbutton-menubutton-button,
 .webconsole-filter-button > .toolbarbutton-menubutton-button:hover:active {
   -moz-padding-start: 6px;
   -moz-padding-end: 3px;
 }
 
rename from toolkit/themes/winstripe/global/icons/webconsole.png
rename to browser/themes/winstripe/devtools/webconsole.png
rename from toolkit/themes/winstripe/global/webConsole_networkPanel.css
rename to browser/themes/winstripe/devtools/webconsole_networkpanel.css
--- a/browser/themes/winstripe/jar.mn
+++ b/browser/themes/winstripe/jar.mn
@@ -106,16 +106,19 @@ browser.jar:
         skin/classic/browser/tabview/tabview.png                    (tabview/tabview.png)
         skin/classic/browser/tabview/tabview-inverted.png           (tabview/tabview-inverted.png)
         skin/classic/browser/tabview/tabview.css                    (tabview/tabview.css)
         skin/classic/browser/devtools/arrows.png                    (devtools/arrows.png)
         skin/classic/browser/devtools/goto-mdn.png                  (devtools/goto-mdn.png)
         skin/classic/browser/devtools/csshtmltree.css               (devtools/csshtmltree.css)
         skin/classic/browser/devtools/gcli.css                      (devtools/gcli.css)
         skin/classic/browser/devtools/toolbarbutton-close.png       (devtools/toolbarbutton-close.png)
+        skin/classic/browser/devtools/webconsole.css                  (devtools/webconsole.css)
+        skin/classic/browser/devtools/webconsole_networkpanel.css     (devtools/webconsole_networkpanel.css)
+        skin/classic/browser/devtools/webconsole.png                  (devtools/webconsole.png)
         skin/classic/browser/devtools/breadcrumbs/ltr-end-pressed.png              (devtools/breadcrumbs/ltr-end-pressed.png)
         skin/classic/browser/devtools/breadcrumbs/ltr-end-selected-pressed.png     (devtools/breadcrumbs/ltr-end-selected-pressed.png)
         skin/classic/browser/devtools/breadcrumbs/ltr-end-selected.png             (devtools/breadcrumbs/ltr-end-selected.png)
         skin/classic/browser/devtools/breadcrumbs/ltr-end.png                      (devtools/breadcrumbs/ltr-end.png)
         skin/classic/browser/devtools/breadcrumbs/ltr-middle-pressed.png           (devtools/breadcrumbs/ltr-middle-pressed.png)
         skin/classic/browser/devtools/breadcrumbs/ltr-middle-selected-pressed.png  (devtools/breadcrumbs/ltr-middle-selected-pressed.png)
         skin/classic/browser/devtools/breadcrumbs/ltr-middle-selected.png          (devtools/breadcrumbs/ltr-middle-selected.png)
         skin/classic/browser/devtools/breadcrumbs/ltr-middle.png                   (devtools/breadcrumbs/ltr-middle.png)
@@ -259,16 +262,19 @@ browser.jar:
         skin/classic/aero/browser/tabview/tabview.png                (tabview/tabview.png)
         skin/classic/aero/browser/tabview/tabview-inverted.png       (tabview/tabview-inverted.png)
         skin/classic/aero/browser/tabview/tabview.css                (tabview/tabview.css)
         skin/classic/aero/browser/devtools/arrows.png                (devtools/arrows.png)
         skin/classic/aero/browser/devtools/goto-mdn.png              (devtools/goto-mdn.png)
         skin/classic/aero/browser/devtools/csshtmltree.css           (devtools/csshtmltree.css)
         skin/classic/aero/browser/devtools/gcli.css                  (devtools/gcli.css)
         skin/classic/aero/browser/devtools/toolbarbutton-close.png   (devtools/toolbarbutton-close.png)
+        skin/classic/aero/browser/devtools/webconsole.css                  (devtools/webconsole.css)
+        skin/classic/aero/browser/devtools/webconsole_networkpanel.css     (devtools/webconsole_networkpanel.css)
+        skin/classic/aero/browser/devtools/webconsole.png                  (devtools/webconsole.png)
         skin/classic/aero/browser/devtools/breadcrumbs/ltr-end-pressed.png              (devtools/breadcrumbs/ltr-end-pressed.png)
         skin/classic/aero/browser/devtools/breadcrumbs/ltr-end-selected-pressed.png     (devtools/breadcrumbs/ltr-end-selected-pressed.png)
         skin/classic/aero/browser/devtools/breadcrumbs/ltr-end-selected.png             (devtools/breadcrumbs/ltr-end-selected.png)
         skin/classic/aero/browser/devtools/breadcrumbs/ltr-end.png                      (devtools/breadcrumbs/ltr-end.png)
         skin/classic/aero/browser/devtools/breadcrumbs/ltr-middle-pressed.png           (devtools/breadcrumbs/ltr-middle-pressed.png)
         skin/classic/aero/browser/devtools/breadcrumbs/ltr-middle-selected-pressed.png  (devtools/breadcrumbs/ltr-middle-selected-pressed.png)
         skin/classic/aero/browser/devtools/breadcrumbs/ltr-middle-selected.png          (devtools/breadcrumbs/ltr-middle-selected.png)
         skin/classic/aero/browser/devtools/breadcrumbs/ltr-middle.png                   (devtools/breadcrumbs/ltr-middle.png)
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -133,17 +133,18 @@ class ZipFileReader(object):
 
     for name in self._zipfile.namelist():
       self._extractname(name, path)
 
 log = logging.getLogger()
 
 def isURL(thing):
   """Return True if |thing| looks like a URL."""
-  return urlparse(thing).scheme != ''
+  # We want to download URLs like http://... but not Windows paths like c:\...
+  return len(urlparse(thing).scheme) >= 2
 
 def addCommonOptions(parser, defaults={}):
   parser.add_option("--xre-path",
                     action = "store", type = "string", dest = "xrePath",
                     # individual scripts will set a sane default
                     default = None,
                     help = "absolute path to directory containing XRE (probably xulrunner)")
   if 'SYMBOLS_PATH' not in defaults:
@@ -279,17 +280,17 @@ def getDebuggerInfo(directory, debugger,
       "interactive" : getDebuggerInfo("interactive", False),
       "args": getDebuggerInfo("args", "").split()
     }
 
     if debuggerArgs:
       debuggerInfo["args"] = debuggerArgs.split()
     if debuggerInteractive:
       debuggerInfo["interactive"] = debuggerInteractive
-  
+
   return debuggerInfo
 
 
 def dumpLeakLog(leakLogFile, filter = False):
   """Process the leak log, without parsing it.
 
   Use this function if you want the raw log only.
   Use it preferably with the |XPCOM_MEM_LEAK_LOG| environment variable.
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -46,16 +46,19 @@
 #include "nsJSUtils.h"
 #include "nsNetUtil.h"
 #include "nsScriptLoader.h"
 #include "nsIJSContextStack.h"
 #include "nsIXULRuntime.h"
 #include "nsIScriptError.h"
 #include "nsIConsoleService.h"
 #include "nsIProtocolHandler.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIJSRuntimeService.h"
+#include "xpcpublic.h"
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 
 static bool
 IsChromeProcess()
 {
@@ -803,16 +806,68 @@ nsFrameScriptExecutor::LoadFrameScriptIn
         JSPRINCIPALS_DROP(mCx, jsprin);
       }
     } 
     JSContext* unused;
     nsContentUtils::ThreadJSContextStack()->Pop(&unused);
   }
 }
 
+bool
+nsFrameScriptExecutor::InitTabChildGlobalInternal(nsISupports* aScope)
+{
+  
+  nsCOMPtr<nsIJSRuntimeService> runtimeSvc = 
+    do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
+  NS_ENSURE_TRUE(runtimeSvc, false);
+
+  JSRuntime* rt = nsnull;
+  runtimeSvc->GetRuntime(&rt);
+  NS_ENSURE_TRUE(rt, false);
+
+  JSContext* cx = JS_NewContext(rt, 8192);
+  NS_ENSURE_TRUE(cx, false);
+
+  mCx = cx;
+
+  nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
+
+  JS_SetNativeStackQuota(cx, 128 * sizeof(size_t) * 1024);
+
+  JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_PRIVATE_IS_NSISUPPORTS);
+  JS_SetVersion(cx, JSVERSION_LATEST);
+  JS_SetErrorReporter(cx, ContentScriptErrorReporter);
+
+  xpc_LocalizeContext(cx);
+
+  JSAutoRequest ar(cx);
+  nsIXPConnect* xpc = nsContentUtils::XPConnect();
+  const PRUint32 flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES |
+                         nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT;
+
+  
+  JS_SetContextPrivate(cx, aScope);
+
+  nsresult rv =
+    xpc->InitClassesWithNewWrappedGlobal(cx, aScope,
+                                         NS_GET_IID(nsISupports),
+                                         mPrincipal, nsnull,
+                                         flags, getter_AddRefs(mGlobal));
+  NS_ENSURE_SUCCESS(rv, false);
+
+    
+  JSObject* global = nsnull;
+  rv = mGlobal->GetJSObject(&global);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  JS_SetGlobalObject(cx, global);
+  DidCreateCx();
+  return true;
+}
+
 // static
 void
 nsFrameScriptExecutor::Traverse(nsFrameScriptExecutor *tmp,
                                 nsCycleCollectionTraversalCallback &cb)
 {
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mGlobal)
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCx");
   nsContentUtils::XPConnect()->NoteJSContext(tmp->mCx, cb);
--- a/content/base/src/nsFrameMessageManager.h
+++ b/content/base/src/nsFrameMessageManager.h
@@ -226,16 +226,17 @@ protected:
                             mDelayedCxDestroy(false)
   { MOZ_COUNT_CTOR(nsFrameScriptExecutor); }
   ~nsFrameScriptExecutor()
   { MOZ_COUNT_DTOR(nsFrameScriptExecutor); }
   void DidCreateCx();
   // Call this when you want to destroy mCx.
   void DestroyCx();
   void LoadFrameScriptInternal(const nsAString& aURL);
+  bool InitTabChildGlobalInternal(nsISupports* aScope);
   static void Traverse(nsFrameScriptExecutor *tmp,
                        nsCycleCollectionTraversalCallback &cb);
   nsCOMPtr<nsIXPConnectJSObjectHolder> mGlobal;
   JSContext* mCx;
   PRUint32 mCxStackRefCnt;
   bool mDelayedCxDestroy;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   static nsDataHashtable<nsStringHashKey, nsFrameJSScriptExecutorHolder*>* sCachedScripts;
--- a/content/base/src/nsInProcessTabChildGlobal.cpp
+++ b/content/base/src/nsInProcessTabChildGlobal.cpp
@@ -288,63 +288,20 @@ nsInProcessTabChildGlobal::PreHandleEven
 #endif
 
   return NS_OK;
 }
 
 nsresult
 nsInProcessTabChildGlobal::InitTabChildGlobal()
 {
-  nsCOMPtr<nsIJSRuntimeService> runtimeSvc = 
-    do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
-  NS_ENSURE_STATE(runtimeSvc);
-
-  JSRuntime* rt = nsnull;
-  runtimeSvc->GetRuntime(&rt);
-  NS_ENSURE_STATE(rt);
-
-  JSContext* cx = JS_NewContext(rt, 8192);
-  NS_ENSURE_STATE(cx);
-
-  mCx = cx;
-
-  nsContentUtils::XPConnect()->SetSecurityManagerForJSContext(cx, nsContentUtils::GetSecurityManager(), 0);
-  nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
-
-  JS_SetNativeStackQuota(cx, 128 * sizeof(size_t) * 1024);
-
-  JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_PRIVATE_IS_NSISUPPORTS);
-  JS_SetVersion(cx, JSVERSION_LATEST);
-  JS_SetErrorReporter(cx, ContentScriptErrorReporter);
-
-  xpc_LocalizeContext(cx);
-
-  JSAutoRequest ar(cx);
-  nsIXPConnect* xpc = nsContentUtils::XPConnect();
-  const PRUint32 flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES |
-                         /*nsIXPConnect::OMIT_COMPONENTS_OBJECT ?  |*/
-                         nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT;
 
   nsISupports* scopeSupports =
     NS_ISUPPORTS_CAST(nsIDOMEventTarget*, this);
-  JS_SetContextPrivate(cx, scopeSupports);
-
-  nsresult rv =
-    xpc->InitClassesWithNewWrappedGlobal(cx, scopeSupports,
-                                         NS_GET_IID(nsISupports),
-                                         GetPrincipal(), nsnull,
-                                         flags, getter_AddRefs(mGlobal));
-  NS_ENSURE_SUCCESS(rv, false);
-
-  JSObject* global = nsnull;
-  rv = mGlobal->GetJSObject(&global);
-  NS_ENSURE_SUCCESS(rv, false);
-
-  JS_SetGlobalObject(cx, global);
-  DidCreateCx();
+  NS_ENSURE_STATE(InitTabChildGlobalInternal(scopeSupports));
   return NS_OK;
 }
 
 class nsAsyncScriptLoad : public nsRunnable
 {
 public:
   nsAsyncScriptLoad(nsInProcessTabChildGlobal* aTabChild, const nsAString& aURL)
   : mTabChild(aTabChild), mURL(aURL) {}
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -2486,16 +2486,26 @@ nsXMLHttpRequest::Send(nsIVariant *aBody
       if (!charset.IsEmpty()) {
         nsCAutoString specifiedCharset;
         bool haveCharset;
         PRInt32 charsetStart, charsetEnd;
         rv = NS_ExtractCharsetFromContentType(contentType, specifiedCharset,
                                               &haveCharset, &charsetStart,
                                               &charsetEnd);
         if (NS_SUCCEEDED(rv)) {
+          // special case: the extracted charset is quoted with single quotes
+          // -- for the purpose of preserving what was set we want to handle
+          // them as delimiters (although they aren't really)
+          if (specifiedCharset.Length() >= 2 &&
+              specifiedCharset.First() == '\'' &&
+              specifiedCharset.Last() == '\'') {
+            specifiedCharset = Substring(specifiedCharset, 1,
+                                         specifiedCharset.Length() - 2);
+          }
+
           // If the content-type the page set already has a charset parameter,
           // and it's the same charset, up to case, as |charset|, just send the
           // page-set content-type header.  Apparently at least
           // google-web-toolkit is broken and relies on the exact case of its
           // charset parameter, which makes things break if we use |charset|
           // (which is always a fully resolved charset per our charset alias
           // table, hence might be differently cased).
           if (!specifiedCharset.Equals(charset,
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -2059,16 +2059,20 @@ void nsHTMLMediaElement::ProcessMediaFra
 
 void nsHTMLMediaElement::MetadataLoaded(PRUint32 aChannels, PRUint32 aRate)
 {
   mChannels = aChannels;
   mRate = aRate;
   ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
   DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
   DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
+  if (!mBegun) {
+    // Something ended our downloaded. We're probably done with downloading already.
+    ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
+  }
   if (mDecoder && mDecoder->IsSeekable()) {
     ProcessMediaFragmentURI();
     mDecoder->SetEndTime(mFragmentEnd);
   }
 }
 
 void nsHTMLMediaElement::FirstFrameLoaded(bool aResourceFullyLoaded)
 {
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -1288,22 +1288,20 @@ nsHTMLDocument::GetURL(nsAString& aURL)
   }
 
   CopyUTF8toUTF16(str, aURL);
 
   return NS_OK;
 }
 
 nsIContent*
-nsHTMLDocument::GetBody(nsresult *aResult)
+nsHTMLDocument::GetBody()
 {
   Element* body = GetBodyElement();
 
-  *aResult = NS_OK;
-
   if (body) {
     // There is a body element, return that as the body.
     return body;
   }
 
   // The document is most likely a frameset document so look for the
   // outer most frameset element
   nsRefPtr<nsContentList> nodeList =
@@ -1312,20 +1310,19 @@ nsHTMLDocument::GetBody(nsresult *aResul
   return nodeList->GetNodeAt(0);
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::GetBody(nsIDOMHTMLElement** aBody)
 {
   *aBody = nsnull;
 
-  nsresult rv;
-  nsIContent *body = GetBody(&rv);
-
-  return body ? CallQueryInterface(body, aBody) : rv;
+  nsIContent *body = GetBody();
+
+  return body ? CallQueryInterface(body, aBody) : NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::SetBody(nsIDOMHTMLElement* aBody)
 {
   nsCOMPtr<nsIContent> newBody = do_QueryInterface(aBody);
   Element* root = GetRootElement();
 
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -130,17 +130,18 @@ public:
    * Returns the result of document.all[aID] which can either be a node
    * or a nodelist depending on if there are multiple nodes with the same
    * id.
    */
   nsISupports *GetDocumentAllResult(const nsAString& aID,
                                     nsWrapperCache **aCache,
                                     nsresult *aResult);
 
-  nsIContent *GetBody(nsresult *aResult);
+  nsIContent *GetBody();
+  Element *GetHead() { return GetHeadElement(); }
   already_AddRefed<nsContentList> GetElementsByName(const nsAString & aName)
   {
     return NS_GetFuncStringContentList(this, MatchNameAttribute, nsnull,
                                        UseExistingNameString, aName);
   }
 
 
   virtual nsresult ResolveName(const nsAString& aName,
--- a/content/media/test/manifest.js
+++ b/content/media/test/manifest.js
@@ -347,31 +347,31 @@ function MediaTestManager() {
     this.nextTest();
   }
   
   // Registers that the test corresponding to 'token' has been started.
   // Don't call more than once per token.
   this.started = function(token) {
     this.tokens.push(token);
     this.numTestsRunning++;
-    is(this.numTestsRunning, this.tokens.length, "[started] Length of array should match number of running tests");
+    is(this.numTestsRunning, this.tokens.length, "[started " + token + "] Length of array should match number of running tests");
   }
   
   // Registers that the test corresponding to 'token' has finished. Call when
   // you've finished your test. If all tests are complete this will finish the
   // run, otherwise it may start up the next run. It's ok to call multiple times
   // per token.
   this.finished = function(token) {
     var i = this.tokens.indexOf(token);
     if (i != -1) {
       // Remove the element from the list of running tests.
       this.tokens.splice(i, 1);
     }
     this.numTestsRunning--;
-    is(this.numTestsRunning, this.tokens.length, "[finished] Length of array should match number of running tests");
+    is(this.numTestsRunning, this.tokens.length, "[finished " + token + "] Length of array should match number of running tests");
     if (this.tokens.length < PARALLEL_TESTS) {
       this.nextTest();
     }
   }
 
   // Starts the next batch of tests, or finishes if they're all done.
   // Don't call this directly, call finished(token) when you're done.
   this.nextTest = function() {
--- a/content/media/test/test_preload_actions.html
+++ b/content/media/test/test_preload_actions.html
@@ -40,51 +40,52 @@ var finished = false;
 addLoadEvent(function() {gotLoadEvent=true;});
 
 function log(m) {
   var l = document.getElementById("log");
   l.innerHTML += m;
 }
 
 function maybeFinish(v, n) {
+  if (v._finished) {
+    return;
+  }
+  v._finished = true;
   log(n + ",");
   if (v.parentNode) {
     v.parentNode.removeChild(v);
   }
   manager.finished(v.token);
 }
 
 function filename(uri) {
   return uri.substr(uri.lastIndexOf("/")+1);
 }
 
-// Every test must have a setup(v) function, and must set _finished field on target v to
-// true when test is complete.
+// Every test must have a setup(v) function, and must call maybeFinish() when test is complete.
 var tests = [
   {
     // 1. Add preload:none video with src to document. Load should halt at NETWORK_IDLE and HAVE_NOTHING,
     // after receiving a suspend event. Should not receive loaded events until after we call load().
     // Note the suspend event is explictly sent by our "stop the load" code, but other tests can't rely
     // on it for the preload:metadata case, as there can be multiple suspend events when loading metadata.
     suspend:
     function(e) {
       var v = e.target;
       is(v._gotLoadStart, true, "(1) Must get loadstart.");
       is(v._gotLoadedMetaData, false, "(1) Must not get loadedmetadata.");
       is(v.readyState, v.HAVE_NOTHING, "(1) ReadyState must be HAVE_NOTHING");
       is(v.networkState, v.NETWORK_IDLE, "(1) NetworkState must be NETWORK_IDLE");
-      v._finished = true;
       maybeFinish(v, 1);
     },
     
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
-      v._finished = false;
       v.preload = "none";
       v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
       v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
       v.addEventListener("suspend", this.suspend, false);
       v.src = test.name;
       document.body.appendChild(v); // Causes implicit load, which will be halted due to preload:none.
     },
   },
@@ -93,50 +94,46 @@ var tests = [
     // after suspend event and after loadedmetadata.
     loadeddata:
     function(e) {
       var v = e.target;
       is(v._gotLoadStart, true, "(2) Must get loadstart.");
       is(v._gotLoadedMetaData, true, "(2) Must get loadedmetadata.");
       ok(v.readyState >= v.HAVE_CURRENT_DATA, "(2) ReadyState must be >= HAVE_CURRENT_DATA");
       is(v.networkState, v.NETWORK_IDLE, "(2) NetworkState must be NETWORK_IDLE");
-      v._finished = true;
       maybeFinish(v, 2);
     },
     
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
-      v._finished = false;
       v.preload = "metadata";
       v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
       v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
       v.addEventListener("loadeddata", this.loadeddata, false);
       v.src = test.name;
       document.body.appendChild(v); // Causes implicit load, which will be halted after
                                      // metadata due to preload:metadata.
     },
   },
   {
     // 3. Add preload:auto to document. Should receive canplaythrough eventually.
     canplaythrough:
     function(e) {
       var v = e.target;
       is(v._gotLoadStart, true, "(3) Must get loadstart.");
       is(v._gotLoadedMetaData, true, "(3) Must get loadedmetadata.");
-      v._finished = true;
       maybeFinish(v, 3);
     },
     
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
-      v._finished = false;
       v.preload = "auto";
       v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
       v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
       v.addEventListener("canplaythrough", this.canplaythrough, false);
       v.src = test.name; // Causes implicit load.
       document.body.appendChild(v);
     },
   },
@@ -154,26 +151,24 @@ var tests = [
       is(v.readyState, v.HAVE_NOTHING, "(4) ReadyState must be HAVE_NOTHING");
       is(v.networkState, v.NETWORK_IDLE, "(4) NetworkState must be NETWORK_IDLE");
       v.play(); // Should load and play through.
     },
     
     ended:
     function(e) {
       ok(true, "(4) Got playback ended");
-      e.target._finished = true;
       maybeFinish(e.target, 4);
     },
       
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
       v._gotSuspend = false;
-      v._finished = false;
       v.preload = "none";
       v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
       v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
       v.addEventListener("suspend", this.suspend, false);
       v.addEventListener("ended", this.ended, false);
       v.src = test.name;
       document.body.appendChild(v);
     },
@@ -183,25 +178,23 @@ var tests = [
     // preload:none load. Add a src, it shouldn't load.
     suspend:
     function(e) {
       var v = e.target;
       is(v._gotLoadStart, true, "(5) Must get loadstart.");
       is(v._gotLoadedMetaData, false, "(5) Must not get loadedmetadata.");
       is(v.readyState, v.HAVE_NOTHING, "(5) ReadyState must be HAVE_NOTHING");
       is(v.networkState, v.NETWORK_IDLE, "(5) NetworkState must be NETWORK_IDLE");
-      v._finished = true;
       maybeFinish(v, 5);
     },
       
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
-      v._finished = false;
       v.preload = "none";
       v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
       v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
       v.addEventListener("suspend", this.suspend, false);
       document.body.appendChild(v); // Causes implicit load, which will be halted due to no resource.
       v.src = test.name; // Load should start, and halt at preload:none.
     },
   },
@@ -210,25 +203,23 @@ var tests = [
     // preload:none load. Add a source, it shouldn't load.
     suspend:
     function(e) {
       var v = e.target;
       is(v._gotLoadStart, true, "(6) Must get loadstart.");
       is(v._gotLoadedMetaData, false, "(6) Must not get loadedmetadata.");
       is(v.readyState, v.HAVE_NOTHING, "(6) ReadyState must be HAVE_NOTHING");
       is(v.networkState, v.NETWORK_IDLE, "(6) NetworkState must be NETWORK_IDLE");
-      v._finished = true;
       maybeFinish(v, 6);
     },
       
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
-      v._finished = false;
       v.preload = "none";
       v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
       v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
       v.addEventListener("suspend", this.suspend, false);
       document.body.appendChild(v); // Causes implicit load, which will be halted due to no resource.
       var s = document.createElement("source");
       s.src = test.name;
       s.type = test.type;
@@ -250,26 +241,24 @@ var tests = [
       is(v.networkState, v.NETWORK_IDLE, "(7) NetworkState must be NETWORK_IDLE");
       v.play(); // Should load and play through.
     },
 
     ended:
     function(e) {
       ok(true, "(7) Got playback ended");
       var v = e.target;
-      v._finished = true;
       is(v._gotErrorEvent, true, "(7) Should get error event from first source load failure");      
       maybeFinish(v, 7);
     },
       
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
-      v._finished = false;
       v.preload = "none";
       v._gotErrorEvent = false;
       v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
       v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
       v.addEventListener("suspend", this.suspend, false);
       v.addEventListener("ended", this.ended, false);
       var s1 = document.createElement("source");
       s1.src = "not-a-real-file.404"
@@ -286,124 +275,112 @@ var tests = [
   {
     // 8. Change preload value from none to metadata should cause metadata to be loaded.
     loadeddata:
     function(e) {
       var v = e.target;
       is(v._gotLoadedMetaData, true, "(8) Must get loadedmetadata.");
       ok(v.readyState >= v.HAVE_CURRENT_DATA, "(8) ReadyState must be >= HAVE_CURRENT_DATA on suspend.");
       is(v.networkState, v.NETWORK_IDLE, "(8) NetworkState must be NETWORK_IDLE when load is halted");
-      v._finished = true;
       maybeFinish(v, 8);
     },
     
     setup:
     function(v) {
       v._gotLoadedMetaData = false;
-      v._finished = false;
       v.preload = "none";
       v.addEventListener("loadstart", function(e){v.preload = "metadata";}, false);
       v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
       v.addEventListener("loadeddata", this.loadeddata, false);
       v.src = test.name; // Causes implicit load.
       document.body.appendChild(v);
     },
   },
   /*{
     // 9. Change preload value from metadata to auto should cause entire media to be loaded.
     // For some reason we don't always receive the canplaythrough event, particuarly on this test.
     // We've disabled this test until bug 568402 is fixed.
     canplaythrough:
     function(e) {
       var v = e.target;
-      if (v._finished)
-        return;
       is(v._gotLoadStart, true, "(9) Must get loadstart.");
       is(v._gotLoadedMetaData, true, "(9) Must get loadedmetadata.");
-      v._finished = true;
       maybeFinish(v, 9);
     },
     
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
-      v._finished = false;
       v.preload = "metadata";
       v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
       v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
       v.addEventListener("loadeddata", function(){v.preload = "auto"}, false);
       v.addEventListener("canplaythrough", this.canplaythrough, false);
       v.src = test.name; // Causes implicit load.
       document.body.appendChild(v);
     },
   },*/
   {
     // 10. Change preload value from none to auto should cause entire media to be loaded.
     canplaythrough:
     function(e) {
       var v = e.target;
       is(v._gotLoadedMetaData, true, "(10) Must get loadedmetadata.");
-      v._finished = true;
       maybeFinish(v, 10);
     },
     
     setup:
     function(v) {
       v._gotLoadedMetaData = false;
-      v._finished = false;
       v.preload = "none";
       v.addEventListener("loadstart", function(e){v.preload = "auto";}, false);
       v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
       v.addEventListener("canplaythrough", this.canplaythrough, false);
       v.src = test.name; // Causes implicit load.
       document.body.appendChild(v);
     },
   },
   {
     // 11. Change preload value from none to metadata should cause metadata to load.
     loadeddata:
     function(e) {
       var v = e.target;
       is(v._gotLoadedMetaData, true, "(11) Must get loadedmetadata.");
       ok(v.readyState >= v.HAVE_CURRENT_DATA, "(11) ReadyState must be >= HAVE_CURRENT_DATA.");
       is(v.networkState, v.NETWORK_IDLE, "(11) NetworkState must be NETWORK_IDLE.");
-      v._finished = true;
       maybeFinish(v, 11);
     },
 
     setup:
     function(v) {
       v._gotLoadedMetaData = false;
-      v._finished = false;
       v.preload = "none";
       v.addEventListener("loadstart", function(e){v.preload = "metadata";}, false);
       v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
       v.addEventListener("loadeddata", this.loadeddata, false);
       v.src = test.name; // Causes implicit load.
       document.body.appendChild(v);
     },
   },
   {
     // 12. Change preload value from auto to metadata after load started,
     // should still do full load, should not halt after metadata only.
     canplaythrough:
     function(e) {
       var v = e.target;
       is(v._gotLoadedMetaData, true, "(12) Must get loadedmetadata.");
       is(v._gotLoadStart, true, "(12) Must get loadstart.");
-      v._finished = true;
       maybeFinish(v, 12);
     },
 
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
-      v._finished = false;
       v.preload = "auto";
       v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
       v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
       v.addEventListener("canplaythrough", this.canplaythrough, false);
       v.src = test.name; // Causes implicit load.
       document.body.appendChild(v);
       v.preload = "metadata";
     },
@@ -414,25 +391,23 @@ var tests = [
     // should still load up to metadata, should not halt immediately.
     loadeddata:
     function(e) {
       var v = e.target;
       is(v._gotLoadStart, true, "(13) Must get loadstart.");
       is(v._gotLoadedMetaData, true, "(13) Must get loadedmetadata.");
       ok(v.readyState >= v.HAVE_CURRENT_DATA, "(13) ReadyState must be >= HAVE_CURRENT_DATA.");
       is(v.networkState, v.NETWORK_IDLE, "(13) NetworkState must be NETWORK_IDLE.");
-      v._finished = true;
       maybeFinish(v, 13);
     },
 
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
-      v._finished = false;
       v.preload = "metadata";
       v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
       v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
       v.addEventListener("loadeddata", this.loadeddata, false);
       v.src = test.name; // Causes implicit load.
       document.body.appendChild(v);
       v.preload = "none";
     },
@@ -448,114 +423,104 @@ var tests = [
       is(v.networkState, v.NETWORK_IDLE, "(14) NetworkState must be NETWORK_IDLE");
       v.play();
     },
     
     ended:
     function(e) {
       ok(true, "(14) Got playback ended");
       var v = e.target;
-      v._finished = true;
       maybeFinish(v, 14);
     },
 
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
-      v._finished = false;
       v.preload = "metadata";
       v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
       v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
       v.addEventListener("ended", this.ended, false);
       v.addEventListener("loadeddata", this.loadeddata, false);
       v.src = test.name;
       document.body.appendChild(v); // Causes implicit load, which will be halted after
                                      // metadata due to preload:metadata.
     },
   },
   {
     // 15. Autoplay should override preload:none.
     ended:
     function(e) {
       ok(true, "(15) Got playback ended.");
       var v = e.target;
-      v._finished = true;
       maybeFinish(v, 15);
     },
     
     setup:
     function(v) {
       v._gotLoadStart = false;
       v._gotLoadedMetaData = false;
-      v._finished = false;
       v.preload = "none";
       v.autoplay = true;
       v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
       v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
       v.addEventListener("ended", this.ended, false);
       v.src = test.name; // Causes implicit load.
       document.body.appendChild(v);
     },
   },
   {
     // 16. Autoplay should override preload:metadata.
     ended:
     function(e) {
       ok(true, "(16) Got playback ended.");
       var v = e.target;
-      v._finished = true;
       maybeFinish(v, 16);
     },
     
     setup:
     function(v) {
-      v._finished = false;
       v.preload = "metadata";
       v.autoplay = true;
       v.addEventListener("ended", this.ended, false);
       v.src = test.name; // Causes implicit load.
       document.body.appendChild(v);
     },
   },
   {
     // 17. On a preload:none video, adding autoplay should disable preload none, i.e. don't break autoplay!
     ended:
     function(e) {
       ok(true, "(17) Got playback ended.");
       var v = e.target;
-      v._finished = true;
       maybeFinish(v, 17);
     },
     
     setup:
     function(v) {
       v.addEventListener("ended", this.ended, false);
-      v._finished = false;
       v.preload = "none";
       document.body.appendChild(v); // Causes implicit load, which will be halted due to preload:none.
       v.autoplay = true;
       v.src = test.name;
     },    
   },
   {
     // 18. On a preload='none' video, call play() before load algorithms's sync
     // has run, the play() call should override preload='none'.
     ended:
     function(e) {
       ok(true, "(18) Got playback ended.");
       var v = e.target;
-      v._finished = true;
       maybeFinish(v, 18);
     },
     
     setup:
     function(v) {
       v.addEventListener("ended", this.ended, false);
-      v._finished = false;
       v.preload = "none";
       v.src = test.name; // Schedules async section to continue load algorithm.
       document.body.appendChild(v);
       v.play(); // Should cause preload:none to be overridden.
     },  
     }
 ];
 
--- a/content/svg/content/src/nsSVGPathElement.cpp
+++ b/content/svg/content/src/nsSVGPathElement.cpp
@@ -434,17 +434,17 @@ nsSVGPathElement::GetPathLengthScale(Pat
   if (mPathLength.IsExplicitlySet()) {
     float authorsPathLengthEstimate = mPathLength.GetAnimValue();
     if (authorsPathLengthEstimate > 0) {
       gfxMatrix matrix;
       if (aFor == eForTextPath) {
         // For textPath, a transform on the referenced path affects the
         // textPath layout, so when calculating the actual path length
         // we need to take that into account.
-        matrix = PrependLocalTransformTo(gfxMatrix());
+        matrix = PrependLocalTransformTo(matrix);
       }
       nsRefPtr<gfxFlattenedPath> path = GetFlattenedPath(matrix);
       if (path) {
         return path->GetLength() / authorsPathLengthEstimate;
       }
     }
   }
   return 1.0;
--- a/dom/Makefile.in
+++ b/dom/Makefile.in
@@ -61,16 +61,17 @@ DIRS = \
   interfaces/xul \
   interfaces/storage \
   interfaces/json \
   interfaces/offline \
   interfaces/geolocation \
   interfaces/notification \
   interfaces/svg \
   interfaces/smil \
+  interfaces/apps \
   $(NULL)
 
 DIRS += \
   base \
   battery \
   sms \
   src \
   locales \
--- a/dom/base/Makefile.in
+++ b/dom/base/Makefile.in
@@ -47,17 +47,23 @@ LIBRARY_NAME	= jsdombase_s
 LIBXUL_LIBRARY	= 1
 FORCE_STATIC_LIB = 1
 
 EXTRA_PP_COMPONENTS = \
 		ConsoleAPI.js \
 		ConsoleAPI.manifest \
 		$(NULL)
 
+EXTRA_COMPONENTS = \
+	        Webapps.js \
+	        Webapps.manifest \
+		$(NULL)
+
 EXTRA_JS_MODULES = ConsoleAPIStorage.jsm \
+                Webapps.jsm \
 		$(NULL)
 
 XPIDLSRCS = \
   nsIEntropyCollector.idl \
   nsIScriptChannel.idl \
   $(NULL)
 
 EXPORTS = \
new file mode 100644
--- /dev/null
+++ b/dom/base/Webapps.js
@@ -0,0 +1,346 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Open Web Apps.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Fabrice Desré <fabrice@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+function WebappsRegistry() {
+  this.messages = ["Webapps:Install:Return:OK", "Webapps:Install:Return:KO",
+                   "Webapps:Uninstall:Return:OK", "Webapps:Uninstall:Return:KO",
+                   "Webapps:Enumerate:Return:OK", "Webapps:Enumerate:Return:KO"];
+
+  this.mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
+
+  this.messages.forEach((function(msgName) {
+    this.mm.addMessageListener(msgName, this);
+  }).bind(this));
+
+  this._window = null;
+  this._id = this._getRandomId();
+  this._callbacks = [];
+}
+
+WebappsRegistry.prototype = {
+  _onerror: null,
+  _oninstall: null,
+  _onuninstall: null,
+
+  /** from https://developer.mozilla.org/en/OpenWebApps/The_Manifest
+   * only the name property is mandatory
+   */
+  checkManifest: function(aManifest, aInstallOrigin) {
+    // TODO : check for install_allowed_from
+    if (aManifest.name == undefined)
+      return false;
+    
+    if (aManifest.installs_allowed_from) {
+      ok = false;
+      aManifest.installs_allowed_from.forEach(function(aOrigin) {
+        if (aOrigin == "*" || aOrigin == aInstallOrigin)
+          ok = true;
+      });
+      return ok;
+    }
+    return true;
+  },
+  
+  getCallbackId: function(aCallback) {
+    let id = "id" + this._getRandomId();
+    this._callbacks[id] = aCallback;
+    return id;
+  },
+  
+  getCallback: function(aId) {
+    return this._callbacks[aId];
+  },
+
+  removeCallback: function(aId) {
+    if (this._callbacks[aId])
+      delete this._callbacks[aId];
+  },
+  
+  _getRandomId: function() {
+    return Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
+  },
+
+  _convertAppsArray: function(aApps) {
+    let apps = new Array();
+    for (let i = 0; i < aApps.length; i++) {
+      let app = aApps[i];
+      apps.push(new WebappsApplication(app.origin, app.manifest, app.receipt, app.installOrigin, app.installTime));
+    }
+    return apps;
+  },
+
+  set oninstall(aCallback) {
+    if (this.hasPrivileges)
+      this._oninstall = aCallback;
+    else
+      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+  },
+  
+  set onuninstall(aCallback) {
+    if (this.hasPrivileges)
+      this._onuninstall = aCallback;
+    else
+      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
+  set onerror(aCallback) {
+    this._onerror = aCallback;
+  },
+
+  receiveMessage: function(aMessage) {
+    let msg = aMessage.json;
+    if (!(msg.oid == this._id || aMessage.name == "Webapps:Install:Return:OK" || aMessage.name == "Webapps:Uninstall:Return:OK"))
+      return
+    let app = msg.app;
+    let cb;
+    switch (aMessage.name) {
+      case "Webapps:Install:Return:OK":
+        if (this._oninstall)
+          this._oninstall.handleEvent(new WebappsApplication(app.origin, app.manifest, app.receipt,
+                                                app.installOrigin, app.installTime));
+        break;
+      case "Webapps:Install:Return:KO":
+        if (this._onerror)
+          this._onerror.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.DENIED));
+        break;
+      case "Webapps:Uninstall:Return:OK":
+        if (this._onuninstall)
+          this._onuninstall.handleEvent(new WebappsApplication(msg.origin, null, null, null, 0));
+        break;
+      case "Webapps:Uninstall:Return:KO":
+        if (this._onerror)
+          this._onerror.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.PERMISSION_DENIED));
+        break;
+      case "Webapps:Enumerate:Return:OK":
+        cb = this.getCallback(msg.callbackID);
+        if (cb.success) {
+          let apps = this._convertAppsArray(msg.apps);
+          cb.success.handleEvent(apps, apps.length);
+        }
+        break;
+      case "Webapps:Enumerate:Return:KO":
+        cb = this.getCallback(msg.callbackID);
+        if (cb.error)
+          cb.error.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.PERMISSION_DENIED));
+        break;
+    }
+    this.removeCallback(msg.callbackID);
+  },
+  
+  _fireError: function(aCode) {
+    if (!this._onerror)
+      return;
+    this._onerror.handleEvent(new RegistryError(aCode));
+  },
+
+  _getOrigin: function(aURL) {
+    let uri = Services.io.newURI(aURL, null, null);
+    return uri.prePath; 
+  },
+
+  // mozIDOMApplicationRegistry implementation
+  
+  install: function(aURL, aReceipt) {
+    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
+    xhr.open("GET", aURL, true);
+
+    xhr.addEventListener("load", (function() {
+      if (xhr.status == 200) {
+        try {
+          let installOrigin = this._getOrigin(this._window.location.href);
+          let manifest = JSON.parse(xhr.responseText, installOrigin);
+          if (!this.checkManifest(manifest, installOrigin)) {
+            this._fireError(Ci.mozIDOMApplicationRegistryError.INVALID_MANIFEST);
+          } else {
+            this.mm.sendAsyncMessage("Webapps:Install", { app: { installOrigin: installOrigin,
+                                                          origin: this._getOrigin(aURL),
+                                                          manifest: manifest,
+                                                          receipt: aReceipt },
+                                                          from: this._window.location.href,
+                                                          oid: this._id });
+          }
+        } catch(e) {
+          this._fireError(Ci.mozIDOMApplicationRegistryError.MANIFEST_PARSE_ERROR);
+        }
+      }
+      else {
+        this._fireError(Ci.mozIDOMApplicationRegistryError.MANIFEST_URL_ERROR);
+      }      
+    }).bind(this), false);
+
+    xhr.addEventListener("error", (function() {
+      this._fireError(Ci.mozIDOMApplicationRegistryError.NETWORK_ERROR);
+    }).bind(this), false);
+
+    xhr.send(null);
+  },
+
+  uninstall: function(aOrigin) {
+    if (this.hasPrivileges)
+      this.mm.sendAsyncMessage("Webapps:Uninstall", { from: this._window.location.href,
+                                                      origin: aOrigin,
+                                                      oid: this._id });
+    else
+      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
+  launch: function(aOrigin) {
+    this.mm.sendAsyncMessage("Webapps:Launch", { origin: aOrigin,
+                                                 from: this._window.location.href});
+  },
+  
+  enumerate: function(aSuccess, aError) {
+    this.mm.sendAsyncMessage("Webapps:Enumerate", { from: this._window.location.href,
+                                                    origin: this._getOrigin(this._window.location.href),
+                                                    oid: this._id,
+                                                    callbackID:  this.getCallbackId({ success: aSuccess, error: aError }) });
+  },
+
+  handleEvent: function(aEvent) {
+    if (aEvent.type == "unload") {
+      // remove all callbacks and event handlers so we don't call anything on a cleared scope
+      try {
+        this._oninstall = null;
+        this._onuninstall = null;
+        this._onerror = null;
+        this._callbacks = [];
+      } catch(e) {
+        dump("WebappsRegistry error:" + e + "\n");
+      }
+    }
+  },
+  
+  // nsIDOMGlobalPropertyInitializer implementation
+  init: function(aWindow) {
+    dump("DOMApplicationRegistry::init() " + aWindow + "\n");
+    this._window = aWindow;
+    this._window.addEventListener("unload", this, false);
+    this._window.appId = this._id;
+    let from = Services.io.newURI(this._window.location.href, null, null);
+    let perm = Services.perms.testExactPermission(from, "webapps-manage");
+
+    //only pages with perm set and chrome or about pages can uninstall, enumerate all set oninstall an onuninstall
+    this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION || from.schemeIs("chrome") || from.schemeIs("about");
+  },
+  
+  classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationRegistry, Ci.nsIDOMGlobalPropertyInitializer]),
+  
+  classInfo: XPCOMUtils.generateCI({classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
+                                    contractID: "@mozilla.org/webapps;1",
+                                    interfaces: [Ci.mozIDOMApplicationRegistry],
+                                    flags: Ci.nsIClassInfo.DOM_OBJECT,
+                                    classDescription: "Webapps Registry"})
+}
+
+function WebappsApplication(aOrigin, aManifest, aReceipt, aInstallOrigin, aInstallTime) {
+  this._origin = aOrigin;
+  this._manifest = aManifest;
+  this._receipt = aReceipt;
+  this._installOrigin = aInstallOrigin;
+  this._installTime = aInstallTime;
+}
+
+WebappsApplication.prototype = {
+  _origin: null,
+  _manifest: null,
+  _receipt: null,
+  _installOrigin: null,
+  _installTime: 0,
+
+  get origin() {
+    return this._origin;
+  },
+
+  get manifest() {
+    return this._manifest;
+  },
+
+  get receipt() {
+    return this._receipt;
+  },
+
+  get installOrigin() {
+    return this._installOrigin;
+  },
+  
+  get installTime() {
+    return this._installTime;
+  },
+
+  classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplication]),
+
+  classInfo: XPCOMUtils.generateCI({classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
+                                    contractID: "@mozilla.org/webapps/application;1",
+                                    interfaces: [Ci.mozIDOMApplication],
+                                    flags: Ci.nsIClassInfo.DOM_OBJECT,
+                                    classDescription: "Webapps Application"})
+}
+
+function RegistryError(aCode) {
+  this._code = aCode;
+}
+
+RegistryError.prototype = {
+  _code: null,
+  
+  get code() {
+    return this._code;
+  },
+  
+  classID: Components.ID("{b4937718-11a3-400b-a69f-ab442a418569}"),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationRegistryError]),
+
+  classInfo: XPCOMUtils.generateCI({classID: Components.ID("{b4937718-11a3-400b-a69f-ab442a418569}"),
+                                    contractID: "@mozilla.org/webapps/error;1",
+                                    interfaces: [Ci.mozIDOMApplicationRegistryError],
+                                    flags: Ci.nsIClassInfo.DOM_OBJECT,
+                                    classDescription: "Webapps Registry Error"})
+}
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry, WebappsApplication, RegistryError]);
new file mode 100644
--- /dev/null
+++ b/dom/base/Webapps.jsm
@@ -0,0 +1,380 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Mobile Browser.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Fabrice Desré <fabrice@mozilla.com>
+ *   Mark Finkle <mfinkle@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const Cu = Components.utils; 
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+let EXPORTED_SYMBOLS = ["DOMApplicationRegistry", "DOMApplicationManifest"];
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
+  Cu.import("resource://gre/modules/NetUtil.jsm");
+  return NetUtil;
+});
+
+let DOMApplicationRegistry = {
+  appsDir: null,
+  appsFile: null,
+  webapps: { },
+
+  init: function() {
+    this.mm = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
+    let messages = ["Webapps:Install", "Webapps:Uninstall",
+                    "Webapps:Enumerate", "Webapps:Launch"];
+
+    messages.forEach((function(msgName) {
+      this.mm.addMessageListener(msgName, this);
+    }).bind(this));
+
+    let file =  Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties).get("ProfD", Ci.nsIFile);
+    file.append("webapps");
+    if (!file.exists() || !file.isDirectory()) {
+      file.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
+    }
+    this.appsDir = file;
+    this.appsFile = file.clone();
+    this.appsFile.append("webapps.json");
+    if (!this.appsFile.exists())
+      return;
+    
+    try {
+      let channel = NetUtil.newChannel(this.appsFile);
+      channel.contentType = "application/json";
+      let self = this;
+      NetUtil.asyncFetch(channel, function(aStream, aResult) {
+        if (!Components.isSuccessCode(aResult)) {
+          Cu.reportError("DOMApplicationRegistry: Could not read from json file " + this.appsFile.path);
+          return;
+        }
+
+        // Read json file into a string
+        let data = null;
+        try {
+          self.webapps = JSON.parse(NetUtil.readInputStreamToString(aStream, aStream.available()) || "");
+          aStream.close();
+        } catch (ex) {
+          Cu.reportError("DOMApplicationRegistry: Could not parse JSON: " + ex);
+        }
+      });
+    } catch (ex) {
+      Cu.reportError("DOMApplicationRegistry: Could not read from " + aFile.path + " : " + ex);
+    }
+  },
+
+  receiveMessage: function(aMessage) {
+    let msg = aMessage.json;
+    let from = Services.io.newURI(msg.from, null, null);
+    let perm = Services.perms.testExactPermission(from, "webapps-manage");
+
+    //only pages with perm set and chrome or about pages can uninstall, enumerate all set oninstall an onuninstall
+    let hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION || from.schemeIs("chrome") || from.schemeIs("about");
+
+    switch (aMessage.name) {
+      case "Webapps:Install":
+        // always ask for UI to install
+        Services.obs.notifyObservers(this, "webapps-ask-install", JSON.stringify(msg));
+        break;
+      case "Webapps:Uninstall":
+        if (hasPrivileges)
+          this.uninstall(msg);
+        break;
+      case "Webapps:Launch":
+        Services.obs.notifyObservers(this, "webapps-launch", JSON.stringify(msg));
+        break;
+      case "Webapps:Enumerate":
+        if (hasPrivileges)
+          this.enumerateAll(msg)
+        else
+          this.enumerate(msg);
+        break;
+    }
+  },
+
+  _writeFile: function ss_writeFile(aFile, aData, aCallbak) {
+    // Initialize the file output stream.
+    let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
+    ostream.init(aFile, 0x02 | 0x08 | 0x20, 0600, ostream.DEFER_OPEN);
+
+    // Obtain a converter to convert our data to a UTF-8 encoded input stream.
+    let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
+    converter.charset = "UTF-8";
+
+    // Asynchronously copy the data to the file.
+    let istream = converter.convertToInputStream(aData);
+    NetUtil.asyncCopy(istream, ostream, function(rc) {
+      if (aCallbak)
+        aCallbak();
+    });
+  },
+  
+  // clones a app object, without the manifest
+  _cloneAppObject: function(aApp) {
+    let clone = {
+      installOrigin: aApp.installOrigin,
+      origin: aApp.origin,
+      receipt: aApp.receipt,
+      installTime: aApp.installTime
+    };
+    return clone;
+  },
+  
+  denyInstall: function(aData) {
+    this.mm.sendAsyncMessage("Webapps:Install:Return:KO", aData);
+  },
+  
+  confirmInstall: function(aData) {
+    let app = aData.app;
+    let id = this._appId(app.origin);
+
+    // install an application again is considered as an update
+    if (id) {
+      let dir = this.appsDir.clone();
+      dir.append(id);
+      try {
+        dir.remove(true);
+      } catch(e) {
+      }
+    }
+    else {
+      let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
+      id = uuidGenerator.generateUUID().toString();
+    }
+
+    let dir = this.appsDir.clone();
+    dir.append(id);
+    dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0700);
+    
+    let manFile = dir.clone();
+    manFile.append("manifest.json");
+    this._writeFile(manFile, JSON.stringify(app.manifest));
+
+    this.webapps[id] = this._cloneAppObject(app);
+    delete this.webapps[id].manifest;
+    this.webapps[id].installTime = (new Date()).getTime()
+
+    this._writeFile(this.appsFile, JSON.stringify(this.webapps), (function() {
+      this.mm.sendAsyncMessage("Webapps:Install:Return:OK", aData);
+    }).bind(this));
+  },
+ 
+  _appId: function(aURI) {
+    for (let id in this.webapps) {
+      if (this.webapps[id].origin == aURI)
+        return id;
+    }
+    return null;
+  },
+
+  _readManifest: function(aId) {
+    let file = this.appsDir.clone();
+    file.append(aId);
+    file.append("manifest.json");
+    let data = "";  
+    let fstream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
+    var cstream = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream);
+    fstream.init(file, -1, 0, 0);
+    cstream.init(fstream, "UTF-8", 0, 0);
+    let (str = {}) {  
+      let read = 0;  
+      do {   
+        read = cstream.readString(0xffffffff, str); // read as much as we can and put it in str.value  
+        data += str.value;  
+      } while (read != 0);  
+    }  
+    cstream.close(); // this closes fstream  
+    try {
+      return JSON.parse(data);
+    } catch(e) {
+      return null;
+    }
+  },
+  
+  uninstall: function(aData) {
+    for (let id in this.webapps) {
+      let app = this.webapps[id];
+      if (app.origin == aData.origin) {
+        delete this.webapps[id];
+        this._writeFile(this.appsFile, JSON.stringify(this.webapps));
+        let dir = this.appsDir.clone();
+        dir.append(id);
+        try {
+          dir.remove(true);
+        } catch (e) {
+        }
+        this.mm.sendAsyncMessage("Webapps:Uninstall:Return:OK", aData);
+      }
+    }
+  },
+  
+  enumerate: function(aData) {
+    aData.apps = [];
+
+    let id = this._appId(aData.origin);
+    // if it's an app, add itself to the result
+    if (id) {
+      let app = this._cloneAppObject(this.webapps[id]);
+      app.manifest = this._readManifest(id);
+      aData.apps.push(app);
+    }
+
+    // check if it's a store.
+    let isStore = false;
+    for (id in this.webapps) {
+      let app = this._cloneAppObject(this.webapps[id]);
+      if (app.installOrigin == aData.origin) {
+        isStore = true;
+        break;
+      }
+    }
+
+    // add all the apps from this store
+    if (isStore) {
+      for (id in this.webapps) {
+        let app = this._cloneAppObject(this.webapps[id]);
+        if (app.installOrigin == aData.origin) {
+          app.manifest = this._readManifest(id);
+          aData.apps.push(app);
+        }
+      }
+    }
+
+    this.mm.sendAsyncMessage("Webapps:Enumerate:Return:OK", aData);
+  },
+
+  denyEnumerate: function(aData) {
+    this.mm.sendAsyncMessage("Webapps:Enumerate:Return:KO", aData);
+  },
+
+  enumerateAll: function(aData) {
+    aData.apps = [];
+
+    for (id in this.webapps) {
+      let app = this._cloneAppObject(this.webapps[id]);
+      app.manifest = this._readManifest(id);
+      aData.apps.push(app);
+    }
+
+    this.mm.sendAsyncMessage("Webapps:Enumerate:Return:OK", aData);
+  },
+
+  getManifestFor: function(aOrigin) {
+    let id = this._appId(aOrigin);
+    if (!id)
+      return null;
+    return this._readManifest(id);
+  }
+};
+
+/**
+ * Helper object to access manifest information with locale support
+ */
+DOMApplicationManifest = function(aManifest, aOrigin) {
+  this._origin = Services.io.newURI(aOrigin, null, null);
+  this._manifest = aManifest;
+  let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry)
+                                                          .QueryInterface(Ci.nsIToolkitChromeRegistry);
+  let locale = chrome.getSelectedLocale("browser").toLowerCase();
+  this._localeRoot = this._manifest;
+  
+  if (this._manifest.locales && this._manifest.locales[locale]) {
+    this._localeRoot = this._manifest.locales[locale];
+  }
+  else if (this._manifest.locales) {
+    // try with the language part of the locale ("en" for en-GB) only
+    let lang = locale.split('-')[0];
+    if (land != locale && this._manifest.locales[lang])
+      this._localeRoot = this._manifest.locales[lang];
+  }
+}
+
+DOMApplicationManifest.prototype = {
+  _localeProp: function(aProp) {
+    if (this._localeRoot[aProp] != undefined)
+      return this._localeRoot[aProp];
+    return this._manifest[aProp];
+  },
+
+  get name() {
+    return this._localeProp("name");
+  },
+  
+  get description() {
+    return this._localeProp("description");
+  },
+  
+  get version() {
+    return this._localeProp("version");
+  },
+  
+  get launch_path() {
+    return this._localeProp("launch_path");
+  },
+  
+  get developer() {
+    return this._localeProp("developer");
+  },
+  
+  get icons() {
+    return this._localeProp("icons");
+  },
+  
+  iconURLForSize: function(aSize) {
+    let icons = this._localeProp("icons");
+    if (!icons)
+      return null;
+    let dist = 100000;
+    let icon = null;
+    for (let size in icons) {
+      let iSize = parseInt(size);
+      if (Math.abs(iSize - aSize) < dist) {
+        icon = this._origin.resolve(icons[size]);
+        dist = Math.abs(iSize - aSize);
+      }
+    }
+    return icon;
+  },
+  
+  fullLaunchPath: function() {
+    let launchPath = this._localeProp("launch_path");
+    return this._origin.resolve(launchPath ? launchPath : "");
+  }
+}
+
+DOMApplicationRegistry.init();
new file mode 100644
--- /dev/null
+++ b/dom/base/Webapps.manifest
@@ -0,0 +1,10 @@
+# Webapps.js
+component {fff440b3-fae2-45c1-bf03-3b5a2e432270} Webapps.js
+contract @mozilla.org/webapps;1 {fff440b3-fae2-45c1-bf03-3b5a2e432270}
+category JavaScript-navigator-property mozApps @mozilla.org/webapps;1
+
+component {723ed303-7757-4fb0-b261-4f78b1f6bd22} Webapps.js
+contract @mozilla.org/webapps/application;1 {723ed303-7757-4fb0-b261-4f78b1f6bd22}
+
+component {b4937718-11a3-400b-a69f-ab442a418569} Webapps.js
+contract @mozilla.org/webapps/error;1 {b4937718-11a3-400b-a69f-ab442a418569}
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/apps/Makefile.in
@@ -0,0 +1,53 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (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.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is web apps.
+#
+# The Initial Developer of the Original Code is Mozilla Foundation
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#  Andreas Gal <gal@mozilla.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+
+DEPTH          = ../../..
+topsrcdir      = @top_srcdir@
+srcdir         = @srcdir@
+VPATH          = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE         = dom
+XPIDL_MODULE   = dom_apps
+GRE_MODULE     = 1
+
+XPIDLSRCS =                               \
+            nsIDOMApplicationRegistry.idl \
+            $(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/apps/nsIDOMApplicationRegistry.idl
@@ -0,0 +1,89 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is web apps.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Andreas Gal <gal@mozilla.com>  (Original Author)
+ *  Fabrice Desré <fabrice@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "domstubs.idl"
+
+[scriptable, uuid(e0c271cb-266b-48c9-a7e4-96590b445c26)]
+interface mozIDOMApplicationRegistryError : nsISupports
+{
+  const unsigned short DENIED = 1;
+  const unsigned short PERMISSION_DENIED = 2;
+  const unsigned short MANIFEST_URL_ERROR = 3;
+  const unsigned short NETWORK_ERROR = 4;
+  const unsigned short MANIFEST_PARSE_ERROR = 5;
+  const unsigned short INVALID_MANIFEST = 6;
+
+  readonly attribute short code;
+};
+
+[scriptable, uuid(a6856a3d-dece-43ce-89b9-72dba07f4246)]
+interface mozIDOMApplication : nsISupports
+{
+  readonly attribute jsval manifest;
+  readonly attribute DOMString receipt;
+  readonly attribute DOMString origin;
+  readonly attribute DOMString installOrigin;
+  readonly attribute unsigned long installTime;
+};
+
+[scriptable, function, uuid(be170df5-9154-463b-9197-10a6195eba52)]
+interface mozIDOMApplicationRegistryEnumerateCallback : nsISupports
+{
+  void handleEvent([array, size_is(count)] in mozIDOMApplication apps,
+                    in unsigned long count);
+};
+
+[scriptable, function, uuid(ae0ed33d-35cf-443a-837b-a6cebf16bd49)]
+interface mozIDOMApplicationRegistryErrorCallback : nsISupports
+{
+  void handleEvent(in mozIDOMApplicationRegistryError error);
+};
+
+[scriptable, uuid(4070ea6f-dca1-4052-8bc6-7a9bcfc314ac)]
+interface mozIDOMApplicationRegistry : nsISupports
+{
+  void install(in DOMString manifestUrl,
+	       [optional] in DOMString receipt);
+  void uninstall(in DOMString origin);
+  void enumerate(in mozIDOMApplicationRegistryEnumerateCallback success,
+		 [optional] in mozIDOMApplicationRegistryErrorCallback error);
+  void launch(in DOMString origin);
+
+  attribute nsIDOMEventListener oninstall;
+  attribute nsIDOMEventListener onuninstall;
+  attribute nsIDOMEventListener onerror;
+};
--- a/dom/interfaces/base/Makefile.in
+++ b/dom/interfaces/base/Makefile.in
@@ -52,17 +52,17 @@ SDK_XPIDLSRCS =                         
 	nsIDOMWindow.idl			\
 	nsIDOMWindowCollection.idl		\
 	nsIDOMWindowUtils.idl			\
 	$(NULL)
 
 XPIDLSRCS =					\
 	nsIFrameRequestCallback.idl             \
 	nsIBrowserDOMWindow.idl			\
-    nsIContentPermissionPrompt.idl  \
+	nsIContentPermissionPrompt.idl  \
 	nsIContentPrefService.idl		\
 	nsIContentURIGrouper.idl		\
 	nsIDOMClientInformation.idl		\
 	nsIDOMConstructor.idl			\
 	nsIDOMCRMFObject.idl			\
 	nsIDOMCrypto.idl			\
 	nsIDOMHistory.idl			\
 	nsIDOMLocation.idl			\
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -892,72 +892,29 @@ TabChild::InitTabChildGlobal()
     return true;
 
   nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mWebNav);
   NS_ENSURE_TRUE(window, false);
   nsCOMPtr<nsIDOMEventTarget> chromeHandler =
     do_QueryInterface(window->GetChromeEventHandler());
   NS_ENSURE_TRUE(chromeHandler, false);
 
-  nsCOMPtr<nsIJSRuntimeService> runtimeSvc = 
-    do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
-  NS_ENSURE_TRUE(runtimeSvc, false);
-
-  JSRuntime* rt = nsnull;
-  runtimeSvc->GetRuntime(&rt);
-  NS_ENSURE_TRUE(rt, false);
-
-  JSContext* cx = JS_NewContext(rt, 8192);
-  NS_ENSURE_TRUE(cx, false);
-
-  mCx = cx;
-
-  nsContentUtils::XPConnect()->SetSecurityManagerForJSContext(cx, nsContentUtils::GetSecurityManager(), 0);
-  nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
-
-  JS_SetNativeStackQuota(cx, 128 * sizeof(size_t) * 1024);
-
-  JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_PRIVATE_IS_NSISUPPORTS);
-  JS_SetVersion(cx, JSVERSION_LATEST);
-  JS_SetErrorReporter(cx, ContentScriptErrorReporter);
-
-  xpc_LocalizeContext(cx);
-
-  JSAutoRequest ar(cx);
-  nsIXPConnect* xpc = nsContentUtils::XPConnect();
-  const PRUint32 flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES |
-                         /*nsIXPConnect::OMIT_COMPONENTS_OBJECT ?  |*/
-                         nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT;
-
   nsRefPtr<TabChildGlobal> scope = new TabChildGlobal(this);
   NS_ENSURE_TRUE(scope, false);
 
   mTabChildGlobal = scope;
 
   nsISupports* scopeSupports =
     NS_ISUPPORTS_CAST(nsIDOMEventTarget*, scope);
-  JS_SetContextPrivate(cx, scopeSupports);
-
-  nsresult rv =
-    xpc->InitClassesWithNewWrappedGlobal(cx, scopeSupports,
-                                         NS_GET_IID(nsISupports),
-                                         scope->GetPrincipal(), nsnull,
-                                         flags, getter_AddRefs(mGlobal));
-  NS_ENSURE_SUCCESS(rv, false);
+  
+  NS_ENSURE_TRUE(InitTabChildGlobalInternal(scopeSupports), false); 
 
   nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(chromeHandler);
   NS_ENSURE_TRUE(root, false);
   root->SetParentTarget(scope);
-  
-  JSObject* global = nsnull;
-  rv = mGlobal->GetJSObject(&global);
-  NS_ENSURE_SUCCESS(rv, false);
-
-  JS_SetGlobalObject(cx, global);
-  DidCreateCx();
   return true;
 }
 
 bool
 TabChild::InitWidget(const nsIntSize& size)
 {
     NS_ABORT_IF_FALSE(!mWidget && !mRemoteFrame, "CreateWidget twice?");
 
--- a/dom/plugins/base/android/ANPSurface.cpp
+++ b/dom/plugins/base/android/ANPSurface.cpp
@@ -42,17 +42,16 @@
 
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
 #define ASSIGN(obj, name)   (obj)->name = anp_surface_##name
 
 #define CLEAR_EXCEPTION(env) if (env->ExceptionOccurred()) env->ExceptionClear();
 
 #define ANDROID_REGION_SIZE 512
 
-// Copied from Android headers
 enum {
     PIXEL_FORMAT_RGBA_8888   = 1,
     PIXEL_FORMAT_RGB_565     = 4,
 };
 
 struct SurfaceInfo {
     uint32_t    w;
     uint32_t    h;
--- a/editor/libeditor/base/nsEditorEventListener.cpp
+++ b/editor/libeditor/base/nsEditorEventListener.cpp
@@ -585,23 +585,26 @@ nsEditorEventListener::MouseClick(nsIDOM
       // Would've used the alt key, but the kde wmgr treats alt-middle specially. 
       bool ctrlKey = false;
       mouseEvent->GetCtrlKey(&ctrlKey);
 
       nsCOMPtr<nsIEditorMailSupport> mailEditor;
       if (ctrlKey)
         mailEditor = do_QueryObject(mEditor);
 
-      PRInt32 clipboard;
-
-#if defined(XP_OS2) || defined(XP_WIN32)
-      clipboard = nsIClipboard::kGlobalClipboard;
-#else
-      clipboard = nsIClipboard::kSelectionClipboard;
-#endif
+      PRInt32 clipboard = nsIClipboard::kGlobalClipboard;
+      nsCOMPtr<nsIClipboard> clipboardService =
+        do_GetService("@mozilla.org/widget/clipboard;1", &rv);
+      if (NS_SUCCEEDED(rv)) {
+        bool selectionSupported;
+        rv = clipboardService->SupportsSelectionClipboard(&selectionSupported);
+        if (NS_SUCCEEDED(rv) && selectionSupported) {
+          clipboard = nsIClipboard::kSelectionClipboard;
+        }
+      }
 
       if (mailEditor)
         mailEditor->PasteAsQuotation(clipboard);
       else
         mEditor->Paste(clipboard);
 
       // Prevent the event from propagating up to be possibly handled
       // again by the containing window:
--- a/editor/libeditor/html/tests/Makefile.in
+++ b/editor/libeditor/html/tests/Makefile.in
@@ -82,33 +82,29 @@ include $(topsrcdir)/config/rules.mk
 		test_bug612447.html \
 		test_bug620906.html \
 		test_bug622371.html \
 		test_bug629845.html \
 		test_bug640321.html \
 		test_bug668599.html \
 		test_bug674770-1.html \
 		file_bug674770-1.html \
+		test_bug674770-2.html \
 		test_bug674861.html \
 		test_bug676401.html \
 		test_bug677752.html \
 		test_bug697842.html \
 		test_CF_HTML_clipboard.html \
 		test_contenteditable_focus.html \
 		test_htmleditor_keyevent_handling.html \
 		test_select_all_without_body.html \
 		file_select_all_without_body.html \
 		test_root_element_replacement.html \
 		$(NULL)
 
-ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
-_TEST_FILES +=  test_bug674770-2.html \
-		$(NULL)
-endif
-
 ifneq (mobile,$(MOZ_BUILD_APP))
 _TEST_FILES +=  test_spellcheck_pref.html \
 		$(NULL)
 endif
 
 _DATA_FILES = \
 		data/cfhtml-chromium.txt \
 		data/cfhtml-firefox.txt \
--- a/editor/libeditor/html/tests/test_bug674770-2.html
+++ b/editor/libeditor/html/tests/test_bug674770-2.html
@@ -59,19 +59,19 @@ SimpleTest.waitForFocus(function() {
   frameDocument.getElementById("editor2").addEventListener("click", clickEventHnalder, false);
 
   var text = frameDocument.getElementById("text");
 
   text.focus();
 
   SimpleTest.executeSoon(function() {
     synthesizeKey("a", { accelKey: true }, frameWindow);
-    // Windows doesn't have primary selection, we should copy the text to the
-    // global clipboard.
-    if (navigator.platform.indexOf("Win") == 0) {
+    // Windows and Mac don't have primary selection, we should copy the text to
+    // the global clipboard.
+    if (!SpecialPowers.supportsSelectionClipboard()) {
       SimpleTest.waitForClipboard("pasted",
         function() { synthesizeKey("c", { accelKey: true }, frameWindow); },
         function() { SimpleTest.executeSoon(runInputTests1) },
         cleanup);
     } else {
       // Otherwise, don't call waitForClipboard since it breaks primary
       // selection.
       runInputTests1();
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -1308,17 +1308,17 @@ public:
      * Context reset constants.
      * These are used to determine who is guilty when a context reset
      * happens.
      */
     enum ContextResetARB {
         CONTEXT_NO_ERROR = 0,
         CONTEXT_GUILTY_CONTEXT_RESET_ARB = 0x8253,
         CONTEXT_INNOCENT_CONTEXT_RESET_ARB = 0x8254,
-        CONTEXT_UNKNOWN_CONTEXT_RESET_ARB = 0x8255,
+        CONTEXT_UNKNOWN_CONTEXT_RESET_ARB = 0x8255
     };
 
     bool HasRobustness() {
         return mHasRobustness;
     }
 
 protected:
     bool mInitialized;
--- a/gfx/src/nsRegion.cpp
+++ b/gfx/src/nsRegion.cpp
@@ -210,37 +210,62 @@ void RgnRectMemoryAllocator::Free (nsReg
   mFreeEntries++;
   aRect->next = mFreeListHead;
   mFreeListHead = aRect;
   Unlock ();
 }
 
 
 // Global pool for nsRegion::RgnRect allocation
-static RgnRectMemoryAllocator* gRectPool;
+static PRUintn gRectPoolTlsIndex;
+
+void RgnRectMemoryAllocatorDTOR(void *priv)
+{
+  RgnRectMemoryAllocator* allocator = (static_cast<RgnRectMemoryAllocator*>(
+                                       PR_GetThreadPrivate(gRectPoolTlsIndex)));
+  delete allocator;
+}
 
 nsresult nsRegion::InitStatic()
 {
-  gRectPool = new RgnRectMemoryAllocator(INIT_MEM_CHUNK_ENTRIES);
-  return !gRectPool ? NS_ERROR_OUT_OF_MEMORY : NS_OK;
+  return PR_NewThreadPrivateIndex(&gRectPoolTlsIndex, RgnRectMemoryAllocatorDTOR);
 }
 
 void nsRegion::ShutdownStatic()
 {
-    delete gRectPool;
+  RgnRectMemoryAllocator* allocator = (static_cast<RgnRectMemoryAllocator*>(
+                                       PR_GetThreadPrivate(gRectPoolTlsIndex)));
+  if (!allocator)
+    return;
+
+  delete allocator;
+
+  PR_SetThreadPrivate(gRectPoolTlsIndex, nsnull);
 }
 
 void* nsRegion::RgnRect::operator new (size_t) CPP_THROW_NEW
 {
-  return gRectPool->Alloc ();
+  RgnRectMemoryAllocator* allocator = (static_cast<RgnRectMemoryAllocator*>(
+                                       PR_GetThreadPrivate(gRectPoolTlsIndex)));
+  if (!allocator) {
+    allocator = new RgnRectMemoryAllocator(INIT_MEM_CHUNK_ENTRIES);
+    PR_SetThreadPrivate(gRectPoolTlsIndex, allocator);
+  }
+  return allocator->Alloc ();
 }
 
 void nsRegion::RgnRect::operator delete (void* aRect, size_t)
 {
-  gRectPool->Free (static_cast<RgnRect*>(aRect));
+  RgnRectMemoryAllocator* allocator = (static_cast<RgnRectMemoryAllocator*>(
+                                       PR_GetThreadPrivate(gRectPoolTlsIndex)));
+  if (!allocator) {
+    NS_ERROR("Invalid nsRegion::RgnRect delete");
+    return;
+  }
+  allocator->Free (static_cast<RgnRect*>(aRect));
 }
 
 
 
 void nsRegion::Init()
 {
   mRectListHead.prev = mRectListHead.next = &mRectListHead;
   mCurRect = &mRectListHead;
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -73,16 +73,17 @@
 #include "gfxTextRunWordCache.h"
 
 #include "nsUnicharUtils.h"
 #include "nsUnicodeRange.h"
 #include "gfxUnicodeProperties.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
+#include "mozilla/TimeStamp.h"
 
 using namespace mozilla;
 
 // font info loader constants
 static const PRUint32 kDelayBeforeLoadingCmaps = 8 * 1000; // 8secs
 static const PRUint32 kIntervalBetweenLoadingCmaps = 150; // 150ms
 static const PRUint32 kNumFontsPerSlice = 10; // read in info 10 fonts at a time
 
@@ -401,47 +402,61 @@ gfxPlatformFontList::FindFontForChar(con
             gfxFontStyle normalStyle;
             fontEntry = FindFontForFamily(mReplacementCharFallbackFamily, &normalStyle, needsBold);
         }
 
         if (fontEntry && fontEntry->TestCharacterMap(aCh))
             return fontEntry;
     }
 
+    static bool first = true;
+    TimeStamp start = TimeStamp::Now();
+
     FontSearch data(aCh, aPrevFont);
 
     // iterate over all font families to find a font that support the character
     mFontFamilies.Enumerate(gfxPlatformFontList::FindFontForCharProc, &data);
 
+    TimeDuration elapsed = TimeStamp::Now() - start;
+
 #ifdef PR_LOGGING
     PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textrun);
 
     if (NS_UNLIKELY(log)) {
         PRUint32 charRange = gfxFontUtils::CharRangeBit(aCh);
         PRUint32 unicodeRange = FindCharUnicodeRange(aCh);
         PRUint32 hbscript = gfxUnicodeProperties::GetScriptCode(aCh);
         PR_LOG(log, PR_LOG_DEBUG,\
                ("(textrun-systemfallback) char: u+%6.6x "
-                "char-range: %d unicode-range: %d script: %d match: [%s] count: %d\n",
+                "char-range: %d unicode-range: %d script: %d match: [%s]"
+                " count: %d time: %dus\n",
                 aCh,
                 charRange, unicodeRange, hbscript,
                 (data.mBestMatch ?
                  NS_ConvertUTF16toUTF8(data.mBestMatch->Name()).get() :
                  "<none>"),
-                data.mCount));
+                data.mCount,
+                PRInt32(elapsed.ToMicroseconds())));
     }
 #endif
 
     // no match? add to set of non-matching codepoints
     if (!data.mBestMatch) {
         mCodepointsWithNoFonts.set(aCh);
     } else if (aCh == 0xFFFD) {
         mReplacementCharFallbackFamily = data.mBestMatch->FamilyName();
     }
 
+    PRInt32 intElapsed = PRInt32(first ? elapsed.ToMilliseconds() :
+                                         elapsed.ToMicroseconds());
+    Telemetry::Accumulate((first ? Telemetry::SYSTEM_FONT_FALLBACK_FIRST :
+                                   Telemetry::SYSTEM_FONT_FALLBACK),
+                          intElapsed);
+    first = false;
+
     return data.mBestMatch;
 }
 
 PLDHashOperator PR_CALLBACK 
 gfxPlatformFontList::FindFontForCharProc(nsStringHashKey::KeyType aKey, nsRefPtr<gfxFontFamily>& aFamilyEntry,
      void *userArg)
 {
     FontSearch *data = static_cast<FontSearch*>(userArg);
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -512,18 +512,23 @@ ExecuteRegExp(JSContext *cx, Native nati
     bool ok;
     JSObject *obj = NonGenericMethodGuard(cx, args, native, &RegExpClass, &ok);
     if (!obj)
         return ok;
 
     RegExpObject *reobj = obj->asRegExp();
 
     RegExpMatcher matcher(cx);
-    if (!matcher.reset(reobj))
-        return false;
+    if (reobj->startsWithAtomizedGreedyStar()) {
+        if (!matcher.resetWithTestOptimized(reobj))
+            return false;
+    } else {
+        if (!matcher.reset(reobj))
+            return false;
+    }
 
     RegExpStatics *res = cx->regExpStatics();
 
     /* Step 2. */
     JSString *input = js_ValueToString(cx, (args.length() > 0) ? args[0] : UndefinedValue());
     if (!input)
         return false;
 
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -688,16 +688,20 @@ InitTypeConstructor(JSContext* cx,
   // to all CData objects created from this type constructor. (These will
   // become functions and properties on CData objects created from this type.)
   if (instanceFns && !JS_DefineFunctions(cx, dataProto, instanceFns))
     return false;
 
   if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps))
     return false;
 
+  // Link the type prototype to the data prototype.
+  if (!JS_SetReservedSlot(cx, typeProto, SLOT_OURDATAPROTO, OBJECT_TO_JSVAL(dataProto)))
+    return false;
+
   if (!JS_FreezeObject(cx, obj) ||
       //!JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212!
       !JS_FreezeObject(cx, typeProto))
     return false;
 
   return true;
 }
 
@@ -735,17 +739,17 @@ InitInt64Class(JSContext* cx,
   return prototype;
 }
 
 static JSBool
 AttachProtos(JSContext* cx, JSObject* proto, JSObject** protos)
 {
   // For a given 'proto' of [[Class]] "CTypeProto", attach each of the 'protos'
   // to the appropriate CTypeProtoSlot. (SLOT_UINT64PROTO is the last slot
-  // of [[Class]] "CTypeProto".)
+  // of [[Class]] "CTypeProto" that we fill in this automated manner.)
   for (JSUint32 i = 0; i <= SLOT_UINT64PROTO; ++i) {
     if (!JS_SetReservedSlot(cx, proto, i, OBJECT_TO_JSVAL(protos[i])))
       return false;
   }
 
   return true;
 }
 
@@ -779,16 +783,21 @@ InitTypeClasses(JSContext* cx, JSObject*
   //   * 'prototype' property:
   //     * [[Class]] "CDataProto"
   //     * 'constructor' property === ctypes.CData
   //     * Provides properties and functions common to all CDatas.
   JSObject* CDataProto = InitCDataClass(cx, parent, CTypeProto);
   if (!CDataProto)
     return false;
 
+  // Link CTypeProto to CDataProto.
+  if (!JS_SetReservedSlot(cx, CTypeProto, SLOT_OURDATAPROTO,
+                          OBJECT_TO_JSVAL(CDataProto)))
+    return false;
+
   // Create and attach the special class constructors: ctypes.PointerType,
   // ctypes.ArrayType, ctypes.StructType, and ctypes.FunctionType.
   // Each of these constructors 'c' has, respectively:
   //   * [[Class]] "Function"
   //   * __proto__ === Function.prototype
   //   * A constructor that creates a user-defined type.
   //   * 'prototype' property:
   //     * [[Class]] "CTypeProto"
@@ -2824,16 +2833,22 @@ CType::Trace(JSTracer* trc, JSObject* ob
 }
 
 bool
 CType::IsCType(JSContext* cx, JSObject* obj)
 {
   return JS_GET_CLASS(cx, obj) == &sCTypeClass;
 }
 
+bool
+CType::IsCTypeProto(JSContext* cx, JSObject* obj)
+{
+  return JS_GET_CLASS(cx, obj) == &sCTypeProtoClass;
+}
+
 TypeCode
 CType::GetTypeCode(JSContext* cx, JSObject* typeObj)
 {
   JS_ASSERT(IsCType(cx, typeObj));
 
   jsval result;
   ASSERT_OK(JS_GetReservedSlot(cx, typeObj, SLOT_TYPECODE, &result));
   return TypeCode(JSVAL_TO_INT(result));
@@ -3029,49 +3044,51 @@ JSObject*
 CType::GetProtoFromCtor(JSContext* cx, JSObject* obj, CTypeProtoSlot slot)
 {
   // Get ctypes.{Pointer,Array,Struct}Type.prototype from a reserved slot
   // on the type constructor.
   jsval protoslot;
   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FN_CTORPROTO, &protoslot));
   JSObject* proto = JSVAL_TO_OBJECT(protoslot);
   JS_ASSERT(proto);
-  JS_ASSERT(JS_GET_CLASS(cx, proto) == &sCTypeProtoClass);
+  JS_ASSERT(CType::IsCTypeProto(cx, proto));
 
   // Get the desired prototype.
   jsval result;
   ASSERT_OK(JS_GetReservedSlot(cx, proto, slot, &result));
   return JSVAL_TO_OBJECT(result);
 }
 
 JSObject*
 CType::GetProtoFromType(JSContext* cx, JSObject* obj, CTypeProtoSlot slot)
 {
   JS_ASSERT(IsCType(cx, obj));
 
   // Get the prototype of the type object.
   JSObject* proto = JS_GetPrototype(cx, obj);
   JS_ASSERT(proto);
-  JS_ASSERT(JS_GET_CLASS(cx, proto) == &sCTypeProtoClass);
+  JS_ASSERT(CType::IsCTypeProto(cx, proto));
 
   // Get the requested ctypes.{Pointer,Array,Struct,Function}Type.prototype.
   jsval result;
   ASSERT_OK(JS_GetReservedSlot(cx, proto, slot, &result));
   return JSVAL_TO_OBJECT(result);
 }
 
 JSBool
 CType::PrototypeGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
 {
-  if (!CType::IsCType(cx, obj)) {
-    JS_ReportError(cx, "not a CType");
+  if (!(CType::IsCType(cx, obj) || CType::IsCTypeProto(cx, obj))) {
+    JS_ReportError(cx, "not a CType or CTypeProto");
     return JS_FALSE;
   }
 
-  ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_PROTO, vp));
+  unsigned slot = CType::IsCTypeProto(cx, obj) ? (unsigned) SLOT_OURDATAPROTO
+                                               : (unsigned) SLOT_PROTO;
+  ASSERT_OK(JS_GetReservedSlot(cx, obj, slot, vp));
   JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp) || JSVAL_IS_VOID(*vp));
   return JS_TRUE;
 }
 
 JSBool
 CType::NameGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
 {
   if (!CType::IsCType(cx, obj)) {
@@ -3146,62 +3163,80 @@ CType::CreateArray(JSContext* cx, uintN 
   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
   return JS_TRUE;
 }
 
 JSBool
 CType::ToString(JSContext* cx, uintN argc, jsval* vp)
 {
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
-  if (!obj || !CType::IsCType(cx, obj)) {
+  if (!obj ||
+      !(CType::IsCType(cx, obj) || CType::IsCTypeProto(cx, obj)))
+  {
     JS_ReportError(cx, "not a CType");
     return JS_FALSE;
   }
 
-  AutoString type;
-  AppendString(type, "type ");
-  AppendString(type, GetName(cx, obj));
-
-  JSString* result = NewUCString(cx, type);
+  // Create the appropriate string depending on whether we're sCTypeClass or
+  // sCTypeProtoClass.
+  JSString* result;
+  if (CType::IsCType(cx, obj)) {
+    AutoString type;
+    AppendString(type, "type ");
+    AppendString(type, GetName(cx, obj));
+    result = NewUCString(cx, type);
+  }
+  else {
+    result = JS_NewStringCopyZ(cx, "[CType proto object]");
+  }
   if (!result)
     return JS_FALSE;
-  
+
   JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
   return JS_TRUE;
 }
 
 JSBool
 CType::ToSource(JSContext* cx, uintN argc, jsval* vp)
 {
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
-  if (!obj || !CType::IsCType(cx, obj)) {
+  if (!obj ||
+      !(CType::IsCType(cx, obj) || CType::IsCTypeProto(cx, obj)))
+  {
     JS_ReportError(cx, "not a CType");
     return JS_FALSE;
   }
 
-  AutoString source;
-  BuildTypeSource(cx, obj, false, source);
-  JSString* result = NewUCString(cx, source);
+  // Create the appropriate string depending on whether we're sCTypeClass or
+  // sCTypeProtoClass.
+  JSString* result;
+  if (CType::IsCType(cx, obj)) {
+    AutoString source;
+    BuildTypeSource(cx, obj, false, source);
+    result = NewUCString(cx, source);
+  } else {
+    result = JS_NewStringCopyZ(cx, "[CType proto object]");
+  }
   if (!result)
     return JS_FALSE;
-  
+
   JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
   return JS_TRUE;
 }
 
 JSBool
 CType::HasInstance(JSContext* cx, JSObject* obj, const jsval* v, JSBool* bp)
 {
   JS_ASSERT(CType::IsCType(cx, obj));
 
   jsval slot;
   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_PROTO, &slot));
   JSObject* prototype = JSVAL_TO_OBJECT(slot);
   JS_ASSERT(prototype);
-  JS_ASSERT(JS_GET_CLASS(cx, prototype) == &sCDataProtoClass);
+  JS_ASSERT(CData::IsCDataProto(cx, prototype));
 
   *bp = JS_FALSE;
   if (JSVAL_IS_PRIMITIVE(*v))
     return JS_TRUE;
 
   JSObject* proto = JSVAL_TO_OBJECT(*v);
   while ((proto = JS_GetPrototype(cx, proto))) {
     if (proto == prototype) {
@@ -5264,17 +5299,17 @@ CClosure::Create(JSContext* cx,
     JS_ReportOutOfMemory(cx);
     return NULL;
   }
 
   // Get the prototype of the FunctionType object, of class CTypeProto,
   // which stores our JSContext for use with the closure.
   JSObject* proto = JS_GetPrototype(cx, typeObj);
   JS_ASSERT(proto);
-  JS_ASSERT(JS_GET_CLASS(cx, proto) == &sCTypeProtoClass);
+  JS_ASSERT(CType::IsCTypeProto(cx, proto));
 
   // Get a JSContext for use with the closure.
   jsval slot;
   ASSERT_OK(JS_GetReservedSlot(cx, proto, SLOT_CLOSURECX, &slot));
   if (!JSVAL_IS_VOID(slot)) {
     // Use the existing JSContext.
     cinfo->cx = static_cast<JSContext*>(JSVAL_TO_PRIVATE(slot));
     JS_ASSERT(cinfo->cx);
@@ -5694,16 +5729,22 @@ CData::GetData(JSContext* cx, JSObject* 
 }
 
 bool
 CData::IsCData(JSContext* cx, JSObject* obj)
 {
   return JS_GET_CLASS(cx, obj) == &sCDataClass;
 }
 
+bool
+CData::IsCDataProto(JSContext* cx, JSObject* obj)
+{
+  return JS_GET_CLASS(cx, obj) == &sCDataProtoClass;
+}
+
 JSBool
 CData::ValueGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp)
 {
   if (!IsCData(cx, obj)) {
     JS_ReportError(cx, "not a CData");
     return JS_FALSE;
   }
 
@@ -5928,39 +5969,45 @@ JSBool
 CData::ToSource(JSContext* cx, uintN argc, jsval* vp)
 {
   if (argc != 0) {
     JS_ReportError(cx, "toSource takes zero arguments");
     return JS_FALSE;
   }
 
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
-  if (!obj || !CData::IsCData(cx, obj)) {
+  if (!obj ||
+      !(CData::IsCData(cx, obj) || CData::IsCDataProto(cx, obj))) {
     JS_ReportError(cx, "not a CData");
     return JS_FALSE;
   }
 
-  JSObject* typeObj = CData::GetCType(cx, obj);
-  void* data = CData::GetData(cx, obj);
-
-  // Walk the types, building up the toSource() string.
-  // First, we build up the type expression:
-  // 't.ptr' for pointers;
-  // 't.array([n])' for arrays;
-  // 'n' for structs, where n = t.name, the struct's name. (We assume this is
-  // bound to a variable in the current scope.)
-  AutoString source;
-  BuildTypeSource(cx, typeObj, true, source);
-  AppendString(source, "(");
-  if (!BuildDataSource(cx, typeObj, data, false, source))
-    return JS_FALSE;
-
-  AppendString(source, ")");
-
-  JSString* result = NewUCString(cx, source);
+  JSString* result;
+  if (CData::IsCData(cx, obj)) {
+    JSObject* typeObj = CData::GetCType(cx, obj);
+    void* data = CData::GetData(cx, obj);
+
+    // Walk the types, building up the toSource() string.
+    // First, we build up the type expression:
+    // 't.ptr' for pointers;
+    // 't.array([n])' for arrays;
+    // 'n' for structs, where n = t.name, the struct's name. (We assume this is
+    // bound to a variable in the current scope.)
+    AutoString source;
+    BuildTypeSource(cx, typeObj, true, source);
+    AppendString(source, "(");
+    if (!BuildDataSource(cx, typeObj, data, false, source))
+      return JS_FALSE;
+
+    AppendString(source, ")");
+
+    result = NewUCString(cx, source);
+  }
+  else
+    result = JS_NewStringCopyZ(cx, "[CData proto object]");
   if (!result)
     return JS_FALSE;
 
   JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(result));
   return JS_TRUE;
 }
 
 /*******************************************************************************
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -387,17 +387,18 @@ enum CTypeProtoSlot {
   SLOT_FUNCTIONPROTO     = 3,  // ctypes.FunctionType.prototype object
   SLOT_CDATAPROTO        = 4,  // ctypes.CData.prototype object
   SLOT_POINTERDATAPROTO  = 5,  // common ancestor of all CData objects of PointerType
   SLOT_ARRAYDATAPROTO    = 6,  // common ancestor of all CData objects of ArrayType
   SLOT_STRUCTDATAPROTO   = 7,  // common ancestor of all CData objects of StructType
   SLOT_FUNCTIONDATAPROTO = 8,  // common ancestor of all CData objects of FunctionType
   SLOT_INT64PROTO        = 9,  // ctypes.Int64.prototype object
   SLOT_UINT64PROTO       = 10, // ctypes.UInt64.prototype object
-  SLOT_CLOSURECX         = 11, // JSContext for use with FunctionType closures
+  SLOT_OURDATAPROTO      = 11, // the data prototype corresponding to this object
+  SLOT_CLOSURECX         = 12, // JSContext for use with FunctionType closures
   CTYPEPROTO_SLOTS
 };
 
 enum CTypeSlot {
   SLOT_PROTO     = 0, // 'prototype' property of the CType object
   SLOT_TYPECODE  = 1, // TypeCode of the CType object
   SLOT_FFITYPE   = 2, // ffi_type representing the type
   SLOT_NAME      = 3, // name of the type
@@ -452,16 +453,17 @@ namespace CType {
   JSObject* Create(JSContext* cx, JSObject* typeProto, JSObject* dataProto,
     TypeCode type, JSString* name, jsval size, jsval align, ffi_type* ffiType);
 
   JSObject* DefineBuiltin(JSContext* cx, JSObject* parent, const char* propName,
     JSObject* typeProto, JSObject* dataProto, const char* name, TypeCode type,
     jsval size, jsval align, ffi_type* ffiType);
 
   bool IsCType(JSContext* cx, JSObject* obj);
+  bool IsCTypeProto(JSContext* cx, JSObject* obj);
   TypeCode GetTypeCode(JSContext* cx, JSObject* typeObj);
   bool TypesEqual(JSContext* cx, JSObject* t1, JSObject* t2);
   size_t GetSize(JSContext* cx, JSObject* obj);
   bool GetSafeSize(JSContext* cx, JSObject* obj, size_t* result);
   bool IsSizeDefined(JSContext* cx, JSObject* obj);
   size_t GetAlignment(JSContext* cx, JSObject* obj);
   ffi_type* GetFFIType(JSContext* cx, JSObject* obj);
   JSString* GetName(JSContext* cx, JSObject* obj);
@@ -515,16 +517,17 @@ namespace CClosure {
 
 namespace CData {
   JSObject* Create(JSContext* cx, JSObject* typeObj, JSObject* refObj,
     void* data, bool ownResult);
 
   JSObject* GetCType(JSContext* cx, JSObject* dataObj);
   void* GetData(JSContext* cx, JSObject* dataObj);
   bool IsCData(JSContext* cx, JSObject* obj);
+  bool IsCDataProto(JSContext* cx, JSObject* obj);
 
   // Attached by JSAPI as the function 'ctypes.cast'
   JSBool Cast(JSContext* cx, uintN argc, jsval* vp);
   // Attached by JSAPI as the function 'ctypes.getRuntime'
   JSBool GetRuntime(JSContext* cx, uintN argc, jsval* vp);
 }
 
 namespace Int64 {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug691299-regexp.js
@@ -0,0 +1,3 @@
+// |jit-test| error: SyntaxError: invalid quantifier
+
+String.fromCharCode(256).replace(/[^a$]{4294967295}/,"aaaa");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug691797-regexp-1.js
@@ -0,0 +1,4 @@
+var re = /.*star.*/i;
+var str = "The Shawshank Redemption (1994)";
+for (var k = 0; k < 100; k++)
+  assertEq(false, re.test(str));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug691797-regexp-2.js
@@ -0,0 +1,6 @@
+var re = /.*(a\w).*/i;
+var str = "abccccccad";
+for (var k = 0; k < 100; k++) {
+  re.test(str);
+  assertEq('ad', RegExp['$1']);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/bug706110.js
@@ -0,0 +1,18 @@
+function test() {
+  function Bug() { this.prototype }
+  var actual = (new Bug instanceof Bug);
+  assertEq(actual, true);
+}
+test();
+test();
+
+function testLambdaCtor() {
+    var q;
+    for (var x = 0; x < 2; ++x) {
+        var f = function(){};
+	if (x == 1) gc();
+        q = new f;
+    }
+    return q.__proto__ === f.prototype;
+}
+assertEq(testLambdaCtor(), true);
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -125,16 +125,17 @@ class RegExpMatcher;
 class RegExpObjectBuilder;
 class RegExpStatics;
 class MatchPairs;
 
 namespace detail {
 
 class RegExpPrivate;
 class RegExpPrivateCode;
+class RegExpPrivateCacheValue;
 
 } /* namespace detail */
 
 enum RegExpFlag
 {
     IgnoreCaseFlag  = 0x01,
     GlobalFlag      = 0x02,
     MultilineFlag   = 0x04,
@@ -231,17 +232,20 @@ typedef Vector<UpvarCookie, 8> UpvarCook
 
 class Breakpoint;
 class BreakpointSite;
 typedef HashMap<jsbytecode *, BreakpointSite *, DefaultHasher<jsbytecode *>, RuntimeAllocPolicy>
     BreakpointSiteMap;
 class Debugger;
 class WatchpointMap;
 
-typedef HashMap<JSAtom *, detail::RegExpPrivate *, DefaultHasher<JSAtom *>, RuntimeAllocPolicy>
+typedef HashMap<JSAtom *,
+                detail::RegExpPrivateCacheValue,
+                DefaultHasher<JSAtom *>,
+                RuntimeAllocPolicy>
     RegExpPrivateCache;
 
 typedef JSNative             Native;
 typedef JSPropertyOp         PropertyOp;
 typedef JSStrictPropertyOp   StrictPropertyOp;
 typedef JSPropertyDescriptor PropertyDescriptor;
 
 namespace analyze {
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -7267,18 +7267,22 @@ mjit::Compiler::leaveBlock()
 bool
 mjit::Compiler::constructThis()
 {
     JS_ASSERT(isConstructing);
 
     JSFunction *fun = script->function();
 
     do {
-        if (!cx->typeInferenceEnabled() || fun->getType(cx)->unknownProperties())
+        if (!cx->typeInferenceEnabled() ||
+            !fun->hasSingletonType() ||
+            fun->getType(cx)->unknownProperties())
+        {
             break;
+        }
 
         jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
         types::TypeSet *protoTypes = fun->getType(cx)->getProperty(cx, id, false);
 
         JSObject *proto = protoTypes->getSingleton(cx, true);
         if (!proto)
             break;
 
--- a/js/src/tests/js1_5/GC/jstests.list
+++ b/js/src/tests/js1_5/GC/jstests.list
@@ -1,11 +1,11 @@
 url-prefix ../../jsreftest.html?test=js1_5/GC/
 script regress-104584.js
-skip-if(!xulRuntime.shell&&xulRuntime.OS=="Darwin") slow script regress-203278-2.js
+script regress-203278-2.js
 script regress-203278-3.js
 script regress-278725.js
 script regress-306788.js
 script regress-311497.js
 script regress-313276.js
 script regress-313479.js
 script regress-316885-01.js
 script regress-316885-02.js
--- a/js/src/tests/js1_5/Regress/jstests.list
+++ b/js/src/tests/js1_5/Regress/jstests.list
@@ -36,17 +36,17 @@ skip script regress-173067.js # obsolete
 script regress-174709.js
 script regress-176125.js
 script regress-179524.js
 script regress-185165.js
 script regress-191633.js
 script regress-191668.js
 script regress-192414.js
 script regress-193418.js
-skip script regress-203278-1.js # slow
+script regress-203278-1.js
 script regress-203402.js
 script regress-203841.js
 script regress-204210.js
 script regress-210682.js
 script regress-211590.js
 script regress-213482.js
 script regress-214761.js
 script regress-216320.js
--- a/js/src/tests/js1_8/regress/jstests.list
+++ b/js/src/tests/js1_8/regress/jstests.list
@@ -59,17 +59,17 @@ script regress-467495-02.js
 script regress-467495-03.js
 script regress-467495-04.js
 script regress-467495-05.js
 script regress-467495-06.js
 script regress-468711.js
 script regress-469547.js
 script regress-469625-02.js
 script regress-469625-03.js
-skip-if(Android) script regress-471373.js # bug xxx No test results reported
+script regress-471373.js
 script regress-471660.js
 script regress-472450-01.js
 script regress-472450-02.js
 script regress-472528-01.js
 script regress-472528-02.js
 script regress-472703.js
 script regress-474769.js
 skip-if(Android) script regress-474771.js
--- a/js/src/vm/RegExpObject-inl.h
+++ b/js/src/vm/RegExpObject-inl.h
@@ -84,16 +84,33 @@ HasRegExpMetaChars(const jschar *chars, 
 {
     for (size_t i = 0; i < length; ++i) {
         if (IsRegExpMetaChar(chars[i]))
             return true;
     }
     return false;
 }
 
+inline bool
+RegExpObject::startsWithAtomizedGreedyStar() const
+{
+    JSLinearString *source = getSource();
+
+    if (!source->isAtom())
+        return false;
+
+    if (source->length() < 3)
+        return false;
+
+    const jschar *chars = source->chars();
+    return chars[0] == detail::GreedyStarChars[0] &&
+           chars[1] == detail::GreedyStarChars[1] &&
+           chars[2] != '?';
+}
+
 inline size_t *
 RegExpObject::addressOfPrivateRefCount() const
 {
     JS_ASSERT(getPrivate());
     return getPrivate()->addressOfRefCount();
 }
 
 inline void
@@ -252,55 +269,59 @@ detail::RegExpPrivate::getOrCreateCache(
         return cache;
 
     js_ReportOutOfMemory(cx);
     return NULL;
 }
 
 inline bool
 detail::RegExpPrivate::cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags,
+                                   RegExpPrivateCacheKind targetKind,
                                    AlreadyIncRefed<RegExpPrivate> *result)
 {
     RegExpPrivateCache *cache = getOrCreateCache(cx);
     if (!cache)
         return false;
 
     if (RegExpPrivateCache::Ptr p = cache->lookup(atom)) {
-        NeedsIncRef<RegExpPrivate> cached(p->value);
-        if (cached->getFlags() == flags) {
+        RegExpPrivateCacheValue &cacheValue = p->value;
+        if (cacheValue.kind() == targetKind && cacheValue.rep()->getFlags() == flags) {
+            NeedsIncRef<RegExpPrivate> cached(cacheValue.rep());
             cached->incref(cx);
             *result = AlreadyIncRefed<RegExpPrivate>(cached.get());
             return true;
         }
     }
 
     JS_ASSERT(result->null());
     return true;
 }
 
 inline bool
-detail::RegExpPrivate::cacheInsert(JSContext *cx, JSAtom *atom, RegExpPrivate *priv)
+detail::RegExpPrivate::cacheInsert(JSContext *cx, JSAtom *atom, RegExpPrivateCacheKind kind,
+                                   RegExpPrivate *priv)
 {
     JS_ASSERT(priv);
 
     /*
      * Note: allocation performed since lookup may cause a garbage collection,
      * so we have to re-lookup the cache (and inside the cache) after the
      * allocation is performed.
      */
     RegExpPrivateCache *cache = getOrCreateCache(cx);
     if (!cache)
         return false;
 
     if (RegExpPrivateCache::AddPtr addPtr = cache->lookupForAdd(atom)) {
-        /* We clobber existing entries with the same source (but different flags). */
-        JS_ASSERT(addPtr->value->getFlags() != priv->getFlags());
-        addPtr->value = priv;
+        /* We clobber existing entries with the same source (but different flags or kind). */
+        JS_ASSERT(addPtr->value.rep()->getFlags() != priv->getFlags() ||
+                  addPtr->value.kind() != kind);
+        addPtr->value.reset(priv, kind);
     } else {
-        if (!cache->add(addPtr, atom, priv)) {
+        if (!cache->add(addPtr, atom, RegExpPrivateCacheValue(priv, kind))) {
             js_ReportOutOfMemory(cx);
             return false;
         }
     }
 
     return true;
 }
 
@@ -324,27 +345,27 @@ detail::RegExpPrivate::create(JSContext 
      * keep a refcount. The cache holds a "weak ref", where the
      * |RegExpPrivate|'s deallocation decref will first cause it to
      * remove itself from the cache.
      */
 
     JSAtom *sourceAtom = &source->asAtom();
 
     AlreadyIncRefed<RegExpPrivate> cached;
-    if (!cacheLookup(cx, sourceAtom, flags, &cached))
+    if (!cacheLookup(cx, sourceAtom, flags, RegExpPrivateCache_ExecCapable, &cached))
         return RetType(NULL);
 
     if (cached)
         return cached;
 
     RegExpPrivate *priv = RegExpPrivate::createUncached(cx, source, flags, ts);
     if (!priv)
         return RetType(NULL);
 
-    if (!cacheInsert(cx, sourceAtom, priv))
+    if (!cacheInsert(cx, sourceAtom, RegExpPrivateCache_ExecCapable, priv))
         return RetType(NULL);
 
     return RetType(priv);
 }
 
 /* This function should be deleted once bad Android platforms phase out. See bug 604774. */
 inline bool
 detail::RegExpPrivateCode::isJITRuntimeEnabled(JSContext *cx)
@@ -486,17 +507,17 @@ detail::RegExpPrivate::decref(JSContext 
 #endif
 
     if (--refCount != 0)
         return;
 
     RegExpPrivateCache *cache;
     if (source->isAtom() && (cache = cx->threadData()->getRegExpPrivateCache())) {
         RegExpPrivateCache::Ptr ptr = cache->lookup(&source->asAtom());
-        if (ptr && ptr->value == this)
+        if (ptr && ptr->value.rep() == this)
             cache->remove(ptr);
     }
 
 #ifdef DEBUG
     this->~RegExpPrivate();
     memset(this, 0xcd, sizeof(*this));
     cx->free_(this);
 #else
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -52,16 +52,44 @@ using namespace js;
 using js::detail::RegExpPrivate;
 using js::detail::RegExpPrivateCode;
 
 JS_STATIC_ASSERT(IgnoreCaseFlag == JSREG_FOLD);
 JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB);
 JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE);
 JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY);
 
+/* RegExpMatcher */
+
+bool
+RegExpMatcher::resetWithTestOptimized(RegExpObject *reobj)
+{
+    JS_ASSERT(reobj->startsWithAtomizedGreedyStar());
+
+    JSAtom *source = &reobj->getSource()->asAtom();
+    AlreadyIncRefed<RegExpPrivate> priv =
+        RegExpPrivate::createTestOptimized(cx, source, reobj->getFlags());
+    if (!priv)
+        return false;
+
+    /*
+     * Create a dummy RegExpObject to persist this RegExpPrivate until the next GC.
+     * Note that we give the ref we have to this new object.
+     */
+    RegExpObjectBuilder builder(cx);
+    RegExpObject *dummy = builder.build(priv);
+    if (!dummy) {
+        priv->decref(cx);
+        return false;
+    }
+
+    arc.reset(NeedsIncRef<RegExpPrivate>(priv.get()));
+    return true;
+}
+
 /* RegExpObjectBuilder */
 
 bool
 RegExpObjectBuilder::getOrCreate()
 {
     if (reobj_)
         return true;
 
@@ -463,33 +491,65 @@ js::ParseRegExpFlags(JSContext *cx, JSSt
             return false;
           }
         }
 #undef HANDLE_FLAG
     }
     return true;
 }
 
-/* static */ RegExpPrivate *
+RegExpPrivate *
 RegExpPrivate::createUncached(JSContext *cx, JSLinearString *source, RegExpFlag flags,
                               TokenStream *tokenStream)
 {
     RegExpPrivate *priv = cx->new_<RegExpPrivate>(source, flags);
     if (!priv)
         return NULL;
 
     if (!priv->compile(cx, tokenStream)) {
         Foreground::delete_(priv);
         return NULL;
     }
 
     return priv;
 }
 
 AlreadyIncRefed<RegExpPrivate>
+RegExpPrivate::createTestOptimized(JSContext *cx, JSAtom *cacheKey, RegExpFlag flags)
+{
+    typedef AlreadyIncRefed<RegExpPrivate> RetType;
+
+    RetType cached;
+    if (!cacheLookup(cx, cacheKey, flags, RegExpPrivateCache_TestOptimized, &cached))
+        return RetType(NULL);
+
+    if (cached)
+        return cached;
+
+    /* Strip off the greedy star characters, create a new RegExpPrivate, and cache. */
+    JS_ASSERT(cacheKey->length() > JS_ARRAY_LENGTH(GreedyStarChars));
+    JSDependentString *stripped =
+      JSDependentString::new_(cx, cacheKey, cacheKey->chars() + JS_ARRAY_LENGTH(GreedyStarChars),
+                              cacheKey->length() - JS_ARRAY_LENGTH(GreedyStarChars));
+    if (!stripped)
+        return RetType(NULL);
+
+    RegExpPrivate *priv = createUncached(cx, cacheKey, flags, NULL);
+    if (!priv)
+        return RetType(NULL);
+
+    if (!cacheInsert(cx, cacheKey, RegExpPrivateCache_TestOptimized, priv)) {
+        priv->decref(cx);
+        return RetType(NULL);
+    }
+
+    return RetType(priv);
+}
+
+AlreadyIncRefed<RegExpPrivate>
 RegExpPrivate::create(JSContext *cx, JSLinearString *str, JSString *opt, TokenStream *ts)
 {
     if (!opt)
         return create(cx, str, RegExpFlag(0), ts);
 
     RegExpFlag flags = RegExpFlag(0);
     if (!ParseRegExpFlags(cx, opt, &flags))
         return AlreadyIncRefed<RegExpPrivate>(NULL);
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -129,16 +129,18 @@ class RegExpObject : public ::JSObject
         uintN flags = 0;
         flags |= global() ? GlobalFlag : 0;
         flags |= ignoreCase() ? IgnoreCaseFlag : 0;
         flags |= multiline() ? MultilineFlag : 0;
         flags |= sticky() ? StickyFlag : 0;
         return RegExpFlag(flags);
     }
 
+    inline bool startsWithAtomizedGreedyStar() const;
+
     /* JIT only. */
 
     inline size_t *addressOfPrivateRefCount() const;
 
     /* Flags. */
 
     inline void setIgnoreCase(bool enabled);
     inline void setGlobal(bool enabled);
@@ -210,32 +212,36 @@ class RegExpObjectBuilder
     JSContext       *cx;
     RegExpObject    *reobj_;
 
     bool getOrCreate();
     bool getOrCreateClone(RegExpObject *proto);
 
     RegExpObject *build(AlreadyIncRefed<RegExpPrivate> rep);
 
+    friend class RegExpMatcher;
+
   public:
     RegExpObjectBuilder(JSContext *cx, RegExpObject *reobj = NULL)
       : cx(cx), reobj_(reobj)
     { }
 
     RegExpObject *reobj() { return reobj_; }
 
     RegExpObject *build(JSLinearString *str, RegExpFlag flags);
     RegExpObject *build(RegExpObject *other);
 
     /* Perform a VM-internal clone. */
     RegExpObject *clone(RegExpObject *other, RegExpObject *proto);
 };
 
 namespace detail {
 
+static const jschar GreedyStarChars[] = {'.', '*'};
+
 /* Abstracts away the gross |RegExpPrivate| backend details. */
 class RegExpPrivateCode
 {
 #if ENABLE_YARR_JIT
     typedef JSC::Yarr::BytecodePattern BytecodePattern;
     typedef JSC::Yarr::ErrorCode ErrorCode;
     typedef JSC::Yarr::JSGlobalData JSGlobalData;
     typedef JSC::Yarr::YarrCodeBlock YarrCodeBlock;
@@ -301,16 +307,54 @@ class RegExpPrivateCode
     inline bool compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts, uintN *parenCount,
                         RegExpFlag flags);
 
 
     inline RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
                                    int *output, size_t outputCount);
 };
 
+enum RegExpPrivateCacheKind
+{
+    RegExpPrivateCache_TestOptimized,
+    RegExpPrivateCache_ExecCapable
+};
+
+class RegExpPrivateCacheValue
+{
+    union {
+        RegExpPrivate   *rep_;
+        uintptr_t       bits;
+    };
+
+  public:
+    RegExpPrivateCacheValue() : rep_(NULL) {}
+
+    RegExpPrivateCacheValue(RegExpPrivate *rep, RegExpPrivateCacheKind kind) {
+        reset(rep, kind);
+    }
+
+    RegExpPrivateCacheKind kind() const {
+        return (bits & 0x1)
+                 ? RegExpPrivateCache_TestOptimized
+                 : RegExpPrivateCache_ExecCapable;
+    }
+
+    RegExpPrivate *rep() {
+        return reinterpret_cast<RegExpPrivate *>(bits & ~uintptr_t(1));
+    }
+
+    void reset(RegExpPrivate *rep, RegExpPrivateCacheKind kind) {
+        rep_ = rep;
+        if (kind == RegExpPrivateCache_TestOptimized)
+            bits |= 0x1;
+        JS_ASSERT(this->kind() == kind);
+    }
+};
+
 /*
  * The "meat" of the builtin regular expression objects: it contains the
  * mini-program that represents the source of the regular expression.
  * Excepting refcounts, this is an immutable datastructure after
  * compilation.
  *
  * Non-atomic refcounting is used, so single-thread invariants must be
  * maintained. |RegExpPrivate|s are currently shared within a single
@@ -339,26 +383,30 @@ class RegExpPrivate
     static inline void checkMatchPairs(JSString *input, int *buf, size_t matchItemCount);
 
     static RegExpPrivate *
     createUncached(JSContext *cx, JSLinearString *source, RegExpFlag flags,
                    TokenStream *tokenStream);
 
     static RegExpPrivateCache *getOrCreateCache(JSContext *cx);
     static bool cacheLookup(JSContext *cx, JSAtom *atom, RegExpFlag flags,
-                            AlreadyIncRefed<RegExpPrivate> *result);
-    static bool cacheInsert(JSContext *cx, JSAtom *atom, RegExpPrivate *priv);
+                            RegExpPrivateCacheKind kind, AlreadyIncRefed<RegExpPrivate> *result);
+    static bool cacheInsert(JSContext *cx, JSAtom *atom,
+                            RegExpPrivateCacheKind kind, RegExpPrivate *priv);
 
   public:
     static AlreadyIncRefed<RegExpPrivate>
     create(JSContext *cx, JSLinearString *source, RegExpFlag flags, TokenStream *ts);
 
     static AlreadyIncRefed<RegExpPrivate>
     create(JSContext *cx, JSLinearString *source, JSString *flags, TokenStream *ts);
 
+    static AlreadyIncRefed<RegExpPrivate>
+    createTestOptimized(JSContext *cx, JSAtom *originalSource, RegExpFlag flags);
+
     RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
                             LifoAllocScope &allocScope, MatchPairs **output);
 
     /* Mutators */
 
     void incref(JSContext *cx);
     void decref(JSContext *cx);
 
@@ -417,16 +465,18 @@ class RegExpMatcher
         if (!priv)
             return false;
         arc.reset(NeedsIncRef<RegExpPrivate>(priv));
         return true;
     }
 
     inline bool reset(JSLinearString *patstr, JSString *opt);
 
+    bool resetWithTestOptimized(RegExpObject *reobj);
+
     RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
                             LifoAllocScope &allocScope, MatchPairs **output) {
         JS_ASSERT(!arc.null());
         return arc.get()->execute(cx, chars, length, lastIndex, allocScope, output);
     }
 };
 
 /*
--- a/js/src/yarr/YarrParser.h
+++ b/js/src/yarr/YarrParser.h
@@ -517,16 +517,21 @@ private:
      *
      * Helper for parseTokens(); checks for parse errors and non-greedy quantifiers.
      */
     void parseQuantifier(bool lastTokenWasAnAtom, unsigned min, unsigned max)
     {
         ASSERT(!m_err);
         ASSERT(min <= max);
 
+        if (min == unsigned(-1)) {
+            m_err = QuantifierTooLarge;
+            return;
+        }
+
         if (lastTokenWasAnAtom)
             m_delegate.quantifyAtom(min, max, !tryConsume('?'));
         else
             m_err = QuantifierWithoutAtom;
     }
 
     /*
      * parseTokens():
--- a/js/src/yarr/YarrPattern.cpp
+++ b/js/src/yarr/YarrPattern.cpp
@@ -511,17 +511,17 @@ public:
         }
     }
 
     void disjunction()
     {
         m_alternative = m_alternative->m_parent->addNewAlternative();
     }
 
-    unsigned setupAlternativeOffsets(PatternAlternative* alternative, unsigned currentCallFrameSize, unsigned initialInputPosition)
+    ErrorCode setupAlternativeOffsets(PatternAlternative* alternative, unsigned currentCallFrameSize, unsigned initialInputPosition, unsigned *callFrameSizeOut)
     {
         alternative->m_hasFixedSize = true;
         unsigned currentInputPosition = initialInputPosition;
 
         for (unsigned i = 0; i < alternative->m_terms.size(); ++i) {
             PatternTerm& term = alternative->m_terms[i];
 
             switch (term.type) {
@@ -562,75 +562,87 @@ public:
                 break;
 
             case PatternTerm::TypeParenthesesSubpattern:
                 // Note: for fixed once parentheses we will ensure at least the minimum is available; others are on their own.
                 term.frameLocation = currentCallFrameSize;
                 if (term.quantityCount == 1 && !term.parentheses.isCopy) {
                     if (term.quantityType != QuantifierFixedCount)
                         currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesOnce;
-                    currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition);
+                    if (ErrorCode error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition, &currentCallFrameSize))
+                        return error;
                     // If quantity is fixed, then pre-check its minimum size.
                     if (term.quantityType == QuantifierFixedCount)
                         currentInputPosition += term.parentheses.disjunction->m_minimumSize;
                     term.inputPosition = currentInputPosition;
                 } else if (term.parentheses.isTerminal) {
                     currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesTerminal;
-                    currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition);
+                    if (ErrorCode error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition, &currentCallFrameSize))
+                        return error;
                     term.inputPosition = currentInputPosition;
                 } else {
                     term.inputPosition = currentInputPosition;
-                    setupDisjunctionOffsets(term.parentheses.disjunction, BASE_FRAME_SIZE, currentInputPosition);
+                    unsigned dummy;
+                    if (ErrorCode error = setupDisjunctionOffsets(term.parentheses.disjunction, BASE_FRAME_SIZE, currentInputPosition, &dummy))
+                        return error;
                     currentCallFrameSize += YarrStackSpaceForBackTrackInfoParentheses;
                 }
                 // Fixed count of 1 could be accepted, if they have a fixed size *AND* if all alternatives are of the same length.
                 alternative->m_hasFixedSize = false;
                 break;
 
             case PatternTerm::TypeParentheticalAssertion:
                 term.inputPosition = currentInputPosition;
                 term.frameLocation = currentCallFrameSize;
-                currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize + YarrStackSpaceForBackTrackInfoParentheticalAssertion, currentInputPosition);
+                if (ErrorCode error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize + YarrStackSpaceForBackTrackInfoParentheticalAssertion, currentInputPosition, &currentCallFrameSize))
+                    return error;
                 break;
             }
         }
 
         alternative->m_minimumSize = currentInputPosition - initialInputPosition;
-        return currentCallFrameSize;
+        *callFrameSizeOut = currentCallFrameSize;
+        return NoError;
     }
 
-    unsigned setupDisjunctionOffsets(PatternDisjunction* disjunction, unsigned initialCallFrameSize, unsigned initialInputPosition)
+    ErrorCode setupDisjunctionOffsets(PatternDisjunction* disjunction, unsigned initialCallFrameSize, unsigned initialInputPosition, unsigned *maximumCallFrameSizeOut)
     {
         if ((disjunction != m_pattern.m_body) && (disjunction->m_alternatives.size() > 1))
             initialCallFrameSize += YarrStackSpaceForBackTrackInfoAlternative;
 
         unsigned minimumInputSize = UINT_MAX;
         unsigned maximumCallFrameSize = 0;
         bool hasFixedSize = true;
 
         for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) {
             PatternAlternative* alternative = disjunction->m_alternatives[alt];
-            unsigned currentAlternativeCallFrameSize = setupAlternativeOffsets(alternative, initialCallFrameSize, initialInputPosition);
+            unsigned currentAlternativeCallFrameSize;
+            if (ErrorCode error = setupAlternativeOffsets(alternative, initialCallFrameSize, initialInputPosition, &currentAlternativeCallFrameSize))
+                return error;
             minimumInputSize = std::min(minimumInputSize, alternative->m_minimumSize);
             maximumCallFrameSize = std::max(maximumCallFrameSize, currentAlternativeCallFrameSize);
             hasFixedSize &= alternative->m_hasFixedSize;
         }
         
-        ASSERT(minimumInputSize != UINT_MAX);
+        if (minimumInputSize == UINT_MAX)
+            return PatternTooLarge;
+
         ASSERT(maximumCallFrameSize >= initialCallFrameSize);
 
         disjunction->m_hasFixedSize = hasFixedSize;
         disjunction->m_minimumSize = minimumInputSize;
         disjunction->m_callFrameSize = maximumCallFrameSize;
-        return maximumCallFrameSize;
+        *maximumCallFrameSizeOut = maximumCallFrameSize;
+        return NoError;
     }
 
-    void setupOffsets()
+    ErrorCode setupOffsets()
     {
-        setupDisjunctionOffsets(m_pattern.m_body, BASE_FRAME_SIZE, 0);
+        unsigned dummy;
+        return setupDisjunctionOffsets(m_pattern.m_body, BASE_FRAME_SIZE, 0, &dummy);
     }
 
     // This optimization identifies sets of parentheses that we will never need to backtrack.
     // In these cases we do not need to store state from prior iterations.
     // We can presently avoid backtracking for:
     //   * where the parens are at the end of the regular expression (last term in any of the
     //     alternatives of the main body disjunction).
     //   * where the parens are non-capturing, and quantified unbounded greedy (*).
@@ -713,17 +725,18 @@ ErrorCode YarrPattern::compile(const USt
 
         ASSERT(!error);
         ASSERT(numSubpatterns == m_numSubpatterns);
     }
 
     constructor.checkForTerminalParentheses();
     constructor.optimizeBOL();
         
-    constructor.setupOffsets();
+    if (ErrorCode error = constructor.setupOffsets())
+        return error;
 
     return NoError;
 }
 
 YarrPattern::YarrPattern(const UString& pattern, bool ignoreCase, bool multiline, ErrorCode* error)
     : m_ignoreCase(ignoreCase)
     , m_multiline(multiline)
     , m_containsBackreferences(false)
--- a/js/xpconnect/src/dom_quickstubs.qsconf
+++ b/js/xpconnect/src/dom_quickstubs.qsconf
@@ -225,16 +225,17 @@ members = [
     'nsIDOMHTMLDocument.forms',
     'nsIDOMHTMLDocument.cookie',
     'nsIDOMHTMLDocument.images',
     'nsIDOMHTMLDocument.write',
     'nsIDOMHTMLDocument.writeln',
     'nsIDOMHTMLDocument.domain',
     'nsIDOMHTMLDocument.getSelection',
     'nsIDOMHTMLDocument.designMode',
+    'nsIDOMHTMLDocument.head',
     'nsIDOMHTMLElement.*',
     'nsIDOMHTMLFormElement.elements',
     'nsIDOMHTMLFormElement.name',
     'nsIDOMHTMLFormElement.submit',
     'nsIDOMHTMLFormElement.length',
     'nsIDOMHTMLFormElement.target',
     'nsIDOMHTMLFormElement.action',
     'nsIDOMHTMLFrameElement.src',
@@ -645,17 +646,23 @@ customMethodCalls = {
         'code': nsIDOMHTMLDocument_Write_customMethodCallCode % 'Write'
         },
     'nsIDOMHTMLDocument_Writeln': {
         'thisType': 'nsHTMLDocument',
         'code': nsIDOMHTMLDocument_Write_customMethodCallCode % 'Writeln'
         },
     'nsIDOMHTMLDocument_GetBody': {
         'thisType': 'nsHTMLDocument',
-        'code': '    nsIContent *result = self->GetBody(&rv);'
+        'code': '    nsIContent *result = self->GetBody();',
+        'canFail': False
+        },
+    'nsIDOMHTMLDocument_GetHead': {
+        'thisType': 'nsHTMLDocument',
+        'code': '    nsIContent *result = self->GetHead();',
+        'canFail': False
         },
     'nsIDOMHTMLDocument_GetElementsByName': {
         'thisType': 'nsHTMLDocument',
         'code': '    nsRefPtr<nsContentList> result = '
                 'self->GetElementsByName(arg0);',
         'canFail': False
         },
     'nsIDOMStorage_Clear': {
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -2214,16 +2214,21 @@ DocumentViewerImpl::CreateStyleSet(nsIDo
     }
   }
 
   sheet = nsLayoutStylesheetCache::FormsSheet();
   if (sheet) {
     styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet);
   }
 
+  sheet = nsLayoutStylesheetCache::FullScreenOverrideSheet();
+  if (sheet) {
+    styleSet->PrependStyleSheet(nsStyleSet::eOverrideSheet, sheet);
+  }
+
   // Make sure to clone the quirk sheet so that it can be usefully
   // enabled/disabled as needed.
   nsRefPtr<nsCSSStyleSheet> quirkClone;
   nsCSSStyleSheet* quirkSheet;
   if (!nsLayoutStylesheetCache::UASheet() ||
       !(quirkSheet = nsLayoutStylesheetCache::QuirkSheet()) ||
       !(quirkClone = quirkSheet->Clone(nsnull, nsnull, nsnull, nsnull)) ||
       !sheet) {
new file mode 100644
--- /dev/null
+++ b/layout/style/full-screen-override.css
@@ -0,0 +1,56 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Pearce <chris@pearce.org.nz>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+*|*:-moz-full-screen-ancestor {
+  /* Ancestors of a full-screen element should not induce stacking contexts
+     that would prevent the full-screen element from being on top. */
+  z-index: auto;
+  /* Ancestors of a full-screen element should not be partially transparent,
+     since that would apply to the full-screen element and make the page visible
+     behind it. It would also create a pseudo-stacking-context that would let content
+     draw on top of the full-screen element. */
+  opacity: 1;
+  /* Ancestors of a full-screen element should not apply SVG masking, clipping, or
+     filtering, since that would affect the full-screen element and create a pseudo-
+     stacking context. */
+  mask: none;
+  clip: auto;
+  filter: none;
+  -moz-transform: none;
+}
+
--- a/layout/style/jar.mn
+++ b/layout/style/jar.mn
@@ -1,10 +1,11 @@
 toolkit.jar:
 *  res/ua.css    (ua.css)
    res/html.css    (html.css)
    res/quirk.css    (quirk.css)
+   res/full-screen-override.css    (full-screen-override.css)
    res/viewsource.css    (viewsource.css)
 *  res/forms.css    (forms.css)
    res/arrow.gif    (arrow.gif)
    res/arrowd.gif   (arrowd.gif)
 
 % resource gre-resources %res/
--- a/layout/style/nsLayoutStylesheetCache.cpp
+++ b/layout/style/nsLayoutStylesheetCache.cpp
@@ -150,16 +150,26 @@ nsLayoutStylesheetCache::QuirkSheet()
 {
   EnsureGlobal();
   if (!gStyleCache)
     return nsnull;
 
   return gStyleCache->mQuirkSheet;
 }
 
+nsCSSStyleSheet*
+nsLayoutStylesheetCache::FullScreenOverrideSheet()
+{
+  EnsureGlobal();
+  if (!gStyleCache)
+    return nsnull;
+
+  return gStyleCache->mFullScreenOverrideSheet;
+}
+
 void
 nsLayoutStylesheetCache::Shutdown()
 {
   NS_IF_RELEASE(gCSSLoader);
   NS_IF_RELEASE(gStyleCache);
 }
 
 nsLayoutStylesheetCache::nsLayoutStylesheetCache()
@@ -186,16 +196,23 @@ nsLayoutStylesheetCache::nsLayoutStylesh
   }
   NS_ASSERTION(mUASheet, "Could not load ua.css");
 
   NS_NewURI(getter_AddRefs(uri), "resource://gre-resources/quirk.css");
   if (uri) {
     LoadSheet(uri, mQuirkSheet, true);
   }
   NS_ASSERTION(mQuirkSheet, "Could not load quirk.css");
+
+  NS_NewURI(getter_AddRefs(uri), "resource://gre-resources/full-screen-override.css");
+  if (uri) {
+    LoadSheet(uri, mFullScreenOverrideSheet, true);
+  }
+  NS_ASSERTION(mFullScreenOverrideSheet, "Could not load full-screen-override.css");
+
 }
 
 void
 nsLayoutStylesheetCache::EnsureGlobal()
 {
   if (gStyleCache) return;
 
   gStyleCache = new nsLayoutStylesheetCache();
--- a/layout/style/nsLayoutStylesheetCache.h
+++ b/layout/style/nsLayoutStylesheetCache.h
@@ -59,16 +59,17 @@ class nsLayoutStylesheetCache
   NS_DECL_NSIOBSERVER
 
   static nsCSSStyleSheet* ScrollbarsSheet();
   static nsCSSStyleSheet* FormsSheet();
   static nsCSSStyleSheet* UserContentSheet();
   static nsCSSStyleSheet* UserChromeSheet();
   static nsCSSStyleSheet* UASheet();
   static nsCSSStyleSheet* QuirkSheet();
+  static nsCSSStyleSheet* FullScreenOverrideSheet();
 
   static void Shutdown();
 
 private:
   nsLayoutStylesheetCache();
   ~nsLayoutStylesheetCache() {}
 
   static void EnsureGlobal();
@@ -80,11 +81,12 @@ private:
   static nsLayoutStylesheetCache* gStyleCache;
   static mozilla::css::Loader* gCSSLoader;
   nsRefPtr<nsCSSStyleSheet> mScrollbarsSheet;
   nsRefPtr<nsCSSStyleSheet> mFormsSheet;
   nsRefPtr<nsCSSStyleSheet> mUserContentSheet;
   nsRefPtr<nsCSSStyleSheet> mUserChromeSheet;
   nsRefPtr<nsCSSStyleSheet> mUASheet;
   nsRefPtr<nsCSSStyleSheet> mQuirkSheet;
+  nsRefPtr<nsCSSStyleSheet> mFullScreenOverrideSheet;
 };
 
 #endif
--- a/layout/style/ua.css
+++ b/layout/style/ua.css
@@ -255,33 +255,16 @@
 
 /* If there is a full-screen element that is not the root then
    we should hide the viewport scrollbar. We exclude the chrome
    document to prevent reframing of contained plugins. */
 :not(xul|*):root:-moz-full-screen-ancestor {
   overflow: hidden !important;
 }
 
-*|*:-moz-full-screen-ancestor {
-  /* Ancestors of a full-screen element should not induce stacking contexts
-     that would prevent the full-screen element from being on top. */
-  z-index:auto !important;
-  /* Ancestors of a full-screen element should not be partially transparent,
-     since that would apply to the full-screen element and make the page visible
-     behind it. It would also create a pseudo-stacking-context that would let content
-     draw on top of the full-screen element. */
-  opacity:1 !important;
-  /* Ancestors of a full-screen element should not apply SVG masking, clipping, or
-     filtering, since that would affect the full-screen element and create a pseudo-
-     stacking context. */
-  mask:none !important;
-  clip:none !important;
-  filter:none !important;
-}
-
 /* XML parse error reporting */
 
 parsererror|parsererror {
   display: block;
   font-family: sans-serif;
   font-weight: bold;
   white-space: pre;
   margin: 1em;
--- a/memory/jemalloc/jemalloc.c
+++ b/memory/jemalloc/jemalloc.c
@@ -1079,16 +1079,62 @@ struct arena_s {
  * Data.
  */
 
 #ifndef MOZ_MEMORY_NARENAS_DEFAULT_ONE
 /* Number of CPUs. */
 static unsigned		ncpus;
 #endif
 
+/*
+ * When MALLOC_STATIC_SIZES is defined most of the parameters
+ * controlling the malloc behavior are defined as compile-time constants
+ * for best performance and cannot be altered at runtime.
+ */
+#define MALLOC_STATIC_SIZES 1
+
+#ifdef MALLOC_STATIC_SIZES
+
+/*
+ * VM page size. It must divide the runtime CPU page size or the code
+ * will abort.
+ */
+#define pagesize_2pow			((size_t) 12)
+#define pagesize			((size_t) 1 << pagesize_2pow)
+#define pagesize_mask			(pagesize - 1)
+
+/* Various quantum-related settings. */
+
+#define QUANTUM_DEFAULT 		((size_t) 1 << QUANTUM_2POW_MIN)
+static const size_t	quantum	=	QUANTUM_DEFAULT;
+static const size_t	quantum_mask =	QUANTUM_DEFAULT - 1;
+
+/* Various bin-related settings. */
+
+static const size_t	small_min =	(QUANTUM_DEFAULT >> 1) + 1;
+static const size_t	small_max =	(size_t) SMALL_MAX_DEFAULT;
+
+/* Max size class for bins. */
+static const size_t	bin_maxclass =	pagesize >> 1;
+
+ /* Number of (2^n)-spaced tiny bins. */
+static const unsigned	ntbins =	(unsigned)
+					(QUANTUM_2POW_MIN - TINY_MIN_2POW);
+
+ /* Number of quantum-spaced bins. */
+static const unsigned	nqbins =	(unsigned)
+					(SMALL_MAX_DEFAULT >> QUANTUM_2POW_MIN);
+
+/* Number of (2^n)-spaced sub-page bins. */
+static const unsigned	nsbins =	(unsigned)
+					(pagesize_2pow -
+					 SMALL_MAX_2POW_DEFAULT - 1);
+
+#else /* !MALLOC_STATIC_SIZES */
+
 /* VM page size. */
 static size_t		pagesize;
 static size_t		pagesize_mask;
 static size_t		pagesize_2pow;
 
 /* Various bin-related settings. */
 static size_t		bin_maxclass; /* Max size class for bins. */
 static unsigned		ntbins; /* Number of (2^n)-spaced tiny bins. */
@@ -1096,22 +1142,51 @@ static unsigned		nqbins; /* Number of qu
 static unsigned		nsbins; /* Number of (2^n)-spaced sub-page bins. */
 static size_t		small_min;
 static size_t		small_max;
 
 /* Various quantum-related settings. */
 static size_t		quantum;
 static size_t		quantum_mask; /* (quantum - 1). */
 
+#endif
+
 /* Various chunk-related settings. */
+
+/*
+ * Compute the header size such that it is large enough to contain the page map
+ * and enough nodes for the worst case: one node per non-header page plus one
+ * extra for situations where we briefly have one more node allocated than we
+ * will need.
+ */
+#define calculate_arena_header_size()					\
+	(sizeof(arena_chunk_t) + sizeof(arena_chunk_map_t) * (chunk_npages - 1))
+
+#define calculate_arena_header_pages()					\
+	((calculate_arena_header_size() >> pagesize_2pow) +		\
+	 ((calculate_arena_header_size() & pagesize_mask) ? 1 : 0))
+
+/* Max size class for arenas. */
+#define calculate_arena_maxclass()					\
+	(chunksize - (arena_chunk_header_npages << pagesize_2pow))
+
+#ifdef MALLOC_STATIC_SIZES
+#define CHUNKSIZE_DEFAULT		((size_t) 1 << CHUNK_2POW_DEFAULT)
+static const size_t	chunksize =	CHUNKSIZE_DEFAULT;
+static const size_t	chunksize_mask =CHUNKSIZE_DEFAULT - 1;
+static const size_t	chunk_npages =	CHUNKSIZE_DEFAULT >> pagesize_2pow;
+#define arena_chunk_header_npages	calculate_arena_header_pages()
+#define arena_maxclass			calculate_arena_maxclass()
+#else
 static size_t		chunksize;
 static size_t		chunksize_mask; /* (chunksize - 1). */
 static size_t		chunk_npages;
 static size_t		arena_chunk_header_npages;
 static size_t		arena_maxclass; /* Max size class for arenas. */
+#endif
 
 /********/
 /*
  * Chunks.
  */
 
 #ifdef MALLOC_VALIDATE
 static malloc_rtree_t *chunk_rtree;
@@ -1213,19 +1288,25 @@ static bool	opt_abort = false;
 static bool	opt_junk = false;
 #endif
 #endif
 static size_t	opt_dirty_max = DIRTY_MAX_DEFAULT;
 #ifdef MALLOC_BALANCE
 static uint64_t	opt_balance_threshold = BALANCE_THRESHOLD_DEFAULT;
 #endif
 static bool	opt_print_stats = false;
+#ifdef MALLOC_STATIC_SIZES
+#define opt_quantum_2pow	QUANTUM_2POW_MIN
+#define opt_small_max_2pow	SMALL_MAX_2POW_DEFAULT
+#define opt_chunk_2pow		CHUNK_2POW_DEFAULT
+#else
 static size_t	opt_quantum_2pow = QUANTUM_2POW_MIN;
 static size_t	opt_small_max_2pow = SMALL_MAX_2POW_DEFAULT;
 static size_t	opt_chunk_2pow = CHUNK_2POW_DEFAULT;
+#endif
 #ifdef MALLOC_PAGEFILE
 static bool	opt_pagefile = false;
 #endif
 #ifdef MALLOC_UTRACE
 static bool	opt_utrace = false;
 #endif
 #ifdef MALLOC_SYSV
 static bool	opt_sysv = false;
@@ -5554,41 +5635,44 @@ malloc_init_hard(void)
 	/* Get page size and number of CPUs */
 #ifdef MOZ_MEMORY_WINDOWS
 	{
 		SYSTEM_INFO info;
 
 		GetSystemInfo(&info);
 		result = info.dwPageSize;
 
-		pagesize = (unsigned) result;
-
 #ifndef MOZ_MEMORY_NARENAS_DEFAULT_ONE
 		ncpus = info.dwNumberOfProcessors;
 #endif
 	}
 #else
 #ifndef MOZ_MEMORY_NARENAS_DEFAULT_ONE
 	ncpus = malloc_ncpus();
 #endif
 
 	result = sysconf(_SC_PAGESIZE);
 	assert(result != -1);
-
-	pagesize = (unsigned) result;
-#endif
-
-	/*
-	 * We assume that pagesize is a power of 2 when calculating
-	 * pagesize_mask and pagesize_2pow.
-	 */
+#endif
+
+	/* We assume that the page size is a power of 2. */
 	assert(((result - 1) & result) == 0);
-	pagesize_mask = result - 1;
+#ifdef MALLOC_STATIC_SIZES
+	if (pagesize % (size_t) result) {
+		_malloc_message(_getprogname(),
+				"Compile-time page size does not divide the runtime one.\n",
+				"", "");
+		abort();
+	}
+#else	
+	pagesize = (size_t) result;
+	pagesize_mask = (size_t) result - 1;
 	pagesize_2pow = ffs((int)result) - 1;
-
+#endif
+	
 #ifdef MALLOC_PAGEFILE
 	/*
 	 * Determine where to create page files.  It is insufficient to
 	 * unconditionally use P_tmpdir (typically "/tmp"), since for some
 	 * operating systems /tmp is a separate filesystem that is rather small.
 	 * Therefore prefer, in order, the following locations:
 	 *
 	 * 1) MALLOC_TMPDIR
@@ -5727,30 +5811,32 @@ MALLOC_OUT:
 #ifdef MALLOC_FILL
 				case 'j':
 					opt_junk = false;
 					break;
 				case 'J':
 					opt_junk = true;
 					break;
 #endif
+#ifndef MALLOC_STATIC_SIZES
 				case 'k':
 					/*
 					 * Chunks always require at least one
 					 * header page, so chunks can never be
 					 * smaller than two pages.
 					 */
 					if (opt_chunk_2pow > pagesize_2pow + 1)
 						opt_chunk_2pow--;
 					break;
 				case 'K':
 					if (opt_chunk_2pow + 1 <
 					    (sizeof(size_t) << 3))
 						opt_chunk_2pow++;
 					break;
+#endif
 				case 'n':
 					opt_narenas_lshift--;
 					break;
 				case 'N':
 					opt_narenas_lshift++;
 					break;
 #ifdef MALLOC_PAGEFILE
 				case 'o':
@@ -5763,16 +5849,17 @@ MALLOC_OUT:
 					break;
 #endif
 				case 'p':
 					opt_print_stats = false;
 					break;
 				case 'P':
 					opt_print_stats = true;
 					break;
+#ifndef MALLOC_STATIC_SIZES
 				case 'q':
 					if (opt_quantum_2pow > QUANTUM_2POW_MIN)
 						opt_quantum_2pow--;
 					break;
 				case 'Q':
 					if (opt_quantum_2pow < pagesize_2pow -
 					    1)
 						opt_quantum_2pow++;
@@ -5782,16 +5869,17 @@ MALLOC_OUT:
 					    QUANTUM_2POW_MIN)
 						opt_small_max_2pow--;
 					break;
 				case 'S':
 					if (opt_small_max_2pow < pagesize_2pow
 					    - 1)
 						opt_small_max_2pow++;
 					break;
+#endif
 #ifdef MALLOC_UTRACE
 				case 'u':
 					opt_utrace = false;
 					break;
 				case 'U':
 					opt_utrace = true;
 					break;
 #endif
@@ -5844,16 +5932,17 @@ MALLOC_OUT:
 
 #if (!defined(MOZ_MEMORY_WINDOWS) && !defined(MOZ_MEMORY_DARWIN) && !defined(MOZ_MEMORY_ANDROID))
 	/* Prevent potential deadlock on malloc locks after fork. */
 	/* XXX on Android there is no pthread_atfork, so we specifically
 	   call _malloc_prefork and _malloc_postfork in process_util_linux.cc */
 	pthread_atfork(_malloc_prefork, _malloc_postfork, _malloc_postfork);
 #endif
 
+#ifndef MALLOC_STATIC_SIZES
 	/* Set variables according to the value of opt_small_max_2pow. */
 	if (opt_small_max_2pow < opt_quantum_2pow)
 		opt_small_max_2pow = opt_quantum_2pow;
 	small_max = (1U << opt_small_max_2pow);
 
 	/* Set bin-related variables. */
 	bin_maxclass = (pagesize >> 1);
 	assert(opt_quantum_2pow >= TINY_MIN_2POW);
@@ -5870,33 +5959,20 @@ MALLOC_OUT:
 	else
 		small_min = 1;
 	assert(small_min <= quantum);
 
 	/* Set variables according to the value of opt_chunk_2pow. */
 	chunksize = (1LU << opt_chunk_2pow);
 	chunksize_mask = chunksize - 1;
 	chunk_npages = (chunksize >> pagesize_2pow);
-	{
-		size_t header_size;
-
-		/*
-		 * Compute the header size such that it is large
-		 * enough to contain the page map and enough nodes for the
-		 * worst case: one node per non-header page plus one extra for
-		 * situations where we briefly have one more node allocated
-		 * than we will need.
-		 */
-		header_size = sizeof(arena_chunk_t) +
-		    (sizeof(arena_chunk_map_t) * (chunk_npages - 1));
-		arena_chunk_header_npages = (header_size >> pagesize_2pow) +
-		    ((header_size & pagesize_mask) != 0);
-	}
-	arena_maxclass = chunksize - (arena_chunk_header_npages <<
-	    pagesize_2pow);
+
+	arena_chunk_header_npages = calculate_arena_header_pages();
+	arena_maxclass = calculate_arena_maxclass();
+#endif
 
 #ifdef JEMALLOC_USES_MAP_ALIGN
 	/*
 	 * When using MAP_ALIGN, the alignment parameter must be a power of two
 	 * multiple of the system pagesize, or mmap will fail.
 	 */
 	assert((chunksize % pagesize) == 0);
 	assert((1 << (ffs(chunksize / pagesize) - 1)) == (chunksize/pagesize));
--- a/mobile/xul/installer/package-manifest.in
+++ b/mobile/xul/installer/package-manifest.in
@@ -273,17 +273,16 @@
 @BINPATH@/components/xpcom_threads.xpt
 @BINPATH@/components/xpcom_xpti.xpt
 @BINPATH@/components/xpconnect.xpt
 @BINPATH@/components/xulapp.xpt
 @BINPATH@/components/xul.xpt
 @BINPATH@/components/xuldoc.xpt
 @BINPATH@/components/xultmpl.xpt
 @BINPATH@/components/zipwriter.xpt
-@BINPATH@/components/openwebapps.xpt
 
 ; JavaScript components
 @BINPATH@/components/ConsoleAPI.manifest
 @BINPATH@/components/ConsoleAPI.js
 @BINPATH@/components/FeedProcessor.manifest
 @BINPATH@/components/FeedProcessor.js
 @BINPATH@/components/BrowserFeeds.manifest
 @BINPATH@/components/FeedConverter.js
@@ -327,17 +326,16 @@
 @BINPATH@/components/GPSDGeolocationProvider.js
 @BINPATH@/components/nsSidebar.manifest
 @BINPATH@/components/nsSidebar.js
 @BINPATH@/components/extensions.manifest
 @BINPATH@/components/addonManager.js
 @BINPATH@/components/amContentHandler.js
 @BINPATH@/components/amWebInstallListener.js
 @BINPATH@/components/nsBlocklistService.js
-@BINPATH@/components/OpenWebapps.manifest
 
 #ifdef MOZ_UPDATER
 @BINPATH@/components/nsUpdateService.manifest
 @BINPATH@/components/nsUpdateService.js
 @BINPATH@/components/nsUpdateServiceStub.js
 #endif
 @BINPATH@/components/nsUpdateTimerManager.manifest
 @BINPATH@/components/nsUpdateTimerManager.js
@@ -603,17 +601,16 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
 @BINPATH@/components/HelperAppDialog.js
 @BINPATH@/components/LoginManager.js
 @BINPATH@/components/LoginManagerPrompter.js
 @BINPATH@/components/MobileComponents.manifest
 @BINPATH@/components/MobileComponents.xpt
 @BINPATH@/components/PromptService.js
 @BINPATH@/components/SessionStore.js
 @BINPATH@/components/Sidebar.js
-@BINPATH@/components/OpenWebapps.js
 #ifdef MOZ_SAFE_BROWSING
 @BINPATH@/components/SafeBrowsing.js
 #endif
 #ifdef MOZ_UPDATER
 @BINPATH@/components/UpdatePrompt.js
 #endif
 @BINPATH@/components/XPIDialogService.js
 @BINPATH@/components/CapturePicker.js
--- a/netwerk/base/src/nsURLHelper.cpp
+++ b/netwerk/base/src/nsURLHelper.cpp
@@ -803,17 +803,17 @@ net_FindStringEnd(const nsCString& flatS
 static PRUint32
 net_FindMediaDelimiter(const nsCString& flatStr,
                        PRUint32 searchStart,
                        char delimiter)
 {
     do {
         // searchStart points to the spot from which we should start looking
         // for the delimiter.
-        const char delimStr[] = { delimiter, '"', '\'', '\0' };
+        const char delimStr[] = { delimiter, '"', '\0' };
         PRUint32 curDelimPos = flatStr.FindCharInSet(delimStr, searchStart);
         if (curDelimPos == PRUint32(kNotFound))
             return flatStr.Length();
             
         char ch = flatStr.CharAt(curDelimPos);
         if (ch == delimiter) {
             // Found delimiter
             return curDelimPos;
@@ -888,17 +888,17 @@ net_ParseMediaType(const nsACString &aMe
         } while (curParamStart < flatStr.Length());
     }
 
     if (typeHasCharset) {
         // Trim LWS leading and trailing whitespace from charset.  We include
         // '(' in the trailing trim set to catch media-type comments, which are
         // not at all standard, but may occur in rare cases.
         charset = net_FindCharNotInSet(charset, charsetEnd, HTTP_LWS);
-        if (*charset == '"' || *charset == '\'') {
+        if (*charset == '"') {
             charsetEnd =
                 start + net_FindStringEnd(flatStr, charset - start, *charset);
             charset++;
             NS_ASSERTION(charsetEnd >= charset, "Bad charset parsing");
         } else {
             charsetEnd = net_FindCharInSet(charset, charsetEnd, HTTP_LWS ";(");
         }
     }
--- a/netwerk/test/unit/test_extract_charset_from_content_type.js
+++ b/netwerk/test/unit/test_extract_charset_from_content_type.js
@@ -116,50 +116,50 @@ function run_test() {
   hadCharset =
     netutil.extractCharsetFromContentType('text/html; charset="ISO-8859-1"',
 					  charset, charsetStart, charsetEnd);
   check(true, "ISO-8859-1", 9, 31);
 
   hadCharset =
     netutil.extractCharsetFromContentType("text/html; charset='ISO-8859-1'",
 					  charset, charsetStart, charsetEnd);
-  check(true, "ISO-8859-1", 9, 31);
+  check(true, "'ISO-8859-1'", 9, 31);
 
   hadCharset =
-    netutil.extractCharsetFromContentType("text/html; charset='ISO-8859-1', text/html",
+    netutil.extractCharsetFromContentType("text/html; charset=\"ISO-8859-1\", text/html",
 					  charset, charsetStart, charsetEnd);
   check(true, "ISO-8859-1", 9, 31);
 
   hadCharset =
-    netutil.extractCharsetFromContentType("text/html; charset='ISO-8859-1', text/html; charset=UTF8",
+    netutil.extractCharsetFromContentType("text/html; charset=\"ISO-8859-1\", text/html; charset=UTF8",
 					  charset, charsetStart, charsetEnd);
   check(true, "UTF8", 42, 56);
 
   hadCharset =
     netutil.extractCharsetFromContentType("text/html; charset=ISO-8859-1, TEXT/HTML",
 					  charset, charsetStart, charsetEnd);
   check(true, "ISO-8859-1", 9, 29);
 
   hadCharset =
     netutil.extractCharsetFromContentType("text/html; charset=ISO-8859-1, TEXT/plain",
 					  charset, charsetStart, charsetEnd);
   check(false, "", 41, 41);
 
   hadCharset =
-    netutil.extractCharsetFromContentType("text/plain, TEXT/HTML; charset='ISO-8859-1', text/html, TEXT/HTML",
+    netutil.extractCharsetFromContentType("text/plain, TEXT/HTML; charset=\"ISO-8859-1\", text/html, TEXT/HTML",
 					  charset, charsetStart, charsetEnd);
   check(true, "ISO-8859-1", 21, 43);
 
   hadCharset =
-    netutil.extractCharsetFromContentType('text/plain, TEXT/HTML; param="charset=UTF8"; charset=\'ISO-8859-1\'; param2="charset=UTF16", text/html, TEXT/HTML',
+    netutil.extractCharsetFromContentType('text/plain, TEXT/HTML; param="charset=UTF8"; charset="ISO-8859-1"; param2="charset=UTF16", text/html, TEXT/HTML',
 					  charset, charsetStart, charsetEnd);
   check(true, "ISO-8859-1", 43, 65);
 
   hadCharset =
-    netutil.extractCharsetFromContentType('text/plain, TEXT/HTML; param=charset=UTF8; charset=\'ISO-8859-1\'; param2=charset=UTF16, text/html, TEXT/HTML',
+    netutil.extractCharsetFromContentType('text/plain, TEXT/HTML; param=charset=UTF8; charset="ISO-8859-1"; param2=charset=UTF16, text/html, TEXT/HTML',
 					  charset, charsetStart, charsetEnd);
   check(true, "ISO-8859-1", 41, 63);
 
   hadCharset =
     netutil.extractCharsetFromContentType("text/plain; param= , text/html",
 					  charset, charsetStart, charsetEnd);
   check(false, "", 30, 30);
 
--- a/netwerk/test/unit/test_parse_content_type.js
+++ b/netwerk/test/unit/test_parse_content_type.js
@@ -51,16 +51,17 @@ function check(aType, aCharset, aHadChar
   do_check_eq(aCharset, charset.value);
   do_check_eq(aHadCharset, hadCharset.value);
   reset();
 }
 
 function run_test() {
   var netutil = Components.classes["@mozilla.org/network/util;1"]
                           .getService(Components.interfaces.nsINetUtil);
+
   type = netutil.parseContentType("text/html", charset, hadCharset);
   check("text/html", "", false);
 
   type = netutil.parseContentType("TEXT/HTML", charset, hadCharset);
   check("text/html", "", false);
 
   type = netutil.parseContentType("text/html, text/html", charset, hadCharset);
   check("text/html", "", false);
@@ -83,39 +84,39 @@ function run_test() {
   check("text/html", "ISO-8859-1", true);
 
   type = netutil.parseContentType('text/html; charset="ISO-8859-1"',
 				  charset, hadCharset);
   check("text/html", "ISO-8859-1", true);
 
   type = netutil.parseContentType("text/html; charset='ISO-8859-1'",
 				  charset, hadCharset);
-  check("text/html", "ISO-8859-1", true);
+  check("text/html", "'ISO-8859-1'", true);
 
-  type = netutil.parseContentType("text/html; charset='ISO-8859-1', text/html",
+  type = netutil.parseContentType("text/html; charset=\"ISO-8859-1\", text/html",
 				  charset, hadCharset);
   check("text/html", "ISO-8859-1", true);
 
-  type = netutil.parseContentType("text/html; charset='ISO-8859-1', text/html; charset=UTF8",
+  type = netutil.parseContentType("text/html; charset=\"ISO-8859-1\", text/html; charset=UTF8",
 				  charset, hadCharset);
   check("text/html", "UTF8", true);
 
   type = netutil.parseContentType("text/html; charset=ISO-8859-1, TEXT/HTML", charset, hadCharset);
   check("text/html", "ISO-8859-1", true);
 
   type = netutil.parseContentType("text/html; charset=ISO-8859-1, TEXT/plain", charset, hadCharset);
   check("text/plain", "", true);
 
-  type = netutil.parseContentType("text/plain, TEXT/HTML; charset='ISO-8859-1', text/html, TEXT/HTML", charset, hadCharset);
+  type = netutil.parseContentType("text/plain, TEXT/HTML; charset=ISO-8859-1, text/html, TEXT/HTML", charset, hadCharset);
   check("text/html", "ISO-8859-1", true);
 
-  type = netutil.parseContentType('text/plain, TEXT/HTML; param="charset=UTF8"; charset=\'ISO-8859-1\'; param2="charset=UTF16", text/html, TEXT/HTML', charset, hadCharset);
+  type = netutil.parseContentType('text/plain, TEXT/HTML; param="charset=UTF8"; charset="ISO-8859-1"; param2="charset=UTF16", text/html, TEXT/HTML', charset, hadCharset);
   check("text/html", "ISO-8859-1", true);
 
-  type = netutil.parseContentType('text/plain, TEXT/HTML; param=charset=UTF8; charset=\'ISO-8859-1\'; param2=charset=UTF16, text/html, TEXT/HTML', charset, hadCharset);
+  type = netutil.parseContentType('text/plain, TEXT/HTML; param=charset=UTF8; charset="ISO-8859-1"; param2=charset=UTF16, text/html, TEXT/HTML', charset, hadCharset);
   check("text/html", "ISO-8859-1", true);  
 
   type = netutil.parseContentType("text/plain; param= , text/html", charset, hadCharset);
   check("text/html", "", false);
 
   type = netutil.parseContentType('text/plain; param=", text/html"', charset, hadCharset);
   check("text/plain", "", false);
 
@@ -129,9 +130,18 @@ function run_test() {
   check("text/plain", "", false);
 
   type = netutil.parseContentType('text/plain charset=UTF8', charset, hadCharset);
   check("text/plain", "", false);
 
   type = netutil.parseContentType('text/plain, TEXT/HTML; param="charset=UTF8"; ; param2="charset=UTF16", text/html, TEXT/HTML', charset, hadCharset);
   check("text/html", "", false);
 
+  // Bug 700589
+
+  // check that single quote doesn't confuse parsing of subsequent parameters
+  type = netutil.parseContentType("text/plain; x='; charset=\"UTF-8\"", charset, hadCharset);
+  check("text/plain", "UTF-8", true);
+
+  // check that single quotes do not get removed from extracted charset
+  type = netutil.parseContentType("text/plain; charset='UTF-8'", charset, hadCharset);
+  check("text/plain", "'UTF-8'", true);
 }
--- a/testing/mochitest/tests/SimpleTest/specialpowersAPI.js
+++ b/testing/mochitest/tests/SimpleTest/specialpowersAPI.js
@@ -682,16 +682,24 @@ SpecialPowersAPI.prototype = {
   },
 
   clipboardCopyString: function(preExpectedVal) {  
     var cbHelperSvc = Components.classes["@mozilla.org/widget/clipboardhelper;1"].
                       getService(Components.interfaces.nsIClipboardHelper);
     cbHelperSvc.copyString(preExpectedVal);
   },
 
+  supportsSelectionClipboard: function() {
+    if (this._cb == null) {
+      this._cb = Components.classes["@mozilla.org/widget/clipboard;1"].
+                            getService(Components.interfaces.nsIClipboard);
+    }
+    return this._cb.supportsSelectionClipboard();
+  },
+
   snapshotWindow: function (win, withCaret) {
     var el = this.window.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
     el.width = win.innerWidth;
     el.height = win.innerHeight;
     var ctx = el.getContext("2d");
     var flags = 0;
 
     ctx.drawWindow(win, win.scrollX, win.scrollY,
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/Makefile.in
@@ -0,0 +1,73 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (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.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozbase.
+#
+# The Initial Developer of the Original Code is
+#   The Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Andrew Halberstadt <halbersa@gmail.com> (Original author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH = ../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = testing_mozbase
+
+include $(topsrcdir)/config/rules.mk
+
+# Harness packages from the srcdir;
+# python packages to be installed IN INSTALLATION ORDER.
+# Packages later in the list can depend only on packages earlier in the list.
+MOZBASE_PACKAGES = \
+  manifestdestiny \
+  mozdevice \
+  mozhttpd \
+  mozinfo \
+  mozinstall \
+  mozlog \
+  mozprocess \
+  mozprofile \
+  mozrunner \
+  $(NULL)
+
+MOZBASE_EXTRAS = \
+  setup_development.py \
+  README \
+  $(NULL)
+
+stage-package: PKG_STAGE = $(DIST)/test-package-stage
+stage-package:
+	$(NSINSTALL) -D $(PKG_STAGE)/mozbase
+	@(cd $(srcdir) && tar $(TAR_CREATE_FLAGS) - $(MOZBASE_PACKAGES)) | (cd $(PKG_STAGE)/mozbase && tar -xf -)
+	@(cd $(srcdir) && tar $(TAR_CREATE_FLAGS) - $(MOZBASE_EXTRAS)) | (cd $(PKG_STAGE)/mozbase && tar -xf -)
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/README
@@ -0,0 +1,7 @@
+This is the git repo for the mozbase suite of python utilities.
+
+Learn more about mozbase here: https://wiki.mozilla.org/Auto-tools/Projects/MozBase
+
+Bugs live at https://bugzilla.mozilla.org/buglist.cgi?resolution=---&component=Mozbase&product=Testing and https://bugzilla.mozilla.org/buglist.cgi?resolution=---&status_whiteboard_type=allwordssubstr&query_format=advanced&status_whiteboard=mozbase
+
+To file a bug, go to https://bugzilla.mozilla.org/enter_bug.cgi?product=Testing&component=Mozbase
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/manifestdestiny/README.txt
@@ -0,0 +1,345 @@
+ManifestDestiny
+===============
+
+Universal manifests for Mozilla test harnesses
+
+
+What is ManifestDestiny?
+------------------------
+
+What ManifestDestiny gives you::
+
+* manifests are (ordered) lists of tests
+* tests may have an arbitrary number of key, value pairs
+* the parser returns an ordered list of test data structures, which
+  are just dicts with some keys.  For example, a test with no
+  user-specified metadata looks like this::
+
+  [{'path':
+    '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests/testToolbar/testBackForwardButtons.js',
+    'name': 'testToolbar/testBackForwardButtons.js', 'here':
+    '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests',
+    'manifest': '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests',}]
+
+The keys displayed here (path, name, here, and manifest) are reserved
+keys for ManifestDestiny and any consuming APIs.  You can add
+additional key, value metadata to each test.
+
+
+Why have test manifests?
+------------------------
+
+Most Mozilla test harnesses work by crawling a directory structure.
+While this is straight-forward, manifests offer several practical
+advantages::
+
+* ability to turn a test off easily: if a test is broken on m-c
+  currently, the only way to turn it off, generally speaking, is just
+  removing the test.  Often this is undesirable, as if the test should
+  be dismissed because other people want to land and it can't be
+  investigated in real time (is it a failure? is the test bad? is no
+  one around that knows the test?), then backing out a test is at best
+  problematic.  With a manifest, a test may be disabled without
+  removing it from the tree and a bug filed with the appropriate
+  reason::
+
+   [test_broken.js]
+   disabled = https://bugzilla.mozilla.org/show_bug.cgi?id=123456
+
+* ability to run different (subsets of) tests on different
+  platforms. Traditionally, we've done a bit of magic or had the test
+  know what platform it would or would not run on. With manifests, you
+  can mark what platforms a test will or will not run on and change
+  these without changing the test.
+
+   [test_works_on_windows_only.js]
+   run-if = os == 'win'
+
+* ability to markup tests with metadata. We have a large, complicated,
+  and always changing infrastructure.  key, value metadata may be used
+  as an annotation to a test and appropriately curated and mined.  For
+  instance, we could mark certain tests as randomorange with a bug
+  number, if it were desirable.
+
+* ability to have sane and well-defined test-runs. You can keep
+  different manifests for different test runs and ``[include:]``
+  (sub)manifests as appropriate to your needs.
+
+
+Manifest Format
+---------------
+
+Manifests are .ini file with the section names denoting the path
+relative to the manifest::
+
+ [foo.js]
+ [bar.js]
+ [fleem.js]
+
+The sections are read in order. In addition, tests may include
+arbitrary key, value metadata to be used by the harness.  You may also
+have a ``[DEFAULT]`` section that will give key, value pairs that will
+be inherited by each test unless overridden::
+
+ [DEFAULT]
+ type = restart
+
+ [lilies.js]
+ color = white
+
+ [daffodils.js]
+ color = yellow
+ type = other
+ # override type from DEFAULT
+
+ [roses.js]
+ color = red
+
+You can also include other manifests::
+
+ [include:subdir/anothermanifest.ini]
+
+Manifests are included relative to the directory of the manifest with
+the ``[include:]`` directive unless they are absolute paths.
+
+
+Data
+----
+
+Manifest Destiny gives tests as a list of dictionaries (in python
+terms). 
+
+* path: full path to the test
+* name: short name of the test; this is the (usually) relative path
+  specified in the section name
+* here: the parent directory of the manifest
+* manifest: the path to the manifest containing the test
+
+This data corresponds to a one-line manifest::
+
+ [testToolbar/testBackForwardButtons.js]
+
+If additional key, values were specified, they would be in this dict
+as well.
+
+Outside of the reserved keys, the remaining key, values
+are up to convention to use.  There is a (currently very minimal)
+generic integration layer in ManifestDestiny for use of all harnesses,
+``manifestparser.TestManifest``.
+For instance, if the 'disabled' key is present, you can get the set of
+tests without disabled (various other queries are doable as well).
+
+Since the system is convention-based, the harnesses may do whatever
+they want with the data.  They may ignore it completely, they may use
+the provided integration layer, or they may provide their own
+integration layer.  This should allow whatever sort of logic is
+desired.  For instance, if in yourtestharness you wanted to run only on
+mondays for a certain class of tests::
+
+ tests = []
+ for test in manifests.tests:
+     if 'runOnDay' in test:
+        if calendar.day_name[calendar.weekday(*datetime.datetime.now().timetuple()[:3])].lower() == test['runOnDay'].lower():
+            tests.append(test)
+     else:
+        tests.append(test)
+
+To recap:
+* the manifests allow you to specify test data
+* the parser gives you this data
+* you can use it however you want or process it further as you need
+
+Tests are denoted by sections in an .ini file (see
+http://hg.mozilla.org/automation/ManifestDestiny/file/tip/manifestdestiny/tests/mozmill-example.ini). 
+
+Additional manifest files may be included with an ``[include:]`` directive::
+
+ [include:path-to-additional-file.manifest]
+
+The path to included files is relative to the current manifest.
+
+The ``[DEFAULT]`` section contains variables that all tests inherit from.
+
+Included files will inherit the top-level variables but may override
+in their own ``[DEFAULT]`` section.
+
+
+ManifestDestiny Architecture
+----------------------------
+
+There is a two- or three-layered approach to the ManifestDestiny
+architecture, depending on your needs::
+
+1. ManifestParser: this is a generic parser for .ini manifests that
+facilitates the `[include:]` logic and the inheritence of
+metadata. Despite the internal variable being called ``self.tests``
+(an oversight), this layer has nothing in particular to do with tests.
+
+2. TestManifest: this is a harness-agnostic integration layer that is
+test-specific. TestManifest faciliates ``skip-if`` and ``run-if``
+logic.
+
+3. Optionally, a harness will have an integration layer than inherits
+from TestManifest if more harness-specific customization is desired at
+the manifest level.
+
+See the source code at http://hg.mozilla.org/automation/ManifestDestiny
+and
+http://hg.mozilla.org/automation/ManifestDestiny/file/tip/manifestparser.py
+in particular.
+
+
+Using Manifests
+---------------
+
+A test harness will normally call ``TestManifest.active_tests`` (
+http://hg.mozilla.org/automation/ManifestDestiny/file/c0399fbfa830/manifestparser.py#l506 )::
+
+   506     def active_tests(self, exists=True, disabled=True, **tags):
+
+The manifests are passed to the ``__init__`` or ``read`` methods with
+appropriate arguments.  ``active_tests`` then allows you to select the
+tests you want::
+
+- exists : return only existing tests
+- disabled : whether to return disabled tests; if not these will be
+  filtered out; if True (the default), the ``disabled`` key of a
+  test's metadata will be present and will be set to the reason that a
+  test is disabled
+- tags : keys and values to filter on (e.g. ``os='linux'``)
+
+``active_tests`` looks for tests with ``skip-if.${TAG}`` or
+``run-if``.  If the condition is or is not fulfilled,
+respectively, the test is marked as disabled.  For instance, if you
+pass ``**dict(os='linux')`` as ``**tags``, if a test contains a line
+``skip-if = os == 'linux'`` this test will be disabled, or 
+``run-if = os = 'win'`` in which case the test will also be disabled.  It
+is up to the harness to pass in tags appropriate to its usage.  
+
+
+Creating Manifests
+------------------
+
+ManifestDestiny comes with a console script, ``manifestparser create``, that
+may be used to create a seed manifest structure from a directory of
+files.  Run ``manifestparser help create`` for usage information.
+
+
+Copying Manifests
+-----------------
+
+To copy tests and manifests from a source::
+
+ manifestparser [options] copy from_manifest to_directory -tag1 -tag2 --key1=value1 key2=value2 ...
+
+
+Upating Tests
+-------------
+
+To update the tests associated with with a manifest from a source
+directory::
+
+ manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ...
+
+
+Tests
+-----
+
+ManifestDestiny includes a suite of tests:
+
+http://hg.mozilla.org/automation/ManifestDestiny/file/tip/manifestdestiny/tests
+
+``test_manifest.txt`` is a doctest that may be helpful in figuring out
+how to use the API.  Tests are run via ``python test.py``.
+
+
+Bugs
+----
+
+Please file any bugs or feature requests at 
+
+https://bugzilla.mozilla.org/enter_bug.cgi?product=Testing&component=ManifestParser
+
+Or contact jhammel @mozilla.org or in #ateam on irc.mozilla.org
+
+
+CLI
+---
+
+Run ``manifestparser help`` for usage information.
+
+To create a manifest from a set of directories::
+
+ manifestparser [options] create directory <directory> <...> [create-options]
+
+To output a manifest of tests::
+
+ manifestparser [options] write manifest <manifest> <...> -tag1 -tag2 --key1=value1 --key2=value2 ...
+
+To copy tests and manifests from a source::
+
+ manifestparser [options] copy from_manifest to_manifest -tag1 -tag2 --key1=value1 key2=value2 ...
+
+To update the tests associated with with a manifest from a source
+directory::
+
+ manifestparser [options] update manifest from_directory -tag1 -tag2 --key1=value1 --key2=value2 ...
+
+
+Design Considerations
+---------------------
+
+Contrary to some opinion, manifestparser.py and the associated .ini
+format were not magically plucked from the sky but were descended upon
+through several design considerations.
+
+* test manifests should be ordered.  While python 2.6 and greater has
+  a ConfigParser that can use an ordered dictionary, it is a
+  requirement that we support python 2.4 for the build + testing
+  environment.  To that end, a ``read_ini`` function was implemented
+  in manifestparser.py that should be the equivalent of the .ini
+  dialect used by ConfigParser.
+
+* the manifest format should be easily human readable/writable.  While
+  there was initially some thought of using JSON, there was pushback
+  that JSON was not easily editable.  An ideal manifest format would
+  degenerate to a line-separated list of files.  While .ini format
+  requires an additional ``[]`` per line, and while there have been
+  complaints about this, hopefully this is good enough.
+
+* python does not have an in-built YAML parser.  Since it was
+  undesirable for manifestparser.py to have any dependencies, YAML was
+  dismissed as a format.
+
+* we could have used a proprietary format but decided against it.
+  Everyone knows .ini and there are good tools to deal with it.
+  However, since read_ini is the only function that transforms a
+  manifest to a list of key, value pairs, while the implications for
+  changing the format impacts downstream code, doing so should be
+  programmatically simple.
+
+* there should be a single file that may easily be
+  transported. Traditionally, test harnesses have lived in
+  mozilla-central. This is less true these days and it is increasingly
+  likely that more tests will not live in mozilla-central going
+  forward.  So ``manifestparser.py`` should be highly consumable. To
+  this end, it is a single file, as appropriate to mozilla-central,
+  which is also a working python package deployed to PyPI for easy
+  installation. 
+
+
+Historical Reference
+--------------------
+
+Date-ordered list of links about how manifests came to be where they are today::
+
+* https://wiki.mozilla.org/Auto-tools/Projects/UniversalManifest
+* http://alice.nodelman.net/blog/post/2010/05/
+* http://alice.nodelman.net/blog/post/universal-manifest-for-unit-tests-a-proposal/
+* https://elvis314.wordpress.com/2010/07/05/improving-personal-hygiene-by-adjusting-mochitests/
+* https://elvis314.wordpress.com/2010/07/27/types-of-data-we-care-about-in-a-manifest/
+* https://bugzilla.mozilla.org/show_bug.cgi?id=585106
+* http://elvis314.wordpress.com/2011/05/20/converting-xpcshell-from-listing-directories-to-a-manifest/
+* https://bugzilla.mozilla.org/show_bug.cgi?id=616999
+* https://wiki.mozilla.org/Auto-tools/Projects/ManifestDestiny
+* https://developer.mozilla.org/en/Writing_xpcshell-based_unit_tests#Adding_your_tests_to_the_xpcshell_manifest
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/manifestdestiny/manifestparser.py
@@ -0,0 +1,1114 @@
+#!/usr/bin/env python
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (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.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is manifestdestiny.
+#
+# The Initial Developer of the Original Code is
+#  The Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#     Jeff Hammel <jhammel@mozilla.com>     (Original author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+"""
+Mozilla universal manifest parser
+"""
+
+# this file lives at
+# http://hg.mozilla.org/automation/ManifestDestiny/raw-file/tip/manifestparser.py
+
+__all__ = ['read_ini', # .ini reader
+           'ManifestParser', 'TestManifest', 'convert', # manifest handling
+           'parse', 'ParseError', 'ExpressionParser'] # conditional expression parser
+
+import os
+import re
+import shutil
+import sys
+from fnmatch import fnmatch
+from optparse import OptionParser
+
+version = '0.5.4' # package version
+try:
+    from setuptools import setup
+except:
+    setup = None
+
+# we need relpath, but it is introduced in python 2.6
+# http://docs.python.org/library/os.path.html
+try:
+    relpath = os.path.relpath
+except AttributeError:
+    def relpath(path, start):
+        """
+        Return a relative version of a path
+        from /usr/lib/python2.6/posixpath.py
+        """
+
+        if not path:
+            raise ValueError("no path specified")
+
+        start_list = os.path.abspath(start).split(os.path.sep)
+        path_list = os.path.abspath(path).split(os.path.sep)
+
+        # Work out how much of the filepath is shared by start and path.
+        i = len(os.path.commonprefix([start_list, path_list]))
+
+        rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
+        if not rel_list:
+            return start
+        return os.path.join(*rel_list)
+
+# expr.py
+# from:
+# http://k0s.org/mozilla/hg/expressionparser
+# http://hg.mozilla.org/users/tmielczarek_mozilla.com/expressionparser
+
+# Implements a top-down parser/evaluator for simple boolean expressions.
+# ideas taken from http://effbot.org/zone/simple-top-down-parsing.htm
+#
+# Rough grammar:
+# expr := literal
+#       | '(' expr ')'
+#       | expr '&&' expr
+#       | expr '||' expr
+#       | expr '==' expr
+#       | expr '!=' expr
+# literal := BOOL
+#          | INT
+#          | STRING
+#          | IDENT
+# BOOL   := true|false
+# INT    := [0-9]+
+# STRING := "[^"]*"
+# IDENT  := [A-Za-z_]\w*
+
+# Identifiers take their values from a mapping dictionary passed as the second
+# argument.
+
+# Glossary (see above URL for details):
+# - nud: null denotation
+# - led: left detonation
+# - lbp: left binding power
+# - rbp: right binding power
+
+class ident_token(object):
+    def __init__(self, value):
+        self.value = value
+    def nud(self, parser):
+        # identifiers take their value from the value mappings passed
+        # to the parser
+        return parser.value(self.value)
+
+class literal_token(object):
+    def __init__(self, value):
+        self.value = value
+    def nud(self, parser):
+        return self.value
+
+class eq_op_token(object):
+    "=="
+    def led(self, parser, left):
+        return left == parser.expression(self.lbp)
+
+class neq_op_token(object):
+    "!="
+    def led(self, parser, left):
+        return left != parser.expression(self.lbp)
+
+class not_op_token(object):
+    "!"
+    def nud(self, parser):
+        return not parser.expression()
+
+class and_op_token(object):
+    "&&"
+    def led(self, parser, left):
+        right = parser.expression(self.lbp)
+        return left and right
+
+class or_op_token(object):
+    "||"
+    def led(self, parser, left):
+        right = parser.expression(self.lbp)
+        return left or right
+
+class lparen_token(object):
+    "("
+    def nud(self, parser):
+        expr = parser.expression()
+        parser.advance(rparen_token)
+        return expr
+
+class rparen_token(object):
+    ")"
+
+class end_token(object):
+    """always ends parsing"""
+
+### derived literal tokens
+
+class bool_token(literal_token):
+    def __init__(self, value):
+        value = {'true':True, 'false':False}[value]
+        literal_token.__init__(self, value)
+
+class int_token(literal_token):
+    def __init__(self, value):
+        literal_token.__init__(self, int(value))
+
+class string_token(literal_token):
+    def __init__(self, value):
+        literal_token.__init__(self, value[1:-1])
+
+precedence = [(end_token, rparen_token),
+              (or_op_token,),
+              (and_op_token,),
+              (eq_op_token, neq_op_token),
+              (lparen_token,),
+              ]
+for index, rank in enumerate(precedence):
+    for token in rank:
+        token.lbp = index # lbp = lowest left binding power
+
+class ParseError(Exception):
+    """errror parsing conditional expression"""
+
+class ExpressionParser(object):
+    def __init__(self, text, valuemapping, strict=False):
+        """
+        Initialize the parser with input |text|, and |valuemapping| as
+        a dict mapping identifier names to values.
+        """
+        self.text = text
+        self.valuemapping = valuemapping
+        self.strict = strict
+
+    def _tokenize(self):
+        """
+        Lex the input text into tokens and yield them in sequence.
+        """
+        # scanner callbacks
+        def bool_(scanner, t): return bool_token(t)
+        def identifier(scanner, t): return ident_token(t)
+        def integer(scanner, t): return int_token(t)
+        def eq(scanner, t): return eq_op_token()
+        def neq(scanner, t): return neq_op_token()
+        def or_(scanner, t): return or_op_token()
+        def and_(scanner, t): return and_op_token()
+        def lparen(scanner, t): return lparen_token()
+        def rparen(scanner, t): return rparen_token()
+        def string_(scanner, t): return string_token(t)
+        def not_(scanner, t): return not_op_token()
+
+        scanner = re.Scanner([
+            (r"true|false", bool_),
+            (r"[a-zA-Z_]\w*", identifier),
+            (r"[0-9]+", integer),
+            (r'("[^"]*")|(\'[^\']*\')', string_),
+            (r"==", eq),
+            (r"!=", neq),
+            (r"\|\|", or_),
+            (r"!", not_),
+            (r"&&", and_),
+            (r"\(", lparen),
+            (r"\)", rparen),
+            (r"\s+", None), # skip whitespace
+            ])
+        tokens, remainder = scanner.scan(self.text)
+        for t in tokens:
+            yield t
+        yield end_token()
+
+    def value(self, ident):
+        """
+        Look up the value of |ident| in the value mapping passed in the
+        constructor.
+        """
+        if self.strict:
+            return self.valuemapping[ident]
+        else:
+            return self.valuemapping.get(ident, None)
+
+    def advance(self, expected):
+        """
+        Assert that the next token is an instance of |expected|, and advance
+        to the next token.
+        """
+        if not isinstance(self.token, expected):
+            raise Exception, "Unexpected token!"
+        self.token = self.iter.next()
+
+    def expression(self, rbp=0):
+        """
+        Parse and return the value of an expression until a token with
+        right binding power greater than rbp is encountered.
+        """
+        t = self.token
+        self.token = self.iter.next()
+        left = t.nud(self)
+        while rbp < self.token.lbp:
+            t = self.token
+            self.token = self.iter.next()
+            left = t.led(self, left)
+        return left
+
+    def parse(self):
+        """
+        Parse and return the value of the expression in the text
+        passed to the constructor. Raises a ParseError if the expression
+        could not be parsed.
+        """
+        try:
+            self.iter = self._tokenize()
+            self.token = self.iter.next()
+            return self.expression()
+        except:
+            raise ParseError("could not parse: %s; variables: %s" % (self.text, self.valuemapping))
+
+    __call__ = parse
+
+def parse(text, **values):
+    """
+    Parse and evaluate a boolean expression in |text|. Use |values| to look
+    up the value of identifiers referenced in the expression. Returns the final
+    value of the expression. A ParseError will be raised if parsing fails.
+    """
+    return ExpressionParser(text, values).parse()
+
+def normalize_path(path):
+    """normalize a relative path"""
+    if sys.platform.startswith('win'):
+        return path.replace('/', os.path.sep)
+    return path
+
+def denormalize_path(path):
+    """denormalize a relative path"""
+    if sys.platform.startswith('win'):
+        return path.replace(os.path.sep, '/')
+    return path
+
+
+def read_ini(fp, variables=None, default='DEFAULT',
+             comments=';#', separators=('=', ':'),
+             strict=True):
+    """
+    read an .ini file and return a list of [(section, values)]
+    - fp : file pointer or path to read
+    - variables : default set of variables
+    - default : name of the section for the default section
+    - comments : characters that if they start a line denote a comment
+    - separators : strings that denote key, value separation in order
+    - strict : whether to be strict about parsing
+    """
+
+    if variables is None:
+        variables = {}
+
+    if isinstance(fp, basestring):
+        fp = file(fp)
+
+    sections = []
+    key = value = None
+    section_names = set([])
+
+    # read the lines
+    for line in fp.readlines():
+
+        stripped = line.strip()
+
+        # ignore blank lines
+        if not stripped:
+            # reset key and value to avoid continuation lines
+            key = value = None
+            continue
+
+        # ignore comment lines
+        if stripped[0] in comments:
+            continue
+
+        # check for a new section
+        if len(stripped) > 2 and stripped[0] == '[' and stripped[-1] == ']':
+            section = stripped[1:-1].strip()
+            key = value = None
+
+            # deal with DEFAULT section
+            if section.lower() == default.lower():
+                if strict:
+                    assert default not in section_names
+                section_names.add(default)
+                current_section = variables
+                continue
+
+            if strict:
+                # make sure this section doesn't already exist
+                assert section not in section_names
+
+            section_names.add(section)
+            current_section = {}
+            sections.append((section, current_section))
+            continue
+
+        # if there aren't any sections yet, something bad happen
+        if not section_names:
+            raise Exception('No sections found')
+
+        # (key, value) pair
+        for separator in separators:
+            if separator in stripped:
+                key, value = stripped.split(separator, 1)
+                key = key.strip()
+                value = value.strip()
+
+                if strict:
+                    # make sure this key isn't already in the section or empty
+                    assert key
+                    if current_section is not variables:
+                        assert key not in current_section
+
+                current_section[key] = value
+                break
+        else:
+            # continuation line ?
+            if line[0].isspace() and key:
+                value = '%s%s%s' % (value, os.linesep, stripped)
+                current_section[key] = value
+            else:
+                # something bad happen!
+                raise Exception("Not sure what you're trying to do")
+
+    # interpret the variables
+    def interpret_variables(global_dict, local_dict):
+        variables = global_dict.copy()
+        variables.update(local_dict)
+        return variables
+
+    sections = [(i, interpret_variables(variables, j)) for i, j in sections]
+    return sections
+
+
+### objects for parsing manifests
+
+class ManifestParser(object):
+    """read .ini manifests"""
+
+    ### methods for reading manifests
+
+    def __init__(self, manifests=(), defaults=None, strict=True):
+        self._defaults = defaults or {}
+        self.tests = []
+        self.strict = strict
+        self.rootdir = None
+        self.relativeRoot = None
+        if manifests:
+            self.read(*manifests)
+
+    def getRelativeRoot(self, root):
+        return root
+
+    def read(self, *filenames, **defaults):
+
+        # ensure all files exist
+        missing = [ filename for filename in filenames
+                    if not os.path.exists(filename) ]
+        if missing:
+            raise IOError('Missing files: %s' % ', '.join(missing))
+
+        # process each file
+        for filename in filenames:
+
+            # set the per file defaults
+            defaults = defaults.copy() or self._defaults.copy()
+            here = os.path.dirname(os.path.abspath(filename))
+            defaults['here'] = here
+
+            if self.rootdir is None:
+                # set the root directory
+                # == the directory of the first manifest given
+                self.rootdir = here
+
+            # read the configuration
+            sections = read_ini(fp=filename, variables=defaults, strict=self.strict)
+
+            # get the tests
+            for section, data in sections:
+
+                # a file to include
+                # TODO: keep track of included file structure:
+                # self.manifests = {'manifest.ini': 'relative/path.ini'}
+                if section.startswith('include:'):
+                    include_file = section.split('include:', 1)[-1]
+                    include_file = normalize_path(include_file)
+                    if not os.path.isabs(include_file):
+                        include_file = os.path.join(self.getRelativeRoot(here), include_file)
+                    if not os.path.exists(include_file):
+                        if self.strict:
+                            raise IOError("File '%s' does not exist" % include_file)
+                        else:
+                            continue
+                    include_defaults = data.copy()
+                    self.read(include_file, **include_defaults)
+                    continue
+
+                # otherwise an item
+                test = data
+                test['name'] = section
+                test['manifest'] = os.path.abspath(filename)
+
+                # determine the path
+                path = test.get('path', section)
+                if '://' not in path: # don't futz with URLs
+                    path = normalize_path(path)
+                    if not os.path.isabs(path):
+                        path = os.path.join(here, path)
+                test['path'] = path
+
+                # append the item
+                self.tests.append(test)
+
+    ### methods for querying manifests
+
+    def query(self, *checks, **kw):
+        """
+        general query function for tests
+        - checks : callable conditions to test if the test fulfills the query
+        """
+        tests = kw.get('tests', None)
+        if tests is None:
+            tests = self.tests
+        retval = []
+        for test in tests:
+            for check in checks:
+                if not check(test):
+                    break
+            else:
+                retval.append(test)
+        return retval
+
+    def get(self, _key=None, inverse=False, tags=None, tests=None, **kwargs):
+        # TODO: pass a dict instead of kwargs since you might hav
+        # e.g. 'inverse' as a key in the dict
+
+        # TODO: tags should just be part of kwargs with None values
+        # (None == any is kinda weird, but probably still better)
+
+        # fix up tags
+        if tags:
+            tags = set(tags)
+        else:
+            tags = set()
+
+        # make some check functions
+        if inverse:
+            has_tags = lambda test: not tags.intersection(test.keys())
+            def dict_query(test):
+                for key, value in kwargs.items():
+                    if test.get(key) == value:
+                        return False
+                return True
+        else:
+            has_tags = lambda test: tags.issubset(test.keys())
+            def dict_query(test):
+                for key, value in kwargs.items():
+                    if test.get(key) != value:
+                        return False
+                return True
+
+        # query the tests
+        tests = self.query(has_tags, dict_query, tests=tests)
+
+        # if a key is given, return only a list of that key
+        # useful for keys like 'name' or 'path'
+        if _key:
+            return [test[_key] for test in tests]
+
+        # return the tests
+        return tests
+
+    def missing(self, tests=None):
+        """return list of tests that do not exist on the filesystem"""
+        if tests is None:
+            tests = self.tests
+        return [test for test in tests
+                if not os.path.exists(test['path'])]
+
+    def manifests(self, tests=None):
+        """
+        return manifests in order in which they appear in the tests
+        """
+        if tests is None:
+            tests = self.tests
+        manifests = []
+        for test in tests:
+            manifest = test.get('manifest')
+            if not manifest:
+                continue
+            if manifest not in manifests:
+                manifests.append(manifest)
+        return manifests
+
+    ### methods for outputting from manifests
+
+    def write(self, fp=sys.stdout, rootdir=None,
+              global_tags=None, global_kwargs=None,
+              local_tags=None, local_kwargs=None):
+        """
+        write a manifest given a query
+        global and local options will be munged to do the query
+        globals will be written to the top of the file
+        locals (if given) will be written per test
+        """
+
+        # root directory
+        if rootdir is None:
+            rootdir = self.rootdir
+
+        # sanitize input
+        global_tags = global_tags or set()
+        local_tags = local_tags or set()
+        global_kwargs = global_kwargs or {}
+        local_kwargs = local_kwargs or {}
+
+        # create the query
+        tags = set([])
+        tags.update(global_tags)
+        tags.update(local_tags)
+        kwargs = {}
+        kwargs.update(global_kwargs)
+        kwargs.update(local_kwargs)
+
+        # get matching tests
+        tests = self.get(tags=tags, **kwargs)
+
+        # print the .ini manifest
+        if global_tags or global_kwargs:
+            print >> fp, '[DEFAULT]'
+            for tag in global_tags:
+                print >> fp, '%s =' % tag
+            for key, value in global_kwargs.items():
+                print >> fp, '%s = %s' % (key, value)
+            print >> fp
+
+        for test in tests:
+            test = test.copy() # don't overwrite
+
+            path = test['name']
+            if not os.path.isabs(path):
+                path = test['path']
+                if self.rootdir:
+                    path = relpath(test['path'], self.rootdir)
+                path = denormalize_path(path)
+            print >> fp, '[%s]' % path
+
+            # reserved keywords:
+            reserved = ['path', 'name', 'here', 'manifest']
+            for key in sorted(test.keys()):
+                if key in reserved:
+                    continue
+                if key in global_kwargs:
+                    continue
+                if key in global_tags and not test[key]:
+                    continue
+                print >> fp, '%s = %s' % (key, test[key])
+            print >> fp
+
+    def copy(self, directory, rootdir=None, *tags, **kwargs):
+        """
+        copy the manifests and associated tests
+        - directory : directory to copy to
+        - rootdir : root directory to copy to (if not given from manifests)
+        - tags : keywords the tests must have
+        - kwargs : key, values the tests must match
+        """
+        # XXX note that copy does *not* filter the tests out of the
+        # resulting manifest; it just stupidly copies them over.
+        # ideally, it would reread the manifests and filter out the
+        # tests that don't match *tags and **kwargs
+
+        # destination
+        if not os.path.exists(directory):
+            os.path.makedirs(directory)
+        else:
+            # sanity check
+            assert os.path.isdir(directory)
+
+        # tests to copy
+        tests = self.get(tags=tags, **kwargs)
+        if not tests:
+            return # nothing to do!
+
+        # root directory
+        if rootdir is None:
+            rootdir = self.rootdir
+
+        # copy the manifests + tests
+        manifests = [relpath(manifest, rootdir) for manifest in self.manifests()]
+        for manifest in manifests:
+            destination = os.path.join(directory, manifest)
+            dirname = os.path.dirname(destination)
+            if not os.path.exists(dirname):
+                os.makedirs(dirname)
+            else:
+                # sanity check
+                assert os.path.isdir(dirname)
+            shutil.copy(os.path.join(rootdir, manifest), destination)
+        for test in tests:
+            if os.path.isabs(test['name']):
+                continue
+            source = test['path']
+            if not os.path.exists(source):
+                print >> sys.stderr, "Missing test: '%s' does not exist!" % source
+                continue
+                # TODO: should err on strict
+            destination = os.path.join(directory, relpath(test['path'], rootdir))
+            shutil.copy(source, destination)
+            # TODO: ensure that all of the tests are below the from_dir
+
+    def update(self, from_dir, rootdir=None, *tags, **kwargs):
+        """
+        update the tests as listed in a manifest from a directory
+        - from_dir : directory where the tests live
+        - rootdir : root directory to copy to (if not given from manifests)
+        - tags : keys the tests must have
+        - kwargs : key, values the tests must match
+        """
+
+        # get the tests
+        tests = self.get(tags=tags, **kwargs)
+
+        # get the root directory
+        if not rootdir:
+            rootdir = self.rootdir
+
+        # copy them!
+        for test in tests:
+            if not os.path.isabs(test['name']):
+                _relpath = relpath(test['path'], rootdir)
+                source = os.path.join(from_dir, _relpath)
+                if not os.path.exists(source):
+                    # TODO err on strict
+                    print >> sys.stderr, "Missing test: '%s'; skipping" % test['name']
+                    continue
+                destination = os.path.join(rootdir, _relpath)
+                shutil.copy(source, destination)
+
+
+class TestManifest(ManifestParser):
+    """
+    apply logic to manifests;  this is your integration layer :)
+    specific harnesses may subclass from this if they need more logic
+    """
+
+    def filter(self, values, tests):
+        """
+        filter on a specific list tag, e.g.:
+        run-if.os = win linux
+        skip-if.os = mac
+        """
+
+        # tags:
+        run_tag = 'run-if'
+        skip_tag = 'skip-if'
+        fail_tag = 'fail-if'
+
+        # loop over test
+        for test in tests:
+            reason = None # reason to disable
+
+            # tagged-values to run
+            if run_tag in test:
+                condition = test[run_tag]
+                if not parse(condition, **values):
+                    reason = '%s: %s' % (run_tag, condition)
+
+            # tagged-values to skip
+            if skip_tag in test:
+                condition = test[skip_tag]
+                if parse(condition, **values):
+                    reason = '%s: %s' % (skip_tag, condition)
+
+            # mark test as disabled if there's a reason
+            if reason:
+                test.setdefault('disabled', reason)
+
+            # mark test as a fail if so indicated
+            if fail_tag in test:
+                condition = test[fail_tag]
+                if parse(condition, **values):
+                    test['expected'] = 'fail'
+
+    def active_tests(self, exists=True, disabled=True, **values):
+        """
+        - exists : return only existing tests
+        - disabled : whether to return disabled tests
+        - tags : keys and values to filter on (e.g. `os = linux mac`)
+        """
+
+        tests = [i.copy() for i in self.tests] # shallow copy
+
+        # mark all tests as passing unless indicated otherwise
+        for test in tests:
+            test['expected'] = test.get('expected', 'pass')
+
+        # ignore tests that do not exist
+        if exists:
+            tests = [test for test in tests if os.path.exists(test['path'])]
+
+        # filter by tags
+        self.filter(values, tests)
+
+        # ignore disabled tests if specified
+        if not disabled:
+            tests = [test for test in tests
+                     if not 'disabled' in test]
+
+        # return active tests
+        return tests
+
+    def test_paths(self):
+        return [test['path'] for test in self.active_tests()]
+
+
+### utility function(s); probably belongs elsewhere
+
+def convert(directories, pattern=None, ignore=(), write=None):
+    """
+    convert directories to a simple manifest
+    """
+
+    retval = []
+    include = []
+    for directory in directories:
+        for dirpath, dirnames, filenames in os.walk(directory):
+
+            # filter out directory names
+            dirnames = [ i for i in dirnames if i not in ignore ]
+            dirnames.sort()
+
+            # reference only the subdirectory
+            _dirpath = dirpath
+            dirpath = dirpath.split(directory, 1)[-1].strip(os.path.sep)
+
+            if dirpath.split(os.path.sep)[0] in ignore:
+                continue
+
+            # filter by glob
+            if pattern:
+                filenames = [filename for filename in filenames
+                             if fnmatch(filename, pattern)]
+
+            filenames.sort()
+
+            # write a manifest for each directory
+            if write and (dirnames or filenames):
+                manifest = file(os.path.join(_dirpath, write), 'w')
+                for dirname in dirnames:
+                    print >> manifest, '[include:%s]' % os.path.join(dirname, write)
+                for filename in filenames:
+                    print >> manifest, '[%s]' % filename
+                manifest.close()
+
+            # add to the list
+            retval.extend([denormalize_path(os.path.join(dirpath, filename))
+                           for filename in filenames])
+
+    if write:
+        return # the manifests have already been written!
+
+    retval.sort()
+    retval = ['[%s]' % filename for filename in retval]
+    return '\n'.join(retval)
+
+### command line attributes
+
+class ParserError(Exception):
+  """error for exceptions while parsing the command line"""
+
+def parse_args(_args):
+    """
+    parse and return:
+    --keys=value (or --key value)
+    -tags
+    args
+    """
+
+    # return values
+    _dict = {}
+    tags = []
+    args = []
+
+    # parse the arguments
+    key = None
+    for arg in _args:
+        if arg.startswith('---'):
+            raise ParserError("arguments should start with '-' or '--' only")
+        elif arg.startswith('--'):
+            if key:
+                raise ParserError("Key %s still open" % key)
+            key = arg[2:]
+            if '=' in key:
+                key, value = key.split('=', 1)
+                _dict[key] = value
+                key = None
+                continue
+        elif arg.startswith('-'):
+            if key:
+                raise ParserError("Key %s still open" % key)
+            tags.append(arg[1:])
+            continue
+        else:
+            if key:
+                _dict[key] = arg
+                continue
+            args.append(arg)
+
+    # return values
+    return (_dict, tags, args)
+
+
+### classes for subcommands
+
+class CLICommand(object):
+    usage = '%prog [options] command'
+    def __init__(self, parser):
+      self._parser = parser # master parser
+    def parser(self):
+      return OptionParser(usage=self.usage, description=self.__doc__,
+                          add_help_option=False)
+
+class Copy(CLICommand):
+    usage = '%prog [options] copy manifest directory -tag1 -tag2 --key1=value1 --key2=value2 ...'
+    def __call__(self, options, args):
+      # parse the arguments
+      try:
+        kwargs, tags, args = parse_args(args)
+      except ParserError, e:
+        self._parser.error(e.message)
+
+      # make sure we have some manifests, otherwise it will
+      # be quite boring
+      if not len(args) == 2:
+        HelpCLI(self._parser)(options, ['copy'])
+        return
+
+      # read the manifests
+      # TODO: should probably ensure these exist here
+      manifests = ManifestParser()
+      manifests.read(args[0])
+
+      # print the resultant query
+      manifests.copy(args[1], None, *tags, **kwargs)
+
+
+class CreateCLI(CLICommand):
+    """
+    create a manifest from a list of directories
+    """
+    usage = '%prog [options] create directory <directory> <...>'
+
+    def parser(self):
+        parser = CLICommand.parser(self)
+        parser.add_option('-p', '--pattern', dest='pattern',
+                          help="glob pattern for files")
+        parser.add_option('-i', '--ignore', dest='ignore',
+                          default=[], action='append',
+                          help='directories to ignore')
+        parser.add_option('-w', '--in-place', dest='in_place',
+                          help='Write .ini files in place; filename to write to')
+        return parser
+
+    def __call__(self, _options, args):
+        parser = self.parser()
+        options, args = parser.parse_args(args)
+
+        # need some directories
+        if not len(args):
+            parser.print_usage()
+            return
+
+        # add the directories to the manifest
+        for arg in args:
+            assert os.path.exists(arg)
+            assert os.path.isdir(arg)
+            manifest = convert(args, pattern=options.pattern, ignore=options.ignore,
+                               write=options.in_place)
+        if manifest:
+            print manifest
+
+
+class WriteCLI(CLICommand):
+    """
+    write a manifest based on a query
+    """
+    usage = '%prog [options] write manifest <manifest> -tag1 -tag2 --key1=value1 --key2=value2 ...'
+    def __call__(self, options, args):
+
+        # parse the arguments
+        try:
+            kwargs, tags, args = parse_args(args)
+        except ParserError, e:
+            self._parser.error(e.message)
+
+        # make sure we have some manifests, otherwise it will
+        # be quite boring
+        if not args:
+            HelpCLI(self._parser)(options, ['write'])
+            return
+
+        # read the manifests
+        # TODO: should probably ensure these exist here
+        manifests = ManifestParser()
+        manifests.read(*args)
+
+        # print the resultant query
+        manifests.write(global_tags=tags, global_kwargs=kwargs)
+
+
+class HelpCLI(CLICommand):
+    """
+    get help on a command
+    """
+    usage = '%prog [options] help [command]'
+
+    def __call__(self, options, args):
+        if len(args) == 1 and args[0] in commands:
+            commands[args[0]](self._parser).parser().print_help()
+        else:
+            self._parser.print_help()
+            print '\nCommands:'
+            for command in sorted(commands):
+                print '  %s : %s' % (command, commands[command].__doc__.strip())
+
+class SetupCLI(CLICommand):
+    """
+    setup using setuptools
+    """
+    # use setup.py from the repo when you want to distribute to python!
+    # otherwise setuptools will complain that it can't find setup.py
+    # and result in a useless package
+
+    usage = '%prog [options] setup [setuptools options]'
+
+    def __call__(self, options, args):
+        sys.argv = [sys.argv[0]] + args
+        assert setup is not None, "You must have setuptools installed to use SetupCLI"
+        here = os.path.dirname(os.path.abspath(__file__))
+        try:
+            filename = os.path.join(here, 'README.txt')
+            description = file(filename).read()
+        except:
+            description = ''
+        os.chdir(here)
+
+        setup(name='ManifestDestiny',
+              version=version,
+              description="Universal manifests for Mozilla test harnesses",
+              long_description=description,
+              classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
+              keywords='mozilla manifests',
+              author='Jeff Hammel',
+              author_email='jhammel@mozilla.com',
+              url='https://wiki.mozilla.org/Auto-tools/Projects/ManifestDestiny',
+              license='MPL',
+              zip_safe=False,
+              py_modules=['manifestparser'],
+              install_requires=[
+                  # -*- Extra requirements: -*-
+                  ],
+              entry_points="""
+              [console_scripts]
+              manifestparser = manifestparser:main
+              """,
+              )
+
+
+class UpdateCLI(CLICommand):
+    """
+    update the tests as listed in a manifest from a directory
+    """
+    usage = '%prog [options] update manifest directory -tag1 -tag2 --key1=value1 --key2=value2 ...'
+
+    def __call__(self, options, args):
+        # parse the arguments
+        try:
+            kwargs, tags, args = parse_args(args)
+        except ParserError, e:
+            self._parser.error(e.message)
+
+        # make sure we have some manifests, otherwise it will
+        # be quite boring
+        if not len(args) == 2:
+            HelpCLI(self._parser)(options, ['update'])
+            return
+
+        # read the manifests
+        # TODO: should probably ensure these exist here
+        manifests = ManifestParser()
+        manifests.read(args[0])
+
+        # print the resultant query
+        manifests.update(args[1], None, *tags, **kwargs)
+
+
+# command -> class mapping
+commands = { 'create': CreateCLI,
+             'help': HelpCLI,
+             'update': UpdateCLI,
+             'write': WriteCLI }
+if setup is not None:
+    commands['setup'] = SetupCLI
+
+def main(args=sys.argv[1:]):
+    """console_script entry point"""
+
+    # set up an option parser
+    usage = '%prog [options] [command] ...'
+    description = __doc__
+    parser = OptionParser(usage=usage, description=description)
+    parser.add_option('-s', '--strict', dest='strict',
+                      action='store_true', default=False,
+                      help='adhere strictly to errors')
+    parser.disable_interspersed_args()
+
+    options, args = parser.parse_args(args)
+
+    if not args:
+        HelpCLI(parser)(options, args)
+        parser.exit()
+
+    # get the command
+    command = args[0]
+    if command not in commands:
+        parser.error("Command must be one of %s (you gave '%s')" % (', '.join(sorted(commands.keys())), command))
+
+    handler = commands[command](parser)
+    handler(options, args[1:])
+
+if __name__ == '__main__':
+    main()
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/manifestdestiny/setup.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (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.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is manifestdestiny.
+#
+# The Initial Developer of the Original Code is
+#  The Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#     Jeff Hammel <jhammel@mozilla.com>     (Original author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+# The real details are in manifestparser.py; this is just a front-end
+# BUT use this file when you want to distribute to python!
+# otherwise setuptools will complain that it can't find setup.py
+# and result in a useless package
+
+import sys
+from manifestparser import SetupCLI
+SetupCLI(None)(None, sys.argv[1:])
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/manifestdestiny/tests/filter-example.ini
@@ -0,0 +1,11 @@
+# illustrate test filters based on various categories
+
+[windowstest]
+run-if = os == 'win'
+
+[fleem]
+skip-if = os == 'mac'
+
+[linuxtest]
+skip-if = (os == 'mac') || (os == 'win')
+fail-if = toolkit == 'cocoa'
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/manifestdestiny/tests/fleem
@@ -0,0 +1,1 @@
+# dummy spot for "fleem" test
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/manifestdestiny/tests/include-example.ini
@@ -0,0 +1,11 @@
+[DEFAULT]
+foo = bar
+
+[include:include/bar.ini]
+
+[fleem]
+
+[include:include/foo.ini]
+red = roses
+blue = violets
+yellow = daffodils
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/manifestdestiny/tests/include/bar.ini
@@ -0,0 +1,4 @@
+[DEFAULT]
+foo = fleem
+
+[crash-handling]
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/manifestdestiny/tests/include/crash-handling
@@ -0,0 +1,1 @@
+# dummy spot for "crash-handling" test
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/manifestdestiny/tests/include/flowers
@@ -0,0 +1,1 @@
+# dummy spot for "flowers" test
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/manifestdestiny/tests/include/foo.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+blue = ocean
+
+[flowers]
+yellow = submarine
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/manifestdestiny/tests/mozmill-example.ini
@@ -0,0 +1,80 @@
+[testAddons/testDisableEnablePlugin.js]
+[testAddons/testGetAddons.js]
+[testAddons/testSearchAddons.js]
+[testAwesomeBar/testAccessLocationBar.js]
+[testAwesomeBar/testCheckItemHighlight.js]
+[testAwesomeBar/testEscapeAutocomplete.js]
+[testAwesomeBar/testFaviconInAutocomplete.js]
+[testAwesomeBar/testGoButton.js]
+[testAwesomeBar/testLocationBarSearches.js]
+[testAwesomeBar/testPasteLocationBar.js]
+[testAwesomeBar/testSuggestHistoryBookmarks.js]
+[testAwesomeBar/testVisibleItemsMax.js]
+[testBookmarks/testAddBookmarkToMenu.js]
+[testCookies/testDisableCookies.js]
+[testCookies/testEnableCookies.js]
+[testCookies/testRemoveAllCookies.js]
+[testCookies/testRemoveCookie.js]
+[testDownloading/testCloseDownloadManager.js]
+[testDownloading/testDownloadStates.js]
+[testDownloading/testOpenDownloadManager.js]
+[testFindInPage/testFindInPage.js]
+[testFormManager/testAutoCompleteOff.js]
+[testFormManager/testBasicFormCompletion.js]
+[testFormManager/testClearFormHistory.js]
+[testFormManager/testDisableFormManager.js]
+[testGeneral/testGoogleSuggestions.js]
+[testGeneral/testStopReloadButtons.js]
+[testInstallation/testBreakpadInstalled.js]
+[testLayout/testNavigateFTP.js]
+[testPasswordManager/testPasswordNotSaved.js]
+[testPasswordManager/testPasswordSavedAndDeleted.js]
+[testPopups/testPopupsAllowed.js]
+[testPopups/testPopupsBlocked.js]
+[testPreferences/testPaneRetention.js]
+[testPreferences/testPreferredLanguage.js]
+[testPreferences/testRestoreHomepageToDefault.js]
+[testPreferences/testSetToCurrentPage.js]
+[testPreferences/testSwitchPanes.js]
+[testPrivateBrowsing/testAboutPrivateBrowsing.js]
+[testPrivateBrowsing/testCloseWindow.js]
+[testPrivateBrowsing/testDisabledElements.js]
+[testPrivateBrowsing/testDisabledPermissions.js]
+[testPrivateBrowsing/testDownloadManagerClosed.js]
+[testPrivateBrowsing/testGeolocation.js]
+[testPrivateBrowsing/testStartStopPBMode.js]
+[testPrivateBrowsing/testTabRestoration.js]
+[testPrivateBrowsing/testTabsDismissedOnStop.js]
+[testSearch/testAddMozSearchProvider.js]
+[testSearch/testFocusAndSearch.js]
+[testSearch/testGetMoreSearchEngines.js]
+[testSearch/testOpenSearchAutodiscovery.js]
+[testSearch/testRemoveSearchEngine.js]
+[testSearch/testReorderSearchEngines.js]
+[testSearch/testRestoreDefaults.js]
+[testSearch/testSearchSelection.js]
+[testSearch/testSearchSuggestions.js]
+[testSecurity/testBlueLarry.js]
+[testSecurity/testDefaultPhishingEnabled.js]
+[testSecurity/testDefaultSecurityPrefs.js]
+[testSecurity/testEncryptedPageWarning.js]
+[testSecurity/testGreenLarry.js]
+[testSecurity/testGreyLarry.js]
+[testSecurity/testIdentityPopupOpenClose.js]
+[testSecurity/testSSLDisabledErrorPage.js]
+[testSecurity/testSafeBrowsingNotificationBar.js]
+[testSecurity/testSafeBrowsingWarningPages.js]
+[testSecurity/testSecurityInfoViaMoreInformation.js]
+[testSecurity/testSecurityNotification.js]
+[testSecurity/testSubmitUnencryptedInfoWarning.js]
+[testSecurity/testUnknownIssuer.js]
+[testSecurity/testUntrustedConnectionErrorPage.js]
+[testSessionStore/testUndoTabFromContextMenu.js]
+[testTabbedBrowsing/testBackgroundTabScrolling.js]
+[testTabbedBrowsing/testCloseTab.js]
+[testTabbedBrowsing/testNewTab.js]
+[testTabbedBrowsing/testNewWindow.js]
+[testTabbedBrowsing/testOpenInBackground.js]
+[testTabbedBrowsing/testOpenInForeground.js]
+[testTechnicalTools/testAccessPageInfoDialog.js]
+[testToolbar/testBackForwardButtons.js]
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/manifestdestiny/tests/mozmill-restart-example.ini
@@ -0,0 +1,26 @@
+[DEFAULT]
+type = restart
+
+[restartTests/testExtensionInstallUninstall/test2.js]
+foo = bar
+
+[restartTests/testExtensionInstallUninstall/test1.js]
+foo = baz
+
+[restartTests/testExtensionInstallUninstall/test3.js]
+[restartTests/testSoftwareUpdateAutoProxy/test2.js]
+[restartTests/testSoftwareUpdateAutoProxy/test1.js]
+[restartTests/testMasterPassword/test1.js]
+[restartTests/testExtensionInstallGetAddons/test2.js]
+[restartTests/testExtensionInstallGetAddons/test1.js]
+[restartTests/testMultipleExtensionInstallation/test2.js]
+[restartTests/testMultipleExtensionInstallation/test1.js]
+[restartTests/testThemeInstallUninstall/test2.js]
+[restartTests/testThemeInstallUninstall/test1.js]
+[restartTests/testThemeInstallUninstall/test3.js]
+[restartTests/testDefaultBookmarks/test1.js]
+[softwareUpdate/testFallbackUpdate/test2.js]
+[softwareUpdate/testFallbackUpdate/test1.js]
+[softwareUpdate/testFallbackUpdate/test3.js]
+[softwareUpdate/testDirectUpdate/test2.js]
+[softwareUpdate/testDirectUpdate/test1.js]
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/manifestdestiny/tests/path-example.ini
@@ -0,0 +1,2 @@
+[foo]
+path = fleem
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/manifestdestiny/tests/test.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+# 
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (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.mozilla.org/MPL/
+# 
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+# 
+# The Original Code is mozilla.org code.
+# 
+# The Initial Developer of the Original Code is
+# Mozilla.org.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+# 
+# Contributor(s):
+#     Jeff Hammel <jhammel@mozilla.com>     (Original author)
+# 
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+# 
+# ***** END LICENSE BLOCK *****
+
+"""tests for ManifestDestiny"""
+
+import doctest
+import os
+import sys
+from optparse import OptionParser
+
+def run_tests(raise_on_error=False, report_first=False):
+
+    # add results here
+    results = {}
+
+    # doctest arguments
+    directory = os.path.dirname(os.path.abspath(__file__))
+    extraglobs = {}
+    doctest_args = dict(extraglobs=extraglobs,
+                        module_relative=False,
+                        raise_on_error=raise_on_error)
+    if report_first:
+        doctest_args['optionflags'] = doctest.REPORT_ONLY_FIRST_FAILURE
+                                
+    # gather tests
+    directory = os.path.dirname(os.path.abspath(__file__))
+    tests =  [ test for test in os.listdir(directory)
+               if test.endswith('.txt') and test.startswith('test_')]
+    os.chdir(directory)
+
+    # run the tests
+    for test in tests:
+        try:
+            results[test] = doctest.testfile(test, **doctest_args)
+        except doctest.DocTestFailure, failure:
+            raise
+        except doctest.UnexpectedException, failure:
+            raise failure.exc_info[0], failure.exc_info[1], failure.exc_info[2]
+        
+    return results
+                                
+
+def main(args=sys.argv[1:]):
+
+    # parse command line options
+    parser = OptionParser(description=__doc__)
+    parser.add_option('--raise', dest='raise_on_error',
+                      default=False, action='store_true',
+                      help="raise on first error")
+    parser.add_option('--report-first', dest='report_first',
+                      default=False, action='store_true',
+                      help="report the first error only (all tests will still run)")
+    parser.add_option('-q', '--quiet', dest='quiet',
+                      default=False, action='store_true',
+                      help="minimize output")
+    options, args = parser.parse_args(args)
+    quiet = options.__dict__.pop('quiet')
+
+    # run the tests
+    results = run_tests(**options.__dict__)
+
+    # check for failure
+    failed = False
+    for result in results.values():
+        if result[0]: # failure count; http://docs.python.org/library/doctest.html#basic-api
+            failed = True
+            break
+    if failed:
+        sys.exit(1) # error
+    if not quiet:
+        # print results
+        print "manifestparser.py: All tests pass!"
+        for test in sorted(results.keys()):
+            result = results[test]
+            print "%s: failed=%s, attempted=%s" % (test, result[0], result[1])
+               
+if __name__ == '__main__':
+    main()
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/manifestdestiny/tests/test_expressionparser.txt
@@ -0,0 +1,120 @@
+Test Expressionparser
+=====================
+
+Test the conditional expression parser.
+
+Boilerplate::
+
+    >>> from manifestparser import parse
+
+Test basic values::
+
+    >>> parse("1")
+    1
+    >>> parse("100")
+    100
+    >>> parse("true")
+    True
+    >>> parse("false")
+    False
+    >>> '' == parse('""')
+    True
+    >>> parse('"foo bar"')
+    'foo bar'
+    >>> parse("'foo bar'")
+    'foo bar'
+    >>> parse("foo", foo=1)
+    1
+    >>> parse("bar", bar=True)
+    True
+    >>> parse("abc123", abc123="xyz")
+    'xyz'
+
+Test equality::
+
+    >>> parse("true == true")
+    True
+    >>> parse("false == false")
+    True
+    >>> parse("false == false")
+    True
+    >>> parse("1 == 1")
+    True
+    >>> parse("100 == 100")
+    True
+    >>> parse('"some text" == "some text"')
+    True
+    >>> parse("true != false")
+    True
+    >>> parse("1 != 2")
+    True
+    >>> parse('"text" != "other text"')
+    True
+    >>> parse("foo == true", foo=True)
+    True
+    >>> parse("foo == 1", foo=1)
+    True
+    >>> parse('foo == "bar"', foo='bar')
+    True
+    >>> parse("foo == bar", foo=True, bar=True)
+    True
+    >>> parse("true == foo", foo=True)
+    True
+    >>> parse("foo != true", foo=False)
+    True
+    >>> parse("foo != 2", foo=1)
+    True
+    >>> parse('foo != "bar"', foo='abc')
+    True
+    >>> parse("foo != bar", foo=True, bar=False)
+    True
+    >>> parse("true != foo", foo=False)
+    True
+    >>> parse("!false")
+    True
+
+Test conjunctions::
+    
+    >>> parse("true && true")
+    True
+    >>> parse("true || false")
+    True
+    >>> parse("false || false")
+    False
+    >>> parse("true && false")
+    False
+    >>> parse("true || false && false")
+    True
+
+Test parentheses::
+    
+    >>> parse("(true)")
+    True
+    >>> parse("(10)")
+    10
+    >>> parse('("foo")')
+    'foo'
+    >>> parse("(foo)", foo=1)
+    1
+    >>> parse("(true == true)")
+    True
+    >>> parse("(true != false)")
+    True
+    >>> parse("(true && true)")
+    True
+    >>> parse("(true || false)")
+    True
+    >>> parse("(true && true || false)")
+    True
+    >>> parse("(true || false) && false")
+    False
+    >>> parse("(true || false) && true")
+    True
+    >>> parse("true && (true || false)")
+    True
+    >>> parse("true && (true || false)")
+    True
+    >>> parse("(true && false) || (true && (true || false))")
+    True
+        
+
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/manifestdestiny/tests/test_manifestparser.txt
@@ -0,0 +1,217 @@
+Test the manifest parser
+========================
+
+You must have ManifestDestiny installed before running these tests.
+Run ``python manifestparser.py setup develop`` with setuptools installed.
+
+Ensure basic parser is sane::
+
+    >>> from manifestparser import ManifestParser
+    >>> parser = ManifestParser()
+    >>> parser.read('mozmill-example.ini')
+    >>> tests = parser.tests
+    >>> len(tests) == len(file('mozmill-example.ini').read().strip().splitlines())
+    True
+    
+Ensure that capitalization and order aren't an issue:
+
+    >>> lines = ['[%s]' % test['name'] for test in tests]
+    >>> lines == file('mozmill-example.ini').read().strip().splitlines()
+    True
+
+Show how you select subsets of tests:
+
+    >>> parser.read('mozmill-restart-example.ini')
+    >>> restart_tests = parser.get(type='restart')
+    >>> len(restart_tests) < len(parser.tests)
+    True
+    >>> import os
+    >>> len(restart_tests) == len(parser.get(manifest=os.path.abspath('mozmill-restart-example.ini')))
+    True
+    >>> assert not [test for test in restart_tests if test['manifest'] != os.path.abspath('mozmill-restart-example.ini')]
+    >>> parser.get('name', tags=['foo'])
+    ['restartTests/testExtensionInstallUninstall/test2.js', 'restartTests/testExtensionInstallUninstall/test1.js']
+    >>> parser.get('name', foo='bar')
+    ['restartTests/testExtensionInstallUninstall/test2.js']
+
+Illustrate how include works::
+
+    >>> parser = ManifestParser(manifests=('include-example.ini',))
+
+All of the tests should be included, in order::
+
+    >>> parser.get('name')
+    ['crash-handling', 'fleem', 'flowers']
+    >>> [(test['name'], os.path.basename(test['manifest'])) for test in parser.tests]
+    [('crash-handling', 'bar.ini'), ('fleem', 'include-example.ini'), ('flowers', 'foo.ini')]
+
+The manifests should be there too::
+
+    >>> len(parser.manifests())
+    3
+
+We're already in the root directory::
+
+    >>> os.getcwd() == parser.rootdir
+    True
+
+DEFAULT values should persist across includes, unless they're
+overwritten.  In this example, include-example.ini sets foo=bar, but
+its overridden to fleem in bar.ini::
+
+    >>> parser.get('name', foo='bar')
+    ['fleem', 'flowers']
+    >>> parser.get('name', foo='fleem')
+    ['crash-handling']
+
+Passing parameters in the include section allows defining variables in
+the submodule scope:
+
+    >>> parser.get('name', tags=['red'])
+    ['flowers']
+
+However, this should be overridable from the DEFAULT section in the
+included file and that overridable via the key directly connected to
+the test::
+
+    >>> parser.get(name='flowers')[0]['blue']
+    'ocean'
+    >>> parser.get(name='flowers')[0]['yellow']
+    'submarine'
+
+You can query multiple times if you need to::
+
+    >>> flowers = parser.get(foo='bar')
+    >>> len(flowers)
+    2
+    >>> roses = parser.get(tests=flowers, red='roses')
+
+Using the inverse flag should invert the set of tests returned::
+
+    >>> parser.get('name', inverse=True, tags=['red'])
+    ['crash-handling', 'fleem']
+
+All of the included tests actually exist::
+
+    >>> [i['name'] for i in parser.missing()]
+    []
+
+Write the output to a manifest:
+
+    >>> from StringIO import StringIO
+    >>> buffer = StringIO()
+    >>> parser.write(fp=buffer, global_kwargs={'foo': 'bar'})
+    >>> buffer.getvalue().strip()
+    '[DEFAULT]\nfoo = bar\n\n[fleem]\n\n[include/flowers]\nblue = ocean\nred = roses\nyellow = submarine'
+
+Test our ability to convert a static directory structure to a
+manifest. First, stub out a directory with files in it::
+
+    >>> import shutil, tempfile
+    >>> def create_stub():
+    ...     directory = tempfile.mkdtemp()
+    ...     for i in 'foo', 'bar', 'fleem':
+    ...         file(os.path.join(directory, i), 'w').write(i)
+    ...     subdir = os.path.join(directory, 'subdir')
+    ...     os.mkdir(subdir)
+    ...     file(os.path.join(subdir, 'subfile'), 'w').write('baz')
+    ...     return directory
+    >>> stub = create_stub()
+    >>> os.path.exists(stub) and os.path.isdir(stub)
+    True
+
+Make a manifest for it::
+
+    >>> from manifestparser import convert
+    >>> print convert([stub])
+    [bar]
+    [fleem]
+    [foo]
+    [subdir/subfile]
+    >>> shutil.rmtree(stub)
+
+Now do the same thing but keep the manifests in place::
+
+    >>> stub = create_stub()
+    >>> convert([stub], write='manifest.ini')
+    >>> sorted(os.listdir(stub))
+    ['bar', 'fleem', 'foo', 'manifest.ini', 'subdir']
+    >>> parser = ManifestParser()
+    >>> parser.read(os.path.join(stub, 'manifest.ini'))
+    >>> [i['name'] for i in parser.tests]
+    ['subfile', 'bar', 'fleem', 'foo']
+    >>> parser = ManifestParser()
+    >>> parser.read(os.path.join(stub, 'subdir', 'manifest.ini'))
+    >>> len(parser.tests)
+    1
+    >>> parser.tests[0]['name']
+    'subfile'
+    >>> shutil.rmtree(stub)
+
+Test our ability to copy a set of manifests::
+
+    >>> tempdir = tempfile.mkdtemp()
+    >>> manifest = ManifestParser(manifests=('include-example.ini',))
+    >>> manifest.copy(tempdir)
+    >>> sorted(os.listdir(tempdir))
+    ['fleem', 'include', 'include-example.ini']
+    >>> sorted(os.listdir(os.path.join(tempdir, 'include')))
+    ['bar.ini', 'crash-handling', 'flowers', 'foo.ini']
+    >>> from_manifest = ManifestParser(manifests=('include-example.ini',))
+    >>> to_manifest = os.path.join(tempdir, 'include-example.ini')
+    >>> to_manifest = ManifestParser(manifests=(to_manifest,))
+    >>> to_manifest.get('name') == from_manifest.get('name')
+    True
+    >>> shutil.rmtree(tempdir)
+
+Test our ability to update tests from a manifest and a directory of
+files::
+
+    >>> tempdir = tempfile.mkdtemp()
+    >>> for i in range(10):
+    ...     file(os.path.join(tempdir, str(i)), 'w').write(str(i))
+
+First, make a manifest::
+
+    >>> manifest = convert([tempdir])
+    >>> newtempdir = tempfile.mkdtemp()
+    >>> manifest_file = os.path.join(newtempdir, 'manifest.ini')
+    >>> file(manifest_file,'w').write(manifest)
+    >>> manifest = ManifestParser(manifests=(manifest_file,))
+    >>> manifest.get('name') == [str(i) for i in range(10)]
+    True
+
+All of the tests are initially missing::
+
+    >>> [i['name'] for i in manifest.missing()] == [str(i) for i in range(10)]
+    True
+
+But then we copy one over::
+
+    >>> manifest.get('name', name='1')
+    ['1']
+    >>> manifest.update(tempdir, name='1')
+    >>> sorted(os.listdir(newtempdir))
+    ['1', 'manifest.ini']
+
+Update that one file and copy all the "tests"::
+   
+    >>> file(os.path.join(tempdir, '1'), 'w').write('secret door')
+    >>> manifest.update(tempdir)
+    >>> sorted(os.listdir(newtempdir))
+    ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'manifest.ini']
+    >>> file(os.path.join(newtempdir, '1')).read().strip()
+    'secret door'
+
+Clean up::
+
+    >>> shutil.rmtree(tempdir)
+    >>> shutil.rmtree(newtempdir)
+
+You can override the path in the section too.  This shows that you can
+use a relative path::
+
+    >>> manifest = ManifestParser(manifests=('path-example.ini',))
+    >>> manifest.tests[0]['path'] == os.path.abspath('fleem')
+    True
+
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/manifestdestiny/tests/test_testmanifest.txt
@@ -0,0 +1,32 @@
+Test the Test Manifest
+======================
+
+Boilerplate::
+
+    >>> import os
+
+Test filtering based on platform::
+
+    >>> from manifestparser import TestManifest
+    >>> manifest = TestManifest(manifests=('filter-example.ini',))
+    >>> [i['name'] for i in manifest.active_tests(os='win', disabled=False, exists=False)]
+    ['windowstest', 'fleem']
+    >>> [i['name'] for i in manifest.active_tests(os='linux', disabled=False, exists=False)]
+    ['fleem', 'linuxtest']
+
+Look for existing tests.  There is only one::
+
+    >>> [i['name'] for i in manifest.active_tests()]
+    ['fleem']
+
+You should be able to expect failures::
+
+    >>> last_test = manifest.active_tests(exists=False, toolkit='gtk2')[-1]
+    >>> last_test['name']
+    'linuxtest'
+    >>> last_test['expected']
+    'pass'
+    >>> last_test = manifest.active_tests(exists=False, toolkit='cocoa')[-1]
+    >>> last_test['expected']
+    'fail'
+
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozhttpd/README.md
@@ -0,0 +1,1 @@
+basic python webserver, tested with talos
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozhttpd/mozhttpd.py
@@ -0,0 +1,172 @@
+#!/usr/bin/env python
+
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (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.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# the Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Joel Maher <joel.maher@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+import BaseHTTPServer
+import SimpleHTTPServer
+import threading
+import sys
+import os
+import urllib
+import re
+from SocketServer import ThreadingMixIn
+
+class EasyServer(ThreadingMixIn, BaseHTTPServer.HTTPServer):
+    allow_reuse_address = True
+    
+class MozRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+    docroot = os.getcwd()
+
+    def parse_request(self):
+        retval = SimpleHTTPServer.SimpleHTTPRequestHandler.parse_request(self)
+        if '?' in self.path:
+            # ignore query string, otherwise SimpleHTTPRequestHandler 
+            # will treat it as PATH_INFO for `translate_path`
+            self.path = self.path.split('?', 1)[0]
+        return retval
+
+    def translate_path(self, path):
+        path = path.strip('/').split()
+        if path == ['']:
+            path = []
+        path.insert(0, self.docroot)
+        return os.path.join(*path)
+
+    # I found on my local network that calls to this were timing out
+    # I believe all of these calls are from log_message
+    def address_string(self):
+        return "a.b.c.d"
+
+    # This produces a LOT of noise
+    def log_message(self, format, *args):
+        pass
+
+class MozHttpd(object):
+
+    def __init__(self, host="127.0.0.1", port=8888, docroot=os.getcwd()):
+        self.host = host
+        self.port = int(port)
+        self.docroot = docroot
+        self.httpd = None
+
+    def start(self, block=False):
+        """
+        start the server.  If block is True, the call will not return.
+        If block is False, the server will be started on a separate thread that
+        can be terminated by a call to .stop()
+        """
+
+        class MozRequestHandlerInstance(MozRequestHandler):
+            docroot = self.docroot
+
+        self.httpd = EasyServer((self.host, self.port), MozRequestHandlerInstance)
+        if block:
+            self.httpd.serve_forever()
+        else:
+            self.server = threading.Thread(target=self.httpd.serve_forever)
+            self.server.setDaemon(True) # don't hang on exit
+            self.server.start()
+        
+    def testServer(self):
+        fileList = os.listdir(self.docroot)
+        filehandle = urllib.urlopen('http://%s:%s/?foo=bar&fleem=&foo=fleem' % (self.host, self.port))
+        data = filehandle.readlines()
+        filehandle.close()
+
+        retval = True
+
+        for line in data:
+            found = False
+            # '@' denotes a symlink and we need to ignore it.
+            webline = re.sub('\<[a-zA-Z0-9\-\_\.\=\"\'\/\\\%\!\@\#\$\^\&\*\(\) ]*\>', '', line.strip('\n')).strip('/').strip().strip('@')
+            if webline != "":
+                if webline == "Directory listing for":
+                    found = True
+                else:
+                    for fileName in fileList:
+                        if fileName == webline:
+                            found = True
+                
+                if not found:
+                    retval = False
+                    print >> sys.stderr, "NOT FOUND: " + webline.strip()
+        return retval
+
+    def stop(self):
+        if self.httpd:
+            self.httpd.shutdown()
+        self.httpd = None
+
+    __del__ = stop
+
+
+def main(args=sys.argv[1:]):
+    
+    # parse command line options
+    from optparse import OptionParser
+    parser = OptionParser()
+    parser.add_option('-p', '--port', dest='port', 
+                      type="int", default=8888,
+                      help="port to run the server on [DEFAULT: %default]")
+    parser.add_option('-H', '--host', dest='host',
+                      default='127.0.0.1',
+                      help="host [DEFAULT: %default]")
+    parser.add_option('-d', '--docroot', dest='docroot',
+                      default=os.getcwd(),
+                      help="directory to serve files from [DEFAULT: %default]")
+    parser.add_option('--test', dest='test',
+                      action='store_true', default=False,
+                      help='run the tests and exit')
+    options, args = parser.parse_args(args)
+    if args:
+        parser.print_help()
+        parser.exit()
+
+    # create the server
+    kwargs = options.__dict__.copy()
+    test = kwargs.pop('test')
+    server = MozHttpd(**kwargs)
+
+    if test:
+        server.start()
+        server.testServer()
+    else:
+        server.start(block=True)
+
+if __name__ == '__main__':
+    main()
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozhttpd/setup.py
@@ -0,0 +1,72 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (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.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozhttpd.
+#
+# The Initial Developer of the Original Code is
+#  The Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Joel Maher <jmaher@mozilla.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+import os
+from setuptools import setup
+
+try:
+    here = os.path.dirname(os.path.abspath(__file__))
+    description = file(os.path.join(here, 'README.md')).read()
+except IOError:
+    description = None
+
+version = '0.1'
+
+deps = []
+
+setup(name='mozhttpd',
+      version=version,
+      description="basic python webserver, tested with talos",
+      long_description=description,
+      classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
+      keywords='mozilla',
+      author='Joel Maher',
+      author_email='tools@lists.mozilla.org',
+      url='https://github.com/mozilla/mozbase/tree/master/mozhttpd',
+      license='MPL',
+      py_modules=['mozhttpd'],
+      packages=[],
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=deps,
+      entry_points="""
+      # -*- Entry points: -*-
+      [console_scripts]
+      mozhttpd = mozhttpd:main
+      """,
+      )
+
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozinfo/README.md
@@ -0,0 +1,62 @@
+Throughout [mozmill](https://developer.mozilla.org/en/Mozmill)
+and other Mozilla python code, checking the underlying
+platform is done in many different ways.  The various checks needed
+lead to a lot of copy+pasting, leaving the reader to wonder....is this
+specific check necessary for (e.g.) an operating system?  Because
+information is not consolidated, checks are not done consistently, nor
+is it defined what we are checking for.
+
+[MozInfo](https://github.com/mozilla/mozbase/tree/master/mozinfo)
+proposes to solve this problem.  MozInfo is a bridge interface,
+making the underlying (complex) plethora of OS and architecture
+combinations conform to a subset of values of relavence to 
+Mozilla software. The current implementation exposes relavent key,
+values: `os`, `version`, `bits`, and `processor`.  Additionally, the
+service pack in use is available on the windows platform.
+
+
+# API Usage
+
+MozInfo is a python package.  Downloading the software and running
+`python setup.py develop` will allow you to do `import mozinfo`
+from python.  
+[mozinfo.py](https://github.com/mozilla/mozbase/blob/master/mozinfo/mozinfo.py)
+is the only file contained is this package,
+so if you need a single-file solution, you can just download or call
+this file through the web.
+
+The top level attributes (`os`, `version`, `bits`, `processor`) are
+available as module globals:
+
+    if mozinfo.os == 'win': ...
+
+In addition, mozinfo exports a dictionary, `mozinfo.info`, that
+contain these values.  mozinfo also exports:
+
+- `choices`: a dictionary of possible values for os, bits, and
+  processor
+- `main`: the console_script entry point for mozinfo
+- `unknown`: a singleton denoting a value that cannot be determined
+
+`unknown` has the string representation `"UNKNOWN"`. unknown will evaluate
+as `False` in python:
+
+    if not mozinfo.os: ... # unknown!
+
+
+# Command Line Usage
+
+MozInfo comes with a command line, `mozinfo` which may be used to
+diagnose one's current system.
+
+Example output:
+
+    os: linux
+    version: Ubuntu 10.10
+    bits: 32
+    processor: x86
+
+Three of these fields, os, bits, and processor, have a finite set of
+choices.  You may display the value of these choices using 
+`mozinfo --os`, `mozinfo --bits`, and `mozinfo --processor`. 
+`mozinfo --help` documents command-line usage.
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozinfo/mozinfo.py
@@ -0,0 +1,208 @@
+#!/usr/bin/env python
+
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (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.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozinfo.
+#
+# The Initial Developer of the Original Code is
+#  The Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#  Jeff Hammel <jhammel@mozilla.com>
+#  Clint Talbert <ctalbert@mozilla.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+"""
+file for interface to transform introspected system information to a format
+pallatable to Mozilla
+
+Information:
+- os : what operating system ['win', 'mac', 'linux', ...]
+- bits : 32 or 64
+- processor : processor architecture ['x86', 'x86_64', 'ppc', ...]
+- version : operating system version string
+
+For windows, the service pack information is also included
+"""
+
+# TODO: it might be a good idea of adding a system name (e.g. 'Ubuntu' for
+# linux) to the information; I certainly wouldn't want anyone parsing this
+# information and having behaviour depend on it
+
+import os
+import platform
+import re
+import sys
+
+# keep a copy of the os module since updating globals overrides this
+_os = os
+
+class unknown(object):
+    """marker class for unknown information"""
+    def __nonzero__(self):
+        return False
+    def __str__(self):
+        return 'UNKNOWN'
+unknown = unknown() # singleton
+
+# get system information
+info = {'os': unknown,
+        'processor': unknown,
+        'version': unknown,
+        'bits': unknown }
+(system, node, release, version, machine, processor) = platform.uname()
+(bits, linkage) = platform.architecture()
+
+# get os information and related data
+if system in ["Microsoft", "Windows"]:
+    info['os'] = 'win'
+    # There is a Python bug on Windows to determine platform values
+    # http://bugs.python.org/issue7860
+    if "PROCESSOR_ARCHITEW6432" in os.environ:
+        processor = os.environ.get("PROCESSOR_ARCHITEW6432", processor)
+    else:
+        processor = os.environ.get('PROCESSOR_ARCHITECTURE', processor)
+        system = os.environ.get("OS", system).replace('_', ' ')
+        service_pack = os.sys.getwindowsversion()[4]
+        info['service_pack'] = service_pack
+elif system == "Linux":
+    (distro, version, codename) = platform.dist()
+    version = "%s %s" % (distro, version)
+    if not processor:
+        processor = machine
+    info['os'] = 'linux'
+elif system == "Darwin":
+    (release, versioninfo, machine) = platform.mac_ver()
+    version = "OS X %s" % release
+    info['os'] = 'mac'
+elif sys.platform in ('solaris', 'sunos5'):
+    info['os'] = 'unix'
+    version = sys.platform
+info['version'] = version # os version
+
+# processor type and bits
+if processor in ["i386", "i686"]:
+    if bits == "32bit":
+        processor = "x86"
+    elif bits == "64bit":
+        processor = "x86_64"
+elif processor == "AMD64":
+    bits = "64bit"
+    processor = "x86_64"
+elif processor == "Power Macintosh":
+    processor = "ppc"
+bits = re.search('(\d+)bit', bits).group(1)
+info.update({'processor': processor,
+             'bits': int(bits),
+            })
+
+# standard value of choices, for easy inspection
+choices = {'os': ['linux', 'win', 'mac', 'unix'],
+           'bits': [32, 64],
+           'processor': ['x86', 'x86_64', 'ppc']}
+
+
+def sanitize(info):
+    """Do some sanitization of input values, primarily
+    to handle universal Mac builds."""
+    if "processor" in info and info["processor"] == "universal-x86-x86_64":
+        # If we're running on OS X 10.6 or newer, assume 64-bit
+        if release[:4] >= "10.6": # Note this is a string comparison
+            info["processor"] = "x86_64"
+            info["bits"] = 64
+        else:
+            info["processor"] = "x86"
+            info["bits"] = 32
+
+# method for updating information
+def update(new_info):
+    """update the info"""
+    info.update(new_info)
+    sanitize(info)
+    globals().update(info)
+
+    # convenience data for os access
+    for os_name in choices['os']:
+        globals()['is' + os_name.title()] = info['os'] == os_name
+    # unix is special
+    if isLinux:
+        globals()['isUnix'] = True
+
+update({})
+
+# exports
+__all__ = info.keys()
+__all__ += ['is' + os_name.title() for os_name in choices['os']]
+__all__ += ['info', 'unknown', 'main', 'choices', 'update']
+
+
+def main(args=None):
+
+    # parse the command line
+    from optparse import OptionParser
+    parser = OptionParser(description=__doc__)
+    for key in choices:
+        parser.add_option('--%s' % key, dest=key,
+                          action='store_true', default=False,
+                          help="display choices for %s" % key)
+    options, args = parser.parse_args()
+
+    # args are JSON blobs to override info
+    if args:
+        try:
+            from json import loads
+        except ImportError:
+            try:
+                from simplejson import loads
+            except ImportError:
+                def loads(string):
+                    """*really* simple json; will not work with unicode"""
+                    return eval(string, {'true': True, 'false': False, 'null': None})
+        for arg in args:
+            if _os.path.exists(arg):
+                string = file(arg).read()
+            else:
+                string = arg
+            update(loads(string))
+
+    # print out choices if requested
+    flag = False
+    for key, value in options.__dict__.items():
+        if value is True:
+            print '%s choices: %s' % (key, ' '.join([str(choice)
+                                                     for choice in choices[key]]))
+            flag = True
+    if flag: return
+
+    # otherwise, print out all info
+    for key, value in info.items():
+        print '%s: %s' % (key, value)
+
+if __name__ == '__main__':
+    main()
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozinfo/setup.py
@@ -0,0 +1,78 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (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.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozinfo.
+#
+# The Initial Developer of the Original Code is
+#  The Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011.
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Jeff Hammel <jhammel@mozilla.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+
+import os
+from setuptools import setup
+
+version = '0.3.3'
+
+# get documentation from the README
+try:
+    here = os.path.dirname(os.path.abspath(__file__))
+    description = file(os.path.join(here, 'README.md')).read()
+except (OSError, IOError):
+    description = ''
+
+# dependencies
+deps = []
+try:
+    import json
+except ImportError:
+    deps = ['simplejson']
+
+setup(name='mozinfo',
+      version=version,
+      description="file for interface to transform introspected system information to a format pallatable to Mozilla",
+      long_description=description,
+      classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
+      keywords='mozilla',
+      author='Jeff Hammel',
+      author_email='jhammel@mozilla.com',
+      url='https://wiki.mozilla.org/Auto-tools',
+      license='MPL',
+      py_modules=['mozinfo'],
+      packages=[],
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=deps,
+      entry_points="""
+      # -*- Entry points: -*-
+      [console_scripts]
+      mozinfo = mozinfo:main
+      """,
+      )
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozinstall/README.md
@@ -0,0 +1,35 @@
+[Mozinstall](https://github.com/mozilla/mozbase/tree/master/mozinstall)
+is a python package for installing Mozilla applications on various platforms.
+
+For example, depending on the platform, Firefox can be distributed as a 
+zip, tar.bz2, exe or dmg file or cloned from a repository. Mozinstall takes the 
+hassle out of extracting and/or running these files and for convenience returns
+the full path to the application's binary in the install directory. In the case 
+that mozinstall is invoked from the command line, the binary path will be 
+printed to stdout.
+
+# Usage
+
+For command line options run mozinstall --help
+
+Mozinstall's main function is the install method
+
+    import mozinstall
+    mozinstall.install('path_to_install_file', dest='path_to_install_folder')
+
+The dest parameter defaults to the directory in which the install file is located.
+The install method accepts a third parameter called apps which tells mozinstall which 
+binary to search for. By default it will search for 'firefox', 'thunderbird' and 'fennec'
+so unless you are installing a different application, this parameter is unnecessary.
+
+# Error Handling
+
+Mozinstall throws two different types of exceptions:
+
+- mozinstall.InvalidSource is thrown when the source is not a recognized file type (zip, exe, tar.bz2, tar.gz, dmg)
+- mozinstall.InstallError is thrown when the installation fails for any reason. A traceback is provided.
+
+# Dependencies
+
+Mozinstall depends on the [mozinfo](https://github.com/mozilla/mozbase/tree/master/mozinfo) 
+package which is also found in the mozbase repository.
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozinstall/mozinstall.py
@@ -0,0 +1,209 @@
+#!/usr/bin/env python
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (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.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozinstall.
+#
+# The Initial Developer of the Original Code is
+#  The Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#  Clint Talbert <ctalbert@mozilla.com>
+#  Andrew Halberstadt <halbersa@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+from optparse import OptionParser
+import mozinfo
+import subprocess
+import zipfile
+import tarfile
+import sys
+import os
+
+_default_apps = ["firefox",
+                 "thunderbird",
+                 "fennec"]
+
+def install(src, dest=None, apps=_default_apps):
+    """
+    Installs a zip, exe, tar.gz, tar.bz2 or dmg file
+    src - the path to the install file
+    dest - the path to install to [default is os.path.dirname(src)]
+    returns - the full path to the binary in the installed folder
+              or None if the binary cannot be found
+    """
+    src = os.path.realpath(src)
+    assert(os.path.isfile(src))
+    if not dest:
+        dest = os.path.dirname(src)
+
+    trbk = None
+    try:
+        install_dir = None
+        if zipfile.is_zipfile(src) or tarfile.is_tarfile(src):
+            install_dir = _extract(src, dest)[0]
+        elif mozinfo.isMac and src.lower().endswith(".dmg"):
+            install_dir = _install_dmg(src, dest)
+        elif mozinfo.isWin and os.access(src, os.X_OK):
+            install_dir = _install_exe(src, dest)
+        else:
+            raise InvalidSource(src + " is not a recognized file type " +
+                                      "(zip, exe, tar.gz, tar.bz2 or dmg)")
+    except InvalidSource, e:
+        raise
+    except Exception, e:
+        cls, exc, trbk = sys.exc_info()
+        install_error = InstallError("Failed to install %s" % src)
+        raise install_error.__class__, install_error, trbk
+    finally:
+        # trbk won't get GC'ed due to circular reference
+        # http://docs.python.org/library/sys.html#sys.exc_info
+        del trbk
+
+    if install_dir:
+        return get_binary(install_dir, apps=apps)
+
+def get_binary(path, apps=_default_apps):
+    """
+    Finds the binary in the specified path
+    path - the path within which to search for the binary
+    returns - the full path to the binary in the folder
+              or None if the binary cannot be found
+    """
+    if mozinfo.isWin:
+        apps = [app + ".exe" for app in apps]
+    for root, dirs, files in os.walk(path):
+        for filename in files:
+            # os.access evaluates to False for some reason, so not using it
+            if filename in apps:
+                return os.path.realpath(os.path.join(root, filename))
+
+def _extract(path, extdir=None, delete=False):
+    """
+    Takes in a tar or zip file and extracts it to extdir
+    If extdir is not specified, extracts to os.path.dirname(path)
+    If delete is set to True, deletes the bundle at path
+    Returns the list of top level files that were extracted
+    """
+    if zipfile.is_zipfile(path):
+        bundle = zipfile.ZipFile(path)
+        namelist = bundle.namelist()
+    elif tarfile.is_tarfile(path):
+        bundle = tarfile.open(path)
+        namelist = bundle.getnames()
+    else:
+        return
+    if extdir is None:
+        extdir = os.path.dirname(path)
+    elif not os.path.exists(extdir):
+        os.makedirs(extdir)
+    bundle.extractall(path=extdir)
+    bundle.close()
+    if delete:
+        os.remove(path)
+    # namelist returns paths with forward slashes even in windows
+    top_level_files = [os.path.join(extdir, name) for name in namelist
+                             if len(name.rstrip('/').split('/')) == 1]
+    # namelist doesn't include folders in windows, append these to the list
+    if mozinfo.isWin:
+        for name in namelist:
+            root = name[:name.find('/')]
+            if root not in top_level_files:
+                top_level_files.append(root)
+    return top_level_files
+
+def _install_dmg(src, dest):
+    proc = subprocess.Popen("hdiutil attach " + src,
+                            shell=True,
+                            stdout=subprocess.PIPE)
+    try:
+        for data in proc.communicate()[0].split():
+            if data.find("/Volumes/") != -1:
+                appDir = data
+                break
+        for appFile in os.listdir(appDir):
+            if appFile.endswith(".app"):
+                 appName = appFile
+                 break
+        subprocess.call("cp -r " + os.path.join(appDir, appName) + " " + dest,
+                        shell=True)
+    finally:
+        subprocess.call("hdiutil detach " + appDir + " -quiet",
+                        shell=True)
+    return os.path.join(dest, appName)
+
+def _install_exe(src, dest):
+    # possibly gets around UAC in vista (still need to run as administrator)
+    os.environ['__compat_layer'] = "RunAsInvoker"
+    cmd = [src, "/S", "/D=" + os.path.realpath(dest)]
+    subprocess.call(cmd)
+    return dest
+
+def cli(argv=sys.argv[1:]):
+    parser = OptionParser()
+    parser.add_option("-s", "--source",
+                      dest="src",
+                      help="Path to installation file. "
+                           "Accepts: zip, exe, tar.bz2, tar.gz, and dmg")
+    parser.add_option("-d", "--destination",
+                      dest="dest",
+                      default=None,
+                      help="[optional] Directory to install application into")
+    parser.add_option("--app", dest="app",
+                      action="append",
+                      default=_default_apps,
+                      help="[optional] Application being installed. "
+                           "Should be lowercase, e.g: "
+                           "firefox, fennec, thunderbird, etc.")
+
+    (options, args) = parser.parse_args(argv)
+    if not options.src or not os.path.exists(options.src):
+        print "Error: must specify valid source"
+        return 2
+
+    # Run it
+    if os.path.isdir(options.src):
+        binary = get_binary(options.src, apps=options.app)
+    else:
+        binary = install(options.src, dest=options.dest, apps=options.app)
+    print binary
+
+class InvalidSource(Exception):
+    """
+    Thrown when the specified source is not a recognized
+    file type (zip, exe, tar.gz, tar.bz2 or dmg)
+    """
+
+class InstallError(Exception):
+    """
+    Thrown when the installation fails. Includes traceback
+    if available.
+    """
+
+if __name__ == "__main__":
+    sys.exit(cli())
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozinstall/setup.py
@@ -0,0 +1,79 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (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.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozinstall.
+#
+# The Initial Developer of the Original Code is
+#  The Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#  Clint Talbert <ctalbert@mozilla.com>
+#  Andrew Halberstadt <halbersa@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+import os
+from setuptools import setup
+
+try:
+    here = os.path.dirname(os.path.abspath(__file__))
+    description = file(os.path.join(here, 'README.md')).read()
+except IOError:
+    description = None
+
+version = '0.3'
+
+deps = ['mozinfo']
+
+setup(name='mozInstall',
+      version=version,
+      description="This is a utility package for installing Mozilla applications on various platforms.",
+      long_description=description,
+      classifiers=['Environment :: Console',
+                   'Intended Audience :: Developers',
+                   'License :: OSI Approved :: Mozilla Public License 1.1 (MPL 1.1)',
+                   'Natural Language :: English',
+                   'Operating System :: OS Independent',
+                   'Programming Language :: Python',
+                   'Topic :: Software Development :: Libraries :: Python Modules',
+                  ], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
+      keywords='mozilla',
+      author='mdas',
+      author_email='mdas@mozilla.com',
+      url='https://github.com/mozilla/mozbase',
+      license='MPL',
+      py_modules=['mozinstall'],
+      packages=[],
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=deps,
+      entry_points="""
+      # -*- Entry points: -*-
+      [console_scripts]
+      mozinstall = mozinstall:cli
+      """,
+      )
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozlog/README.md
@@ -0,0 +1,18 @@
+[Mozlog](https://github.com/mozilla/mozbase/tree/master/mozlog)
+is a python package intended to simplify and standardize logs in the Mozilla universe. 
+It wraps around python's [logging](http://docs.python.org/library/logging.html) 
+module and adds some additional functionality.
+
+# Usage
+
+Import mozlog instead of [logging](http://docs.python.org/library/logging.html) 
+(all functionality in the logging module is also available from the mozlog module). 
+To get a logger, call mozlog.getLogger passing in a name and the path to a log file.
+If no log file is specified, the logger will log to stdout.
+
+    import mozlog
+    logger = mozlog.getLogger('LOG_NAME', 'log_file_path')
+    logger.setLevel(mozlog.DEBUG)
+    logger.info('foo')
+    logger.testPass('bar')
+    mozlog.shutdown()
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozlog/mozlog/__init__.py
@@ -0,0 +1,36 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (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.mozilla.org/MPL/ #
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozlog.
+#
+# The Initial Developer of the Original Code is
+#   The Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Andrew Halberstadt <halbersa@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+from logger import *
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozlog/mozlog/logger.py
@@ -0,0 +1,125 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (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.mozilla.org/MPL/ #
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozlog.
+#
+# The Initial Developer of the Original Code is
+#   The Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Andrew Halberstadt <halbersa@gmail.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+from logging import getLogger as getSysLogger
+from logging import *
+
+_default_level = INFO
+_LoggerClass = getLoggerClass()
+
+# Define mozlog specific log levels
+START      = _default_level + 1
+END        = _default_level + 2
+PASS       = _default_level + 3
+KNOWN_FAIL = _default_level + 4
+FAIL       = _default_level + 5
+# Define associated text of log levels
+addLevelName(START, 'TEST-START')
+addLevelName(END, 'TEST-END')
+addLevelName(PASS, 'TEST-PASS')
+addLevelName(KNOWN_FAIL, 'TEST-KNOWN-FAIL')
+addLevelName(FAIL, 'TEST-UNEXPECTED-FAIL')
+
+class _MozLogger(_LoggerClass):
+    """
+    MozLogger class which adds three convenience log levels
+    related to automated testing in Mozilla
+    """
+    def testStart(self, message, *args, **kwargs):
+        self.log(START, message, *args, **kwargs)
+
+    def testEnd(self, message, *args, **kwargs):
+        self.log(END, message, *args, **kwargs)
+
+    def testPass(self, message, *args, **kwargs):
+        self.log(PASS, message, *args, **kwargs)
+
+    def testFail(self, message, *args, **kwargs):
+        self.log(FAIL, message, *args, **kwargs)
+
+    def testKnownFail(self, message, *args, **kwargs):
+        self.log(KNOWN_FAIL, message, *args, **kwargs)
+
+class _MozFormatter(Formatter):
+    """
+    MozFormatter class used for default formatting
+    This can easily be overriden with the log handler's setFormatter()
+    """
+    level_length = 0
+    max_level_length = len('TEST-START')
+
+    def __init__(self):
+        pass
+
+    def format(self, record):
+        record.message = record.getMessage()
+
+        # Handles padding so record levels align nicely
+        if len(record.levelname) > self.level_length:
+            pad = 0
+            if len(record.levelname) <= self.max_level_length:
+                self.level_length = len(record.levelname)
+        else:
+            pad = self.level_length - len(record.levelname) + 1
+        sep = '|'.rjust(pad)
+        fmt = '%(name)s %(levelname)s ' + sep + ' %(message)s'
+        return fmt % record.__dict__
+
+def getLogger(name, logfile=None):
+    """
+    Returns the logger with the specified name.
+    If the logger doesn't exist, it is created.
+
+    name       - The name of the logger to retrieve
<