Merge the last PGO-green inbound changeset to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 09 Aug 2012 22:53:34 -0400
changeset 107480 4e150530c925a6b2143f82e9c0e81b7425e665d9
parent 107444 08e0e3b95f03ad78cb3eb1e7cd00b918838cdad3 (current diff)
parent 107479 87b84230f0eea6ca6ec2a9389dee355409b3df80 (diff)
child 107503 8d17980dc06db238ca9c6f72ab34c31d72807331
push id1490
push userakeybl@mozilla.com
push dateMon, 08 Oct 2012 18:29:50 +0000
treeherdermozilla-beta@f335e7dacdc1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone17.0a1
first release with
nightly linux32
4e150530c925 / 17.0a1 / 20120810030512 / files
nightly linux64
4e150530c925 / 17.0a1 / 20120810030512 / files
nightly mac
4e150530c925 / 17.0a1 / 20120810030512 / files
nightly win32
4e150530c925 / 17.0a1 / 20120810030512 / files
nightly win64
4e150530c925 / 17.0a1 / 20120810030512 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge the last PGO-green inbound changeset to m-c.
layout/reftests/svg/pattern-scale-01a.svg
mobile/android/base/db/AndroidBrowserDB.java
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -43,39 +43,16 @@ XPCOMUtils.defineLazyGetter(this, 'Debug
   Cu.import('resource://gre/modules/devtools/dbg-server.jsm');
   return DebuggerServer;
 });
 
 function getContentWindow() {
   return shell.contentBrowser.contentWindow;
 }
 
-// FIXME Bug 707625
-// Please don't add more permissions here.
-// XXX never grant 'content-camera' to non-gaia apps
-function addPermissions(urls) {
-  let permissions = [
-    'indexedDB-unlimited', 'webapps-manage', 'offline-app', 'pin-app',
-    'content-camera', 'wifi-manage', 'desktop-notification',
-    'geolocation', 'device-storage', 'alarms'
-  ];
-
-  urls.forEach(function(url) {
-    url = url.trim();
-    if (url) {
-      let uri = Services.io.newURI(url, null, null);
-      let allow = Ci.nsIPermissionManager.ALLOW_ACTION;
-
-      permissions.forEach(function(permission) {
-        Services.perms.add(uri, permission, allow);
-      });
-    }
-  });
-}
-
 var shell = {
 
   get CrashSubmit() {
     delete this.CrashSubmit;
     Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
     return this.CrashSubmit;
   },
 
@@ -155,23 +132,16 @@ var shell = {
     // level modified from this point.
     // try catch block must be used since the emulator fails here. bug 746429
     try {
       Services.audioManager.masterVolume = 0.5;
     } catch(e) {
       dump('Error setting master volume: ' + e + '\n');
     }
 
-    let domains = "";
-    try {
-      domains = Services.prefs.getCharPref('b2g.privileged.domains');
-    } catch(e) {}
-
-    addPermissions(domains.split(","));
-
     CustomEventManager.init();
     WebappsHelper.init();
 
     // XXX could factor out into a settings->pref map.  Not worth it yet.
     SettingsListener.observe("debug.fps.enabled", false, function(value) {
       Services.prefs.setBoolPref("layers.acceleration.draw-fps", value);
     });
     SettingsListener.observe("debug.paint-flashing.enabled", false, function(value) {
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -16,17 +16,17 @@
 
 #if defined(SOLARIS)
 #include <ieeefp.h>
 #endif
 
 //A trick to handle IEEE floating point exceptions on FreeBSD - E.D.
 #ifdef __FreeBSD__
 #include <ieeefp.h>
-#ifdef __alpha__
+#if !defined(__i386__) && !defined(__x86_64__)
 static fp_except_t allmask = FP_X_INV|FP_X_OFL|FP_X_UFL|FP_X_DZ|FP_X_IMP;
 #else
 static fp_except_t allmask = FP_X_INV|FP_X_OFL|FP_X_UFL|FP_X_DZ|FP_X_IMP|FP_X_DNML;
 #endif
 static fp_except_t oldmask = fpsetmask(~allmask);
 #endif
 
 #include "nsAString.h"
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -76,16 +76,26 @@ static NS_DEFINE_CID(kAppShellCID, NS_AP
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gObjectLog = PR_NewLogModule("objlc");
 #endif
 
 #define LOG(args) PR_LOG(gObjectLog, PR_LOG_DEBUG, args)
 #define LOG_ENABLED() PR_LOG_TEST(gObjectLog, PR_LOG_DEBUG)
 
+static bool
+InActiveDocument(nsIContent *aContent)
+{
+  if (!aContent->IsInDoc()) {
+    return false;
+  }
+  nsIDocument *doc = aContent->OwnerDoc();
+  return (doc && doc->IsActive());
+}
+
 ///
 /// Runnables and helper classes
 ///
 
 class nsAsyncInstantiateEvent : public nsRunnable {
 public:
   nsAsyncInstantiateEvent(nsObjectLoadingContent *aContent)
   : mContent(aContent) {}
@@ -132,17 +142,17 @@ NS_IMETHODIMP
 InDocCheckEvent::Run()
 {
   nsObjectLoadingContent *objLC =
     static_cast<nsObjectLoadingContent *>(mContent.get());
 
   nsCOMPtr<nsIContent> content =
     do_QueryInterface(static_cast<nsIImageLoadingContent *>(objLC));
 
-  if (!content->IsInDoc()) {
+  if (!InActiveDocument(content)) {
     nsObjectLoadingContent *objLC =
       static_cast<nsObjectLoadingContent *>(mContent.get());
     objLC->UnloadObject();
   }
   return NS_OK;
 }
 
 /**
@@ -551,49 +561,51 @@ IsPluginEnabledForType(const nsCString& 
 
 bool
 nsObjectLoadingContent::IsSupportedDocument(const nsCString& aMimeType)
 {
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   NS_ASSERTION(thisContent, "must be a content");
 
-  nsresult rv;
   nsCOMPtr<nsIWebNavigationInfo> info(
-    do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID, &rv));
-  PRUint32 supported;
-  if (info) {
-    nsCOMPtr<nsIWebNavigation> webNav;
-    nsIDocument* currentDoc = thisContent->GetCurrentDoc();
-    if (currentDoc) {
-      webNav = do_GetInterface(currentDoc->GetScriptGlobalObject());
-    }
-    rv = info->IsTypeSupported(aMimeType, webNav, &supported);
+    do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID));
+  if (!info) {
+    return false;
   }
 
-  if (NS_SUCCEEDED(rv)) {
-    if (supported == nsIWebNavigationInfo::UNSUPPORTED) {
-      // Try a stream converter
-      // NOTE: We treat any type we can convert from as a supported type. If a
-      // type is not actually supported, the URI loader will detect that and
-      // return an error, and we'll fallback.
-      nsCOMPtr<nsIStreamConverterService> convServ =
-        do_GetService("@mozilla.org/streamConverters;1");
-      bool canConvert = false;
-      if (convServ) {
-        rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert);
-      }
-      return NS_SUCCEEDED(rv) && canConvert;
-    }
+  nsCOMPtr<nsIWebNavigation> webNav;
+  nsIDocument* currentDoc = thisContent->GetCurrentDoc();
+  if (currentDoc) {
+    webNav = do_GetInterface(currentDoc->GetScriptGlobalObject());
+  }
+  
+  PRUint32 supported;
+  nsresult rv = info->IsTypeSupported(aMimeType, webNav, &supported);
 
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  if (supported != nsIWebNavigationInfo::UNSUPPORTED) {
     // Don't want to support plugins as documents
     return supported != nsIWebNavigationInfo::PLUGIN;
   }
 
-  return false;
+  // Try a stream converter
+  // NOTE: We treat any type we can convert from as a supported type. If a
+  // type is not actually supported, the URI loader will detect that and
+  // return an error, and we'll fallback.
+  nsCOMPtr<nsIStreamConverterService> convServ =
+    do_GetService("@mozilla.org/streamConverters;1");
+  bool canConvert = false;
+  if (convServ) {
+    rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert);
+  }
+  return NS_SUCCEEDED(rv) && canConvert;
 }
 
 nsresult
 nsObjectLoadingContent::BindToTree(nsIDocument* aDocument,
                                    nsIContent* /*aParent*/,
                                    nsIContent* /*aBindingParent*/,
                                    bool /*aCompileEventHandlers*/)
 {
@@ -607,28 +619,29 @@ void
 nsObjectLoadingContent::UnbindFromTree(bool /*aDeep*/, bool /*aNullParent*/)
 {
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
   MOZ_ASSERT(thisContent);
   nsIDocument* ownerDoc = thisContent->OwnerDoc();
   ownerDoc->RemovePlugin(this);
 
-  if (mType == eType_Plugin) {
+  if (mType == eType_Plugin && mInstanceOwner) {
     // we'll let the plugin continue to run at least until we get back to
     // the event loop. If we get back to the event loop and the node
     // has still not been added back to the document then we tear down the
     // plugin
     nsCOMPtr<nsIRunnable> event = new InDocCheckEvent(this);
 
     nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
     if (appShell) {
       appShell->RunInStableState(event);
     }
   } else {
+    // Reset state and clear pending events
     /// XXX(johns): The implementation for GenericFrame notes that ideally we
     ///             would keep the docshell around, but trash the frameloader
     UnloadObject();
   }
 
 }
 
 nsObjectLoadingContent::nsObjectLoadingContent()
@@ -689,17 +702,17 @@ nsObjectLoadingContent::InstantiatePlugi
 
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent *>(this));
   // Flush layout so that the plugin is initialized with the latest information.
   nsIDocument* doc = thisContent->GetCurrentDoc();
   if (!doc) {
     return NS_ERROR_FAILURE;
   }
-  if (!doc->IsActive()) {
+  if (!InActiveDocument(thisContent)) {
     NS_ERROR("Shouldn't be calling "
              "InstantiatePluginInstance in an inactive document");
     return NS_ERROR_FAILURE;
   }
   doc->FlushPendingNotifications(Flush_Layout);
 
   nsresult rv = NS_ERROR_FAILURE;
   nsRefPtr<nsPluginHost> pluginHost =
@@ -1466,29 +1479,29 @@ nsObjectLoadingContent::LoadObject(bool 
                                    nsIRequest *aLoadingChannel)
 {
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   NS_ASSERTION(thisContent, "must be a content");
   nsIDocument* doc = thisContent->OwnerDoc();
   nsresult rv = NS_OK;
 
+  // Sanity check
+  if (!InActiveDocument(thisContent)) {
+    NS_NOTREACHED("LoadObject called while not bound to an active document");
+    return NS_ERROR_UNEXPECTED;
+  }
+
   // XXX(johns): In these cases, we refuse to touch our content and just
   //   remain unloaded, as per legacy behavior. It would make more sense to
   //   load fallback content initially and refuse to ever change state again.
   if (doc->IsBeingUsedAsImage() || doc->IsLoadedAsData()) {
     return NS_OK;
   }
 
-  // Sanity check
-  if (!thisContent->IsInDoc()) {
-    NS_NOTREACHED("LoadObject called while not bound to a document");
-    return NS_ERROR_UNEXPECTED;
-  }
-
   LOG(("OBJLC [%p]: LoadObject called, notify %u, forceload %u, channel %p",
        this, aNotify, aForceLoad, aLoadingChannel));
 
   // We can't re-use an already open channel, but aForceLoad may make us try
   // to load a plugin without any changes in channel state.
   if (aForceLoad && mChannelLoaded) {
     CloseChannel();
     mChannelLoaded = false;
@@ -1965,17 +1978,17 @@ nsObjectLoadingContent::NotifyStateChang
   if (!doc) {
     return; // Nothing to do
   }
 
   nsEventStates newState = ObjectState();
 
   if (newState != aOldState) {
     // This will trigger frame construction
-    NS_ASSERTION(thisContent->IsInDoc(), "Something is confused");
+    NS_ASSERTION(InActiveDocument(thisContent), "Something is confused");
     nsEventStates changedBits = aOldState ^ newState;
 
     {
       nsAutoScriptBlocker scriptBlocker;
       doc->ContentStateChanged(thisContent, changedBits);
     }
     if (aSync) {
       // Make sure that frames are actually constructed immediately.
@@ -2146,20 +2159,20 @@ nsObjectLoadingContent::PluginCrashed(ns
 
 NS_IMETHODIMP
 nsObjectLoadingContent::SyncStartPluginInstance()
 {
   NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
                "Must be able to run script in order to instantiate a plugin instance!");
 
   // Don't even attempt to start an instance unless the content is in
-  // the document.
+  // the document and active
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
-  if (!thisContent->IsInDoc()) {
+  if (!InActiveDocument(thisContent)) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIURI> kungFuURIGrip(mURI);
   nsCString contentType(mContentType);
   return InstantiatePluginInstance();
 }
 
--- a/content/canvas/test/test_canvas.html
+++ b/content/canvas/test/test_canvas.html
@@ -15,17 +15,18 @@ function IsD2DEnabled() {
     try {
         enabled = Cc["@mozilla.org/gfx/info;1"].getService(Components.interfaces.nsIGfxInfo).D2DEnabled;
     } catch(e) {}
     
     return enabled;
 }
 
 function IsLinux() {
-    return navigator.platform.indexOf("Linux") == 0;
+    return navigator.platform.indexOf("Linux") == 0 &&
+           navigator.appVersion.indexOf("Android") == -1;
 }
 
 function IsMacOSX10_5orOlder() {
     var is105orOlder = false;
 
     if (navigator.platform.indexOf("Mac") == 0) {
         var version = Cc["@mozilla.org/system-info;1"]
                         .getService(Components.interfaces.nsIPropertyBag2)
--- a/content/html/content/src/nsHTMLObjectElement.cpp
+++ b/content/html/content/src/nsHTMLObjectElement.cpp
@@ -512,16 +512,22 @@ nsMapRuleToAttributesFunc
 nsHTMLObjectElement::GetAttributeMappingFunction() const
 {
   return &MapAttributesIntoRule;
 }
 
 void
 nsHTMLObjectElement::StartObjectLoad(bool aNotify)
 {
+  // BindToTree can call us asynchronously, and we may be removed from the tree
+  // in the interim
+  if (!IsInDoc() || !OwnerDoc()->IsActive()) {
+    return;
+  }
+
   LoadObject(aNotify);
   SetIsNetworkCreated(false);
 }
 
 nsEventStates
 nsHTMLObjectElement::IntrinsicState() const
 {
   return nsGenericHTMLFormElement::IntrinsicState() | ObjectState();
--- a/content/html/content/src/nsHTMLSharedObjectElement.cpp
+++ b/content/html/content/src/nsHTMLSharedObjectElement.cpp
@@ -460,16 +460,22 @@ nsHTMLSharedObjectElement::GetAttributeM
   }
 
   return &MapAttributesIntoRule;
 }
 
 void
 nsHTMLSharedObjectElement::StartObjectLoad(bool aNotify)
 {
+  // BindToTree can call us asynchronously, and we may be removed from the tree
+  // in the interim
+  if (!IsInDoc() || !OwnerDoc()->IsActive()) {
+    return;
+  }
+
   LoadObject(aNotify);
   SetIsNetworkCreated(false);
 }
 
 nsEventStates
 nsHTMLSharedObjectElement::IntrinsicState() const
 {
   return nsGenericHTMLElement::IntrinsicState() | ObjectState();
--- a/dom/Makefile.in
+++ b/dom/Makefile.in
@@ -58,16 +58,17 @@ DIRS += \
   messages \
   power \
   settings \
   sms \
   mms \
   src \
   locales \
   network \
+  permission \
   plugins/base \
   plugins/ipc \
   indexedDB \
   system \
   ipc \
   identity \
   workers \
   camera \
--- a/dom/contacts/ContactManager.js
+++ b/dom/contacts/ContactManager.js
@@ -13,16 +13,17 @@ else
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
+Cu.import("resource://gre/modules/PermissionPromptHelper.jsm");
 
 XPCOMUtils.defineLazyGetter(Services, "DOMRequest", function() {
   return Cc["@mozilla.org/dom/dom-request-service;1"].getService(Ci.nsIDOMRequestService);
 });
 
 XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
   return Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
 });
@@ -272,90 +273,30 @@ function ContactManager()
   debug("Constructor");
 }
 
 ContactManager.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
   _oncontactchange: null,
 
   set oncontactchange(aCallback) {
-    if (this.hasPrivileges)
+    debug("set oncontactchange");
+    let allowCallback = function() {
       this._oncontactchange = aCallback;
-    else
+    }.bind(this);
+    let cancelCallback = function() {
       throw Components.results.NS_ERROR_FAILURE;
+    }
+    this.askPermission("listen", null, allowCallback, cancelCallback);
   },
 
   get oncontactchange() {
     return this._oncontactchange;
   },
 
-  save: function save(aContact) {
-    let request;
-    if (this.hasPrivileges) {
-      debug("save: " + JSON.stringify(aContact) + " :" + aContact.id);
-      let newContact = {};
-      newContact.properties = {
-        name:            [],
-        honorificPrefix: [],
-        givenName:       [],
-        additionalName:  [],
-        familyName:      [],
-        honorificSuffix: [],
-        nickname:        [],
-        email:           [],
-        photo:           [],
-        url:             [],
-        category:        [],
-        adr:             [],
-        tel:             [],
-        org:             [],
-        jobTitle:        [],
-        bday:            null,
-        note:            [],
-        impp:            [],
-        anniversary:     null,
-        sex:             null,
-        genderIdentity:  null
-      };
-      for (let field in newContact.properties)
-        newContact.properties[field] = aContact[field];
-
-      let reason;
-      if (aContact.id == "undefined") {
-        // for example {25c00f01-90e5-c545-b4d4-21E2ddbab9e0} becomes
-        // 25c00f0190e5c545b4d421E2ddbab9e0
-        aContact.id = this._getRandomId().replace('-', '', 'g').replace('{', '').replace('}', '');
-        reason = "create";
-      } else {
-        reason = "update";
-      }
-
-      this._setMetaData(newContact, aContact);
-      debug("send: " + JSON.stringify(newContact));
-      request = this.createRequest();
-      cpmm.sendAsyncMessage("Contact:Save", {contact: newContact,
-                                             requestID: this.getRequestId({request: request, reason: reason })});
-      return request;
-    } else {
-      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
-    }
-  },
-
-  remove: function removeContact(aRecord) {
-    let request;
-    if (this.hasPrivileges) {
-      request = this.createRequest();
-      cpmm.sendAsyncMessage("Contact:Remove", {id: aRecord.id,
-                                               requestID: this.getRequestId({request: request, reason: "remove"})});
-      return request;
-    } else {
-      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
-    }
-  },
-
   _setMetaData: function(aNewContact, aRecord) {
     aNewContact.id = aRecord.id;
     aNewContact.published = aRecord.published;
     aNewContact.updated = aRecord.updated;
   },
 
   _convertContactsArray: function(aContacts) {
     let contacts = new Array();
@@ -403,81 +344,182 @@ ContactManager.prototype = {
       case "Contacts:Find:Return:KO":
       case "Contact:Save:Return:KO":
       case "Contact:Remove:Return:KO":
       case "Contacts:Clear:Return:KO":
         req = this.getRequest(msg.requestID);
         if (req)
           Services.DOMRequest.fireError(req.request, msg.errorMsg);
         break;
+      case "PermissionPromptHelper:AskPermission:OK":
+        debug("id: " + msg.requestID);
+        req = this.getRequest(msg.requestID);
+        if (!req) {
+          break;
+        }
+
+        if (msg.result == Ci.nsIPermissionManager.ALLOW_ACTION) {
+          req.allow();
+        } else {
+          req.cancel();
+        }
+        break;
       default: 
         debug("Wrong message: " + aMessage.name);
     }
     this.removeRequest(msg.requestID);
   },
 
-  find: function(aOptions) {
+  askPermission: function (aAccess, aReqeust, aAllowCallback, aCancelCallback) {
+    debug("askPermission for contacts");
+    let requestID = this.getRequestId({
+      request: aReqeust,
+      allow: function() {
+        aAllowCallback();
+      }.bind(this),
+      cancel : function() {
+        if (aCancelCallback) {
+          aCancelCallback()
+        } else if (request) {
+          Services.DOMRequest.fireError(request, "Not Allowed");
+        }
+      }.bind(this)
+    });
+
+    let principal = this._window.document.nodePrincipal;
+    cpmm.sendAsyncMessage("PermissionPromptHelper:AskPermission", {
+      type: "contacts",
+      access: aAccess,
+      requestID: requestID,
+      origin: principal.origin,
+      appID: principal.appId,
+      browserFlag: principal.isInBrowserElement
+    });
+  },
+
+  save: function save(aContact) {
     let request;
-    if (this.hasPrivileges) {
-      request = this.createRequest();
-      cpmm.sendAsyncMessage("Contacts:Find", {findOptions: aOptions, 
-                                              requestID: this.getRequestId({request: request, reason: "find"})});
-      return request;
+    debug("save: " + JSON.stringify(aContact) + " :" + aContact.id);
+    let newContact = {};
+    newContact.properties = {
+      name:            [],
+      honorificPrefix: [],
+      givenName:       [],
+      additionalName:  [],
+      familyName:      [],
+      honorificSuffix: [],
+      nickname:        [],
+      email:           [],
+      photo:           [],
+      url:             [],
+      category:        [],
+      adr:             [],
+      tel:             [],
+      org:             [],
+      jobTitle:        [],
+      bday:            null,
+      note:            [],
+      impp:            [],
+      anniversary:     null,
+      sex:             null,
+      genderIdentity:  null
+    };
+    for (let field in newContact.properties) {
+      newContact.properties[field] = aContact[field];
+    }
+
+    let reason;
+    if (aContact.id == "undefined") {
+      // for example {25c00f01-90e5-c545-b4d4-21E2ddbab9e0} becomes
+      // 25c00f0190e5c545b4d421E2ddbab9e0
+      aContact.id = this._getRandomId().replace('-', '', 'g').replace('{', '').replace('}', '');
+      reason = "create";
     } else {
-      debug("find not allowed");
-      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+      reason = "update";
     }
+
+    this._setMetaData(newContact, aContact);
+    debug("send: " + JSON.stringify(newContact));
+    request = this.createRequest();
+    let options = { contact: newContact };
+    let allowCallback = function() {
+      cpmm.sendAsyncMessage("Contact:Save", {requestID: this.getRequestId({request: request, reason: reason}), options: options});
+    }.bind(this)
+    this.askPermission(reason, request, allowCallback);
+    return request;
+  },
+
+  find: function(aOptions) {
+    debug("find! " + JSON.stringify(aOptions));
+    let request;
+    request = this.createRequest();
+    let options = { findOptions: aOptions };
+    let allowCallback = function() {
+      cpmm.sendAsyncMessage("Contacts:Find", {requestID: this.getRequestId({request: request, reason: "find"}), options: options});
+    }.bind(this)
+    this.askPermission("find", request, allowCallback);
+    return request;
+  },
+
+  remove: function removeContact(aRecord) {
+    let request;
+    request = this.createRequest();
+    let options = { id: aRecord.id };
+    let allowCallback = function() {
+      cpmm.sendAsyncMessage("Contact:Remove", {requestID: this.getRequestId({request: request, reason: "remove"}), options: options});
+    }.bind(this)
+    this.askPermission("remove", request, allowCallback);
+    return request;
   },
 
   clear: function() {
+    debug("clear");
     let request;
-    if (this.hasPrivileges) {
-      request = this.createRequest();
-      cpmm.sendAsyncMessage("Contacts:Clear", {requestID: this.getRequestId({request: request, reason: "remove"})});
-      return request;
-    } else {
-      debug("clear not allowed");
-      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
-    }
+    request = this.createRequest();
+    let options = {};
+    let allowCallback = function() {
+      cpmm.sendAsyncMessage("Contacts:Clear", {requestID: this.getRequestId({request: request, reason: "remove"}), options: options});
+    }.bind(this)
+    this.askPermission("remove", request, allowCallback);
+    return request;
   },
 
   getSimContacts: function(aType) {
     let request;
-    if (this.hasPrivileges) {
+    request = this.createRequest();
+
+    let allowCallback = function() {
       let callback = function(aType, aContacts) {
         debug("got SIM contacts: " + aType + " " + JSON.stringify(aContacts));
         let result = aContacts.map(function(c) { return { name: [c.alphaId], tel: [c.number] } });
         debug("result: " + JSON.stringify(result));
         Services.DOMRequest.fireSuccess(request, result);
       };
       debug("getSimContacts " + aType);
-      request = this.createRequest();
+
       mRIL.getICCContacts(aType, callback);
-      return request;
-    } else {
-      debug("getSimContacts not allowed");
+    }.bind(this);
+
+    let cancelCallback = function() {
       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
     }
+    this.askPermission("getSimContacts", request, allowCallback, cancelCallback);
+    return request;
   },
 
   init: function(aWindow) {
     // Set navigator.mozContacts to null.
     if (!Services.prefs.getBoolPref("dom.mozContacts.enabled"))
       return null;
 
     this.initHelper(aWindow, ["Contacts:Find:Return:OK", "Contacts:Find:Return:KO",
                               "Contacts:Clear:Return:OK", "Contacts:Clear:Return:KO",
                               "Contact:Save:Return:OK", "Contact:Save:Return:KO",
-                              "Contact:Remove:Return:OK", "Contact:Remove:Return:KO"]);
-
-    let perm = Services.perms.testExactPermissionFromPrincipal(aWindow.document.nodePrincipal, "contacts");
- 
-    //only pages with perm set can use the contacts
-    this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
-    debug("Contacts permission: " + this.hasPrivileges);
+                              "Contact:Remove:Return:OK", "Contact:Remove:Return:KO",
+                              "PermissionPromptHelper:AskPermission:OK"]);
   },
 
   // Called from DOMRequestIpcHelper
   uninit: function uninit() {
     debug("uninit call");
     if (this._oncontactchange)
       this._oncontactchange = null;
   },
--- a/dom/contacts/fallback/ContactDB.jsm
+++ b/dom/contacts/fallback/ContactDB.jsm
@@ -297,17 +297,16 @@ ContactDB.prototype = {
    *        Object specifying search options. Possible attributes:
    *        - filterBy
    *        - filterOp
    *        - filterValue
    *        - count
    */
   find: function find(aSuccessCb, aFailureCb, aOptions) {
     debug("ContactDB:find val:" + aOptions.filterValue + " by: " + aOptions.filterBy + " op: " + aOptions.filterOp + "\n");
-
     let self = this;
     this.newTxn("readonly", function (txn, store) {
       if (aOptions && (aOptions.filterOp == "equals" || aOptions.filterOp == "contains")) {
         self._findWithIndex(txn, store, aOptions);
       } else {
         self._findAll(txn, store, aOptions);
       }
     }, aSuccessCb, aFailureCb);
--- a/dom/contacts/fallback/ContactService.jsm
+++ b/dom/contacts/fallback/ContactService.jsm
@@ -53,29 +53,31 @@ let DOMContactManager = {
     if (this._db)
       this._db.close();
     this._db = null;
   },
 
   receiveMessage: function(aMessage) {
     debug("Fallback DOMContactManager::receiveMessage " + aMessage.name);
     let mm = aMessage.target.QueryInterface(Ci.nsIFrameMessageManager);
-    let msg = aMessage.json;
+    let msg = aMessage.data;
 
     /*
      * Sorting the contacts by sortBy field. sortBy can either be familyName or givenName.
      * If 2 entries have the same sortyBy field or no sortBy field is present, we continue 
      * sorting with the other sortyBy field.
      */
     function sortfunction(a, b){
       let x, y;
       let sortByNameSet = true;
       let result = 0;
-      let sortBy = msg.findOptions.sortBy;
-      let sortOrder = msg.findOptions.sortOrder;
+      let findOptions = msg.options.findOptions;
+      let sortBy = findOptions.sortBy;
+      let sortOrder = findOptions.sortOrder;
+      
       if (!a.properties[sortBy] || !(x = a.properties[sortBy][0].toLowerCase())) {
         sortByNameSet = false;
       }
 
       if (!b.properties[sortBy] || !(y = b.properties[sortBy][0].toLowerCase())) {
         if (sortByNameSet) {
           return sortOrder == 'ascending' ? 1 : -1;
         }
@@ -102,45 +104,50 @@ let DOMContactManager = {
 
     switch (aMessage.name) {
       case "Contacts:Find":
         let result = new Array();
         this._db.find(
           function(contacts) {
             for (let i in contacts)
               result.push(contacts[i]);
-            if (msg.findOptions.sortOrder !== 'undefined' && msg.findOptions.sortBy !== 'undefined') {
-              debug('sortBy: ' + msg.findOptions.sortBy + ', sortOrder: ' + msg.findOptions.sortOrder );
-              result.sort(sortfunction);
-              if (msg.findOptions.filterLimit)
-                result = result.slice(0, msg.findOptions.filterLimit);
+            if (msg.options && msg.options.findOptions) {
+              let findOptions = msg.options.findOptions;
+              if (findOptions.sortOrder !== 'undefined' && findOptions.sortBy !== 'undefined') {
+                debug('sortBy: ' + findOptions.sortBy + ', sortOrder: ' + findOptions.sortOrder );
+                result.sort(sortfunction);
+                if (findOptions.filterLimit)
+                  result = result.slice(0, findOptions.filterLimit);
+              }
             }
 
             debug("result:" + JSON.stringify(result));
             mm.sendAsyncMessage("Contacts:Find:Return:OK", {requestID: msg.requestID, contacts: result});
           }.bind(this),
           function(aErrorMsg) { mm.sendAsyncMessage("Contacts:Find:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }) }.bind(this),
-          msg.findOptions);
+          msg.options.findOptions);
         break;
       case "Contact:Save":
         this._db.saveContact(
-          msg.contact, 
-          function() { mm.sendAsyncMessage("Contact:Save:Return:OK", { requestID: msg.requestID, contactID: msg.contact.id }); }.bind(this),
+          msg.options.contact,
+          function() { mm.sendAsyncMessage("Contact:Save:Return:OK", { requestID: msg.requestID, contactID: msg.options.contact.id }); }.bind(this),
           function(aErrorMsg) { mm.sendAsyncMessage("Contact:Save:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this)
         );
         break;
       case "Contact:Remove":
         this._db.removeContact(
-          msg.id, 
-          function() { mm.sendAsyncMessage("Contact:Remove:Return:OK", { requestID: msg.requestID, contactID: msg.id }); }.bind(this),
+          msg.options.id,
+          function() { mm.sendAsyncMessage("Contact:Remove:Return:OK", { requestID: msg.requestID, contactID: msg.options.id }); }.bind(this),
           function(aErrorMsg) { mm.sendAsyncMessage("Contact:Remove:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this)
         );
         break;
       case "Contacts:Clear":
         this._db.clear(
           function() { mm.sendAsyncMessage("Contacts:Clear:Return:OK", { requestID: msg.requestID }); }.bind(this),
           function(aErrorMsg) { mm.sendAsyncMessage("Contacts:Clear:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this)
         );
+      default:
+        debug("WRONG MESSAGE NAME: " + aMessage.name);
     }
   }
 }
 
 DOMContactManager.init();
--- a/dom/contacts/tests/test_contacts_basics.html
+++ b/dom/contacts/tests/test_contacts_basics.html
@@ -401,31 +401,31 @@ var steps = [
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Adding a new contact with properties1");
     createResult1 = new mozContact();
     createResult1.init(properties1);
+    mozContacts.oncontactchange = null;
     req = navigator.mozContacts.save(createResult1);
     req.onsuccess = function () {
       ok(createResult1.id, "The contact now has an ID.");
       sample_id1 = createResult1.id;
       checkContacts(properties1, createResult1);
       next();
     };
     req.onerror = onFailure;
   },
   function () {
     ok(true, "Retrieving by substring tel1");
     var options = {filterBy: ["tel"],
                    filterOp: "contains",
                    filterValue: properties1.tel[1].number.substring(1,5)};
-    mozContacts.oncontactchange = null;
     req = mozContacts.find(options);
     req.onsuccess = function () {
       ok(req.result.length == 1, "Found exactly 1 contact.");
       findResult1 = req.result[0];
       ok(findResult1.id == sample_id1, "Same ID");
       checkContacts(createResult1, properties1);
       next();
     };
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -178,17 +178,23 @@ public:
     kAudioTrack = 2
   };
 
   NS_IMETHOD
   Run()
   {
     mManager = MediaManager::Get();
 
-    if (mPicture) {
+    // It is an error is audio or video are requested along with picture.
+    if (mPicture && (mAudio || mVideo)) {
+      NS_DispatchToMainThread(new ErrorCallbackRunnable(
+        mError, NS_LITERAL_STRING("NOT_SUPPORTED_ERR"), mWindowID
+      ));
+      return NS_OK;
+    } else {
       SendPicture();
       return NS_OK;
     }
 
     // XXX: Implement merging two streams (See bug 758391).
     if (mAudio && mVideo) {
       NS_DispatchToMainThread(new ErrorCallbackRunnable(
         mError, NS_LITERAL_STRING("NOT_IMPLEMENTED"), mWindowID
new file mode 100644
--- /dev/null
+++ b/dom/permission/Makefile.in
@@ -0,0 +1,26 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE         = dom
+LIBRARY_NAME   = jsdompermission_s
+
+XPIDL_MODULE   = dom_permission
+GRE_MODULE     = 1
+
+EXTRA_JS_MODULES =   \
+  PermissionPromptHelper.jsm \
+  $(NULL)
+
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+include $(topsrcdir)/config/rules.mk
+
+DEFINES += -D_IMPL_NS_LAYOUT
new file mode 100644
--- /dev/null
+++ b/dom/permission/PermissionPromptHelper.jsm
@@ -0,0 +1,97 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* PermissionPromptHelper checks the permissionDB for a given permission
+ * name and performs prompting if needed.
+ * Usage: send PermissionPromptHelper:AskPermission via the FrameMessageManager with:
+ * |origin|, |appID|, |browserFlag| -> used for getting the principal and
+ * |type| and |access| to call testExactPermissionFromPrincipal.
+ * Note that |access| isn't currently used.
+ * Other arugments are:
+ * requestID: ID that gets returned with the result message.
+ *
+ * Once the permission is checked, it returns with the message
+ * "PermissionPromptHelper:AskPermission:OK"
+ * The result contains the |result| e.g.Ci.nsIPermissionManager.ALLOW_ACTION
+ * and a requestID that
+ */
+
+"use strict";
+
+let DEBUG = 0;
+if (DEBUG)
+  debug = function (s) { dump("-*- Permission Prompt Helper component: " + s + "\n"); }
+else
+  debug = function (s) {}
+
+const Cu = Components.utils; 
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+let EXPORTED_SYMBOLS = ["PermissionPromptHelper"];
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
+  return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
+});
+
+var permissionManager = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
+var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
+var appsService = Cc["@mozilla.org/AppsService;1"].getService(Ci.nsIAppsService);
+
+let PermissionPromptHelper = {
+  init: function() {
+    debug("Init");
+    ppmm.addMessageListener("PermissionPromptHelper:AskPermission", this);
+    Services.obs.addObserver(this, "profile-before-change", false);
+  },
+
+  askPermission: function(aMessage, aCallbacks) {
+    let msg = aMessage.json;
+    debug("askPerm: " + JSON.stringify(aMessage.json));
+    let uri = Services.io.newURI(msg.origin, null, null);
+    let principal = secMan.getAppCodebasePrincipal(uri, msg.appID, msg.browserFlag);
+    let perm = permissionManager.testExactPermissionFromPrincipal(principal, msg.type);
+
+    switch(perm) {
+      case Ci.nsIPermissionManager.ALLOW_ACTION:
+        aCallbacks.allow();
+        return;
+      case Ci.nsIPermissionManager.DENY_ACTION:
+        aCallbacks.cancel();
+        return;
+    }
+
+  // FIXXMEE PROMPT MAGIC! Bug 773114.
+  // We have to diplay the prompt here.
+  },
+
+  observe: function(aSubject, aTopic, aData) {
+    ppmm.removeMessageListener("PermissionPromptHelper:AskPermission", this);
+    Services.obs.removeObserver(this, "profile-before-change");
+    ppmm = null;
+  },
+
+  receiveMessage: function(aMessage) {
+    debug("PermissionPromptHelper::receiveMessage " + aMessage.name);
+    let mm = aMessage.target.QueryInterface(Ci.nsIFrameMessageManager);
+    let msg = aMessage.data;
+
+    let result;
+    if (aMessage.name == "PermissionPromptHelper:AskPermission") {
+      this.askPermission(aMessage, {
+        cancel: function() {
+          mm.sendAsyncMessage("PermissionPromptHelper:AskPermission:OK", {result: Ci.nsIPermissionManager.DENY_ACTION, requestID: msg.requestID});
+        },
+        allow: function() {
+          mm.sendAsyncMessage("PermissionPromptHelper:AskPermission:OK", {result: Ci.nsIPermissionManager.ALLOW_ACTION, requestID: msg.requestID});
+        }
+      });
+    }
+  }
+}
+
+PermissionPromptHelper.init();
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -342,17 +342,16 @@ DrawTargetCairo::DrawSurface(SourceSurfa
   cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aSurface);
   cairo_pattern_t* pat = cairo_pattern_create_for_surface(surf);
   cairo_surface_destroy(surf);
 
   cairo_pattern_set_matrix(pat, &src_mat);
   cairo_pattern_set_filter(pat, GfxFilterToCairoFilter(aSurfOptions.mFilter));
   cairo_pattern_set_extend(pat, CAIRO_EXTEND_PAD);
 
-  cairo_save(mContext);
   cairo_translate(mContext, aDest.X(), aDest.Y());
 
   if (OperatorAffectsUncoveredAreas(aOptions.mCompositionOp) ||
       aOptions.mCompositionOp == OP_SOURCE) {
     cairo_push_group(mContext);
       cairo_new_path(mContext);
       cairo_rectangle(mContext, 0, 0, aDest.Width(), aDest.Height());
       cairo_set_source(mContext, pat);
@@ -364,18 +363,16 @@ DrawTargetCairo::DrawSurface(SourceSurfa
     cairo_clip(mContext);
     cairo_set_source(mContext, pat);
   }
 
   cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
 
   cairo_paint_with_alpha(mContext, aOptions.mAlpha);
 
-  cairo_restore(mContext);
-
   cairo_pattern_destroy(pat);
 }
 
 void
 DrawTargetCairo::DrawSurfaceWithShadow(SourceSurface *aSurface,
                                        const Point &aDest,
                                        const Color &aColor,
                                        const Point &aOffset,
@@ -538,45 +535,37 @@ DrawTargetCairo::CopySurface(SourceSurfa
 
   if (!aSurface || aSurface->GetType() != SURFACE_CAIRO) {
     gfxWarning() << "Unsupported surface type specified";
     return;
   }
 
   cairo_surface_t* surf = static_cast<SourceSurfaceCairo*>(aSurface)->GetSurface();
 
-  cairo_save(mContext);
-
   cairo_identity_matrix(mContext);
 
   cairo_set_source_surface(mContext, surf, aDest.x - aSource.x, aDest.y - aSource.y);
   cairo_set_operator(mContext, CAIRO_OPERATOR_SOURCE);
 
   cairo_reset_clip(mContext);
   cairo_new_path(mContext);
   cairo_rectangle(mContext, aDest.x, aDest.y, aSource.width, aSource.height);
   cairo_fill(mContext);
-
-  cairo_restore(mContext);
 }
 
 void
 DrawTargetCairo::ClearRect(const Rect& aRect)
 {
   AutoPrepareForDrawing prep(this, mContext);
 
-  cairo_save(mContext);
-
   cairo_new_path(mContext);
   cairo_set_operator(mContext, CAIRO_OPERATOR_CLEAR);
   cairo_rectangle(mContext, aRect.X(), aRect.Y(),
                   aRect.Width(), aRect.Height());
   cairo_fill(mContext);
-
-  cairo_restore(mContext);
 }
 
 void
 DrawTargetCairo::StrokeRect(const Rect &aRect,
                             const Pattern &aPattern,
                             const StrokeOptions &aStrokeOptions /* = StrokeOptions() */,
                             const DrawOptions &aOptions /* = DrawOptions() */)
 {
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -648,17 +648,17 @@ class OrderedHashSet
 
 /*** HashableValue *******************************************************************************/
 
 bool
 HashableValue::setValue(JSContext *cx, const Value &v)
 {
     if (v.isString()) {
         // Atomize so that hash() and equals() are fast and infallible.
-        JSString *str = js_AtomizeString(cx, v.toString(), DoNotInternAtom);
+        JSString *str = AtomizeString(cx, v.toString(), DoNotInternAtom);
         if (!str)
             return false;
         value = StringValue(str);
     } else if (v.isDouble()) {
         double d = v.toDouble();
         int32_t i;
         if (MOZ_DOUBLE_IS_INT32(d, &i)) {
             // Normalize int32-valued doubles to int32 for faster hashing and testing.
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -260,17 +260,17 @@ CompileRegExpObject(JSContext *cx, RegEx
     if (sourceValue.isUndefined()) {
         source = cx->runtime->emptyString;
     } else {
         /* Coerce to string and compile. */
         JSString *str = ToString(cx, sourceValue);
         if (!str)
             return false;
 
-        source = js_AtomizeString(cx, str);
+        source = AtomizeString(cx, str);
         if (!source)
             return false;
     }
 
     RegExpFlag flags = RegExpFlag(0);
     if (args.hasDefined(1)) {
         JSString *flagStr = ToString(cx, args[1]);
         if (!flagStr)
@@ -550,18 +550,18 @@ StartsWithGreedyStar(JSAtom *source)
 }
 
 static inline bool
 GetSharedForGreedyStar(JSContext *cx, JSAtom *source, RegExpFlag flags, RegExpGuard *g)
 {
     if (cx->compartment->regExps.lookupHack(source, flags, cx, g))
         return true;
 
-    JSAtom *hackedSource = js_AtomizeChars(cx, source->chars() + ArrayLength(GreedyStarChars),
-                                           source->length() - ArrayLength(GreedyStarChars));
+    JSAtom *hackedSource = AtomizeChars(cx, source->chars() + ArrayLength(GreedyStarChars),
+                                        source->length() - ArrayLength(GreedyStarChars));
     if (!hackedSource)
         return false;
 
     return cx->compartment->regExps.getHack(cx, source, hackedSource, flags, g);
 }
 
 /*
  * ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2).
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -115,17 +115,17 @@ frontend::CompileScript(JSContext *cx, H
         sc.strictModeState = StrictMode::STRICT;
 
     if (options.compileAndGo) {
         if (source) {
             /*
              * Save eval program source in script->atoms[0] for the
              * eval cache (see EvalCacheLookup in jsobj.cpp).
              */
-            JSAtom *atom = js_AtomizeString(cx, source);
+            JSAtom *atom = AtomizeString(cx, source);
             jsatomid _;
             if (!atom || !bce.makeAtomIndex(atom, &_))
                 return NULL;
         }
 
         if (callerFrame && callerFrame->isFunctionFrame()) {
             /*
              * An eval script in a caller frame needs to have its enclosing
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -89,17 +89,17 @@ FoldType(JSContext *cx, ParseNode *pn, P
             }
             break;
 
           case PNK_STRING:
             if (pn->isKind(PNK_NUMBER)) {
                 JSString *str = js_NumberToString(cx, pn->pn_dval);
                 if (!str)
                     return false;
-                pn->pn_atom = js_AtomizeString(cx, str);
+                pn->pn_atom = AtomizeString(cx, str);
                 if (!pn->pn_atom)
                     return false;
                 pn->setKind(PNK_STRING);
                 pn->setOp(JSOP_STRING);
             }
             break;
 
           default:;
@@ -272,17 +272,17 @@ FoldXMLConstants(JSContext *cx, ParseNod
             } else if (accum && pn1 != pn2) {
                 while (pn1->pn_next != pn2) {
                     pn1 = parser->freeTree(pn1);
                     --pn->pn_count;
                 }
                 pn1->setKind(PNK_XMLTEXT);
                 pn1->setOp(JSOP_STRING);
                 pn1->setArity(PN_NULLARY);
-                pn1->pn_atom = js_AtomizeString(cx, accum);
+                pn1->pn_atom = AtomizeString(cx, accum);
                 if (!pn1->pn_atom)
                     return false;
                 JS_ASSERT(pnp != &pn1->pn_next);
                 *pnp = pn1;
             }
             pnp = &pn2->pn_next;
             pn1 = *pnp;
             accum = NULL;
@@ -324,17 +324,17 @@ FoldXMLConstants(JSContext *cx, ParseNod
         JS_ASSERT(*pnp == pn1);
         while (pn1->pn_next) {
             pn1 = parser->freeTree(pn1);
             --pn->pn_count;
         }
         pn1->setKind(PNK_XMLTEXT);
         pn1->setOp(JSOP_STRING);
         pn1->setArity(PN_NULLARY);
-        pn1->pn_atom = js_AtomizeString(cx, accum);
+        pn1->pn_atom = AtomizeString(cx, accum);
         if (!pn1->pn_atom)
             return false;
         JS_ASSERT(pnp != &pn1->pn_next);
         *pnp = pn1;
     }
 
     if (pn1 && pn->pn_count == 1) {
         /*
@@ -697,17 +697,17 @@ frontend::FoldConstants(JSContext *cx, P
                 JSAtom *atom = pn2->pn_atom;
                 size_t length2 = atom->length();
                 js_strncpy(chars, atom->chars(), length2);
                 chars += length2;
             }
             JS_ASSERT(*chars == 0);
 
             /* Atomize the result string and mutate pn to refer to it. */
-            pn->pn_atom = js_AtomizeString(cx, str);
+            pn->pn_atom = AtomizeString(cx, str);
             if (!pn->pn_atom)
                 return false;
             pn->setKind(PNK_STRING);
             pn->setOp(JSOP_STRING);
             pn->setArity(PN_NULLARY);
             break;
         }
 
@@ -718,17 +718,17 @@ frontend::FoldConstants(JSContext *cx, P
                 return false;
             if (!pn1->isKind(PNK_STRING) || !pn2->isKind(PNK_STRING))
                 return true;
             RootedString left(cx, pn1->pn_atom);
             RootedString right(cx, pn2->pn_atom);
             RootedString str(cx, js_ConcatStrings(cx, left, right));
             if (!str)
                 return false;
-            pn->pn_atom = js_AtomizeString(cx, str);
+            pn->pn_atom = AtomizeString(cx, str);
             if (!pn->pn_atom)
                 return false;
             pn->setKind(PNK_STRING);
             pn->setOp(JSOP_STRING);
             pn->setArity(PN_NULLARY);
             parser->freeTree(pn1);
             parser->freeTree(pn2);
             break;
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -1043,18 +1043,18 @@ TokenStream::getXMLMarkup(TokenKind *ttp
         }
         if (targetLength == 0)
             goto bad_xml_markup;
 
         JSAtom *data;
         if (contentIndex < 0) {
             data = cx->runtime->atomState.emptyAtom;
         } else {
-            data = js_AtomizeChars(cx, tokenbuf.begin() + contentIndex,
-                                   tokenbuf.length() - contentIndex);
+            data = AtomizeChars(cx, tokenbuf.begin() + contentIndex,
+                                tokenbuf.length() - contentIndex);
             if (!data)
                 goto error;
         }
         tokenbuf.shrinkBy(tokenbuf.length() - targetLength);
         consumeKnownChar('>');
         JSAtom *target = atomize(cx, tokenbuf);
         if (!target)
             goto error;
@@ -1270,17 +1270,17 @@ TokenStream::newToken(ptrdiff_t adjust)
     tp->pos.begin.index = tp->ptr - linebase;
     tp->pos.begin.lineno = tp->pos.end.lineno = lineno;
     return tp;
 }
 
 JS_ALWAYS_INLINE JSAtom *
 TokenStream::atomize(JSContext *cx, CharBuffer &cb)
 {
-    return js_AtomizeChars(cx, cb.begin(), cb.length());
+    return AtomizeChars(cx, cb.begin(), cb.length());
 }
 
 #ifdef DEBUG
 bool
 IsTokenSane(Token *tp)
 {
     /*
      * Nb: TOK_EOL should never be used in an actual Token;  it should only be
@@ -1565,17 +1565,17 @@ TokenStream::getTokenInternal()
 
         /*
          * Identifiers containing no Unicode escapes can be atomized directly
          * from userbuf.  The rest must use the escapes converted via
          * tokenbuf before atomizing.
          */
         JSAtom *atom;
         if (!hadUnicodeEscape)
-            atom = js_AtomizeChars(cx, identStart, userbuf.addressOfNextRawChar() - identStart);
+            atom = AtomizeChars(cx, identStart, userbuf.addressOfNextRawChar() - identStart);
         else
             atom = atomize(cx, tokenbuf);
         if (!atom)
             goto error;
         tp->setName(JSOP_NAME, atom->asPropertyName());
         tt = TOK_NAME;
         goto out;
     }
--- a/js/src/jsapi-tests/testIntern.cpp
+++ b/js/src/jsapi-tests/testIntern.cpp
@@ -8,17 +8,17 @@
 #include "vm/String.h"
 
 using namespace mozilla;
 
 BEGIN_TEST(testAtomizedIsNotInterned)
 {
     /* Try to pick a string that won't be interned by other tests in this runtime. */
     static const char someChars[] = "blah blah blah? blah blah blah";
-    JSAtom *atom = js_Atomize(cx, someChars, ArrayLength(someChars));
+    JSAtom *atom = js::Atomize(cx, someChars, ArrayLength(someChars));
     CHECK(!JS_StringHasBeenInterned(cx, atom));
     CHECK(JS_InternJSString(cx, atom));
     CHECK(JS_StringHasBeenInterned(cx, atom));
     return true;
 }
 END_TEST(testAtomizedIsNotInterned)
 
 struct StringWrapper
--- a/js/src/jsapi-tests/testStringBuffer.cpp
+++ b/js/src/jsapi-tests/testStringBuffer.cpp
@@ -14,17 +14,17 @@
 
 #include "jsobjinlines.h"
 
 BEGIN_TEST(testStringBuffer_finishString)
 {
     JSString *str = JS_NewStringCopyZ(cx, "foopy");
     CHECK(str);
 
-    JSAtom *atom = js_AtomizeString(cx, str);
+    JSAtom *atom = js::AtomizeString(cx, str);
     CHECK(atom);
 
     js::StringBuffer buffer(cx);
     CHECK(buffer.append("foopy"));
 
     JSAtom *finishedAtom = buffer.finishAtom();
     CHECK(finishedAtom);
     CHECK_EQUAL(atom, finishedAtom);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -890,17 +890,17 @@ JSRuntime::init(uint32_t maxbytes)
         !compartments.append(atomsCompartment)) {
         Foreground::delete_(atomsCompartment);
         return false;
     }
 
     atomsCompartment->isSystemCompartment = true;
     atomsCompartment->setGCLastBytes(8192, 8192, GC_NORMAL);
 
-    if (!js_InitAtomState(this))
+    if (!InitAtomState(this))
         return false;
 
     if (!InitRuntimeNumberState(this))
         return false;
 
     dtoaState = js_NewDtoaState();
     if (!dtoaState)
         return false;
@@ -957,17 +957,17 @@ JSRuntime::~JSRuntime()
         }
         fprintf(stderr,
 "JS API usage error: %u context%s left in runtime upon JS_DestroyRuntime.\n",
                 cxcount, (cxcount == 1) ? "" : "s");
     }
 #endif
 
     FinishRuntimeNumberState(this);
-    js_FinishAtomState(this);
+    FinishAtomState(this);
 
     if (dtoaState)
         js_DestroyDtoaState(dtoaState);
 
     js_FinishGC(this);
 #ifdef JS_THREADSAFE
     if (gcLock)
         PR_DestroyLock(gcLock);
@@ -3637,25 +3637,25 @@ JS_LookupElement(JSContext *cx, JSObject
         return false;
     return JS_LookupPropertyById(cx, obj, id, vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_LookupProperty(JSContext *cx, JSObject *objArg, const char *name, jsval *vp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = js_Atomize(cx, name, strlen(name));
+    JSAtom *atom = Atomize(cx, name, strlen(name));
     return atom && JS_LookupPropertyById(cx, obj, AtomToId(atom), vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_LookupUCProperty(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen, jsval *vp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_LookupPropertyById(cx, obj, AtomToId(atom), vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_LookupPropertyWithFlagsById(JSContext *cx, JSObject *objArg, jsid id_, unsigned flags,
                                JSObject **objpArg, jsval *vp)
 {
     RootedObject obj(cx, objArg);
@@ -3678,17 +3678,17 @@ JS_LookupPropertyWithFlagsById(JSContext
     return true;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_LookupPropertyWithFlags(JSContext *cx, JSObject *objArg, const char *name, unsigned flags, jsval *vp)
 {
     RootedObject obj(cx, objArg);
     JSObject *obj2;
-    JSAtom *atom = js_Atomize(cx, name, strlen(name));
+    JSAtom *atom = Atomize(cx, name, strlen(name));
     return atom && JS_LookupPropertyWithFlagsById(cx, obj, AtomToId(atom), flags, &obj2, vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_HasPropertyById(JSContext *cx, JSObject *objArg, jsid idArg, JSBool *foundp)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, idArg);
@@ -3711,25 +3711,25 @@ JS_HasElement(JSContext *cx, JSObject *o
         return false;
     return JS_HasPropertyById(cx, obj, id, foundp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_HasProperty(JSContext *cx, JSObject *objArg, const char *name, JSBool *foundp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = js_Atomize(cx, name, strlen(name));
+    JSAtom *atom = Atomize(cx, name, strlen(name));
     return atom && JS_HasPropertyById(cx, obj, AtomToId(atom), foundp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_HasUCProperty(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen, JSBool *foundp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_HasPropertyById(cx, obj, AtomToId(atom), foundp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_AlreadyHasOwnPropertyById(JSContext *cx, JSObject *objArg, jsid id_, JSBool *foundp)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, id_);
@@ -3764,26 +3764,26 @@ JS_AlreadyHasOwnElement(JSContext *cx, J
         return false;
     return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_AlreadyHasOwnProperty(JSContext *cx, JSObject *objArg, const char *name, JSBool *foundp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = js_Atomize(cx, name, strlen(name));
+    JSAtom *atom = Atomize(cx, name, strlen(name));
     return atom && JS_AlreadyHasOwnPropertyById(cx, obj, AtomToId(atom), foundp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_AlreadyHasOwnUCProperty(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen,
                            JSBool *foundp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_AlreadyHasOwnPropertyById(cx, obj, AtomToId(atom), foundp);
 }
 
 /* Wrapper functions to create wrappers with no corresponding JSJitInfo from API
  * function arguments.
  */
 static JSPropertyOpWrapper
 GetterWrapper(JSPropertyOp getter)
@@ -3911,17 +3911,17 @@ DefineProperty(JSContext *cx, JSHandleOb
     AutoRooterGetterSetter gsRoot(cx, attrs, const_cast<JSPropertyOp *>(&getter.op),
                                   const_cast<JSStrictPropertyOp *>(&setter.op));
     RootedId id(cx);
 
     if (attrs & JSPROP_INDEX) {
         id = INT_TO_JSID(intptr_t(name));
         attrs &= ~JSPROP_INDEX;
     } else {
-        JSAtom *atom = js_Atomize(cx, name, strlen(name));
+        JSAtom *atom = Atomize(cx, name, strlen(name));
         if (!atom)
             return JS_FALSE;
         id = AtomToId(atom);
     }
 
     return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, flags, tinyid);
 }
 
@@ -3945,17 +3945,17 @@ JS_DefinePropertyWithTinyId(JSContext *c
 
 static JSBool
 DefineUCProperty(JSContext *cx, JSHandleObject obj, const jschar *name, size_t namelen,
                  const Value &value_, PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
                  unsigned flags, int tinyid)
 {
     RootedValue value(cx, value_);
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
-    JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return false;
     RootedId id(cx, AtomToId(atom));
     return DefinePropertyById(cx, obj, id, value, GetterWrapper(getter),
                               SetterWrapper(setter), attrs, flags, tinyid);
 }
 
 JS_PUBLIC_API(JSBool)
@@ -4129,50 +4129,50 @@ JS_GetPropertyAttrsGetterAndSetterById(J
     return true;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetPropertyAttributes(JSContext *cx, JSObject *objArg, const char *name,
                          unsigned *attrsp, JSBool *foundp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = js_Atomize(cx, name, strlen(name));
+    JSAtom *atom = Atomize(cx, name, strlen(name));
     return atom && JS_GetPropertyAttrsGetterAndSetterById(cx, obj, AtomToId(atom),
                                                           attrsp, foundp, NULL, NULL);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetUCPropertyAttributes(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen,
                            unsigned *attrsp, JSBool *foundp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_GetPropertyAttrsGetterAndSetterById(cx, obj, AtomToId(atom),
                                                           attrsp, foundp, NULL, NULL);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *objArg, const char *name,
                                    unsigned *attrsp, JSBool *foundp,
                                    JSPropertyOp *getterp, JSStrictPropertyOp *setterp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = js_Atomize(cx, name, strlen(name));
+    JSAtom *atom = Atomize(cx, name, strlen(name));
     return atom && JS_GetPropertyAttrsGetterAndSetterById(cx, obj, AtomToId(atom),
                                                           attrsp, foundp, getterp, setterp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetUCPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *objArg,
                                      const jschar *name, size_t namelen,
                                      unsigned *attrsp, JSBool *foundp,
                                      JSPropertyOp *getterp, JSStrictPropertyOp *setterp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_GetPropertyAttrsGetterAndSetterById(cx, obj, AtomToId(atom),
                                                           attrsp, foundp, getterp, setterp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetOwnPropertyDescriptor(JSContext *cx, JSObject *objArg, jsid idArg, jsval *vp)
 {
     RootedObject obj(cx, objArg);
@@ -4203,27 +4203,27 @@ SetPropertyAttributesById(JSContext *cx,
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetPropertyAttributes(JSContext *cx, JSObject *objArg, const char *name,
                          unsigned attrs, JSBool *foundp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = js_Atomize(cx, name, strlen(name));
+    JSAtom *atom = Atomize(cx, name, strlen(name));
     RootedId id(cx, AtomToId(atom));
     return atom && SetPropertyAttributesById(cx, obj, id, attrs, foundp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetUCPropertyAttributes(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen,
                            unsigned attrs, JSBool *foundp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     RootedId id(cx, AtomToId(atom));
     return atom && SetPropertyAttributesById(cx, obj, id, attrs, foundp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetPropertyById(JSContext *cx, JSObject *objArg, jsid idArg, jsval *vp)
 {
     return JS_ForwardGetPropertyTo(cx, objArg, idArg, objArg, vp);
@@ -4308,33 +4308,33 @@ JS_GetElementIfPresent(JSContext *cx, JS
     *present = isPresent;
     return true;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetProperty(JSContext *cx, JSObject *objArg, const char *name, jsval *vp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = js_Atomize(cx, name, strlen(name));
+    JSAtom *atom = Atomize(cx, name, strlen(name));
     return atom && JS_GetPropertyById(cx, obj, AtomToId(atom), vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetPropertyDefault(JSContext *cx, JSObject *objArg, const char *name, jsval def, jsval *vp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = js_Atomize(cx, name, strlen(name));
+    JSAtom *atom = Atomize(cx, name, strlen(name));
     return atom && JS_GetPropertyByIdDefault(cx, obj, AtomToId(atom), def, vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetUCProperty(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen, jsval *vp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_GetPropertyById(cx, obj, AtomToId(atom), vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetMethodById(JSContext *cx, JSObject *objArg, jsid idArg, JSObject **objp, jsval *vp)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, idArg);
@@ -4351,17 +4351,17 @@ JS_GetMethodById(JSContext *cx, JSObject
     if (objp)
         *objp = obj;
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetMethod(JSContext *cx, JSObject *objArg, const char *name, JSObject **objp, jsval *vp)
 {
-    JSAtom *atom = js_Atomize(cx, name, strlen(name));
+    JSAtom *atom = Atomize(cx, name, strlen(name));
     return atom && JS_GetMethodById(cx, objArg, AtomToId(atom), objp, vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetPropertyById(JSContext *cx, JSObject *objArg, jsid idArg, jsval *vp)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, idArg);
@@ -4394,25 +4394,25 @@ JS_SetElement(JSContext *cx, JSObject *o
     *vp = value;
     return true;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetProperty(JSContext *cx, JSObject *objArg, const char *name, jsval *vp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = js_Atomize(cx, name, strlen(name));
+    JSAtom *atom = Atomize(cx, name, strlen(name));
     return atom && JS_SetPropertyById(cx, obj, AtomToId(atom), vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetUCProperty(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen, jsval *vp)
 {
     RootedObject obj(cx, objArg);
-    JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     return atom && JS_SetPropertyById(cx, obj, AtomToId(atom), vp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_DeletePropertyById2(JSContext *cx, JSObject *objArg, jsid id, jsval *rval)
 {
     RootedObject obj(cx, objArg);
     AssertHeapIsIdle(cx);
@@ -4455,17 +4455,17 @@ JS_DeleteElement2(JSContext *cx, JSObjec
 JS_PUBLIC_API(JSBool)
 JS_DeleteProperty2(JSContext *cx, JSObject *objArg, const char *name, jsval *rval)
 {
     RootedObject obj(cx, objArg);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
 
-    JSAtom *atom = js_Atomize(cx, name, strlen(name));
+    JSAtom *atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return false;
 
     RootedValue value(cx);
     if (!obj->deleteByValue(cx, StringValue(atom), &value, false))
         return false;
 
     *rval = value;
@@ -4475,17 +4475,17 @@ JS_DeleteProperty2(JSContext *cx, JSObje
 JS_PUBLIC_API(JSBool)
 JS_DeleteUCProperty2(JSContext *cx, JSObject *objArg, const jschar *name, size_t namelen, jsval *rval)
 {
     RootedObject obj(cx, objArg);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
 
-    JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return false;
 
     RootedValue value(cx);
     if (!obj->deleteByValue(cx, StringValue(atom), &value, false))
         return false;
 
     *rval = value;
@@ -4829,17 +4829,17 @@ JS_NewFunction(JSContext *cx, JSNative n
 
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, parent);
 
     if (!name) {
         atom = NULL;
     } else {
-        atom = js_Atomize(cx, name, strlen(name));
+        atom = Atomize(cx, name, strlen(name));
         if (!atom)
             return NULL;
     }
 
     return js_NewFunction(cx, NULL, native, nargs, flags, parent, atom);
 }
 
 JS_PUBLIC_API(JSFunction *)
@@ -4990,17 +4990,17 @@ JS_DefineFunctions(JSContext *cx, JSObje
     JSFunction *fun;
 
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     for (; fs->name; fs++) {
         flags = fs->flags;
 
-        RootedAtom atom(cx, js_Atomize(cx, fs->name, strlen(fs->name)));
+        RootedAtom atom(cx, Atomize(cx, fs->name, strlen(fs->name)));
         if (!atom)
             return JS_FALSE;
 
         Rooted<jsid> id(cx, AtomToId(atom));
 
         /*
          * Define a generic arity N+1 static method for the arity N prototype
          * method if flags contains JSFUN_GENERIC_NATIVE.
@@ -5038,34 +5038,34 @@ JS_PUBLIC_API(JSFunction *)
 JS_DefineFunction(JSContext *cx, JSObject *objArg, const char *name, JSNative call,
                   unsigned nargs, unsigned attrs)
 {
     RootedObject obj(cx, objArg);
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
-    JSAtom *atom = js_Atomize(cx, name, strlen(name));
+    JSAtom *atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return NULL;
     Rooted<jsid> id(cx, AtomToId(atom));
     return js_DefineFunction(cx, obj, id, call, nargs, attrs);
 }
 
 JS_PUBLIC_API(JSFunction *)
 JS_DefineUCFunction(JSContext *cx, JSObject *objArg,
                     const jschar *name, size_t namelen, JSNative call,
                     unsigned nargs, unsigned attrs)
 {
     RootedObject obj(cx, objArg);
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
-    JSAtom *atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return NULL;
     Rooted<jsid> id(cx, AtomToId(atom));
     return js_DefineFunction(cx, obj, id, call, nargs, attrs);
 }
 
 extern JS_PUBLIC_API(JSFunction *)
 JS_DefineFunctionById(JSContext *cx, JSObject *objArg, jsid id_, JSNative call,
@@ -5473,25 +5473,25 @@ JS::CompileFunction(JSContext *cx, Handl
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, options.principals, options.originPrincipals);
     AutoLastFrameCheck lfc(cx);
 
     RootedAtom funAtom(cx);
     if (name) {
-        funAtom = js_Atomize(cx, name, strlen(name));
+        funAtom = Atomize(cx, name, strlen(name));
         if (!funAtom)
             return NULL;
     }
 
     Bindings bindings;
     for (unsigned i = 0; i < nargs; i++) {
         uint16_t dummy;
-        RootedAtom argAtom(cx, js_Atomize(cx, argnames[i], strlen(argnames[i])));
+        RootedAtom argAtom(cx, Atomize(cx, argnames[i], strlen(argnames[i])));
         if (!argAtom || !bindings.addArgument(cx, argAtom, &dummy))
             return NULL;
     }
 
     RootedFunction fun(cx, js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, obj, funAtom));
     if (!fun)
         return NULL;
 
@@ -5857,17 +5857,17 @@ JS_CallFunctionName(JSContext *cx, JSObj
 {
     RootedObject obj(cx, objArg);
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, JSValueArray(argv, argc));
     AutoLastFrameCheck lfc(cx);
 
-    JSAtom *atom = js_Atomize(cx, name, strlen(name));
+    JSAtom *atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return false;
 
     RootedValue v(cx);
     RootedId id(cx, AtomToId(atom));
     return GetMethod(cx, obj, id, 0, &v) &&
            Invoke(cx, ObjectOrNullValue(obj), v, argc, argv, rval);
 }
@@ -6051,33 +6051,33 @@ INTERNED_STRING_TO_JSID(JSContext *cx, J
     return AtomToId(&str->asAtom());
 }
 
 JS_PUBLIC_API(JSString *)
 JS_InternJSString(JSContext *cx, JSString *str)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    JSAtom *atom = js_AtomizeString(cx, str, InternAtom);
+    JSAtom *atom = AtomizeString(cx, str, InternAtom);
     JS_ASSERT_IF(atom, JS_StringHasBeenInterned(cx, atom));
     return atom;
 }
 
 JS_PUBLIC_API(JSString *)
 JS_InternString(JSContext *cx, const char *s)
 {
     return JS_InternStringN(cx, s, strlen(s));
 }
 
 JS_PUBLIC_API(JSString *)
 JS_InternStringN(JSContext *cx, const char *s, size_t length)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    JSAtom *atom = js_Atomize(cx, s, length, InternAtom);
+    JSAtom *atom = Atomize(cx, s, length, InternAtom);
     JS_ASSERT_IF(atom, JS_StringHasBeenInterned(cx, atom));
     return atom;
 }
 
 JS_PUBLIC_API(JSString *)
 JS_NewUCString(JSContext *cx, jschar *chars, size_t length)
 {
     AssertHeapIsIdle(cx);
@@ -6103,17 +6103,17 @@ JS_NewUCStringCopyZ(JSContext *cx, const
     return js_NewStringCopyZ(cx, s);
 }
 
 JS_PUBLIC_API(JSString *)
 JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    JSAtom *atom = js_AtomizeChars(cx, s, length, InternAtom);
+    JSAtom *atom = AtomizeChars(cx, s, length, InternAtom);
     JS_ASSERT_IF(atom, JS_StringHasBeenInterned(cx, atom));
     return atom;
 }
 
 JS_PUBLIC_API(JSString *)
 JS_InternUCString(JSContext *cx, const jschar *s)
 {
     return JS_InternUCStringN(cx, s, js_strlen(s));
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -144,30 +144,30 @@ const char js_setter_str[]          = "s
  * For a browser build from 2007-08-09 after the browser starts up there are
  * just 55 double atoms, but over 15000 string atoms. Not to penalize more
  * economical embeddings allocating too much memory initially we initialize
  * atomized strings with just 1K entries.
  */
 #define JS_STRING_HASH_COUNT   1024
 
 JSBool
-js_InitAtomState(JSRuntime *rt)
+js::InitAtomState(JSRuntime *rt)
 {
     JSAtomState *state = &rt->atomState;
 
     JS_ASSERT(!state->atoms.initialized());
     if (!state->atoms.init(JS_STRING_HASH_COUNT))
         return false;
 
     JS_ASSERT(state->atoms.initialized());
     return true;
 }
 
 void
-js_FinishAtomState(JSRuntime *rt)
+js::FinishAtomState(JSRuntime *rt)
 {
     JSAtomState *state = &rt->atomState;
 
     if (!state->atoms.initialized()) {
         /*
          * We are called with uninitialized state when JS_NewRuntime fails and
          * calls JS_DestroyRuntime on a partially initialized runtime.
          */
@@ -180,18 +180,18 @@ js_FinishAtomState(JSRuntime *rt)
 }
 
 bool
 js::InitCommonAtoms(JSContext *cx)
 {
     JSAtomState *state = &cx->runtime->atomState;
     JSAtom **atoms = state->commonAtomsStart();
     for (size_t i = 0; i < ArrayLength(js_common_atom_names); i++, atoms++) {
-        JSAtom *atom = js_Atomize(cx, js_common_atom_names[i], strlen(js_common_atom_names[i]),
-                                  InternAtom);
+        JSAtom *atom = Atomize(cx, js_common_atom_names[i], strlen(js_common_atom_names[i]),
+                               InternAtom);
         if (!atom)
             return false;
         *atoms = atom->asPropertyName();
     }
 
     cx->runtime->emptyString = state->emptyAtom;
     return true;
 }
@@ -322,25 +322,18 @@ AtomizeInline(JSContext *cx, const jscha
     if (!atoms.relookupOrAdd(p, lookup, AtomStateEntry((JSAtom *) key, bool(ib)))) {
         JS_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report */
         return NULL;
     }
 
     return key->morphAtomizedStringIntoAtom();
 }
 
-static JSAtom *
-Atomize(JSContext *cx, const jschar **pchars, size_t length,
-        InternBehavior ib, OwnCharsBehavior ocb = CopyChars)
-{
-    return AtomizeInline(cx, pchars, length, ib, ocb);
-}
-
 JSAtom *
-js_AtomizeString(JSContext *cx, JSString *str, InternBehavior ib)
+js::AtomizeString(JSContext *cx, JSString *str, InternBehavior ib)
 {
     if (str->isAtom()) {
         JSAtom &atom = str->asAtom();
         /* N.B. static atoms are effectively always interned. */
         if (ib != InternAtom || js::StaticStrings::isStatic(&atom))
             return &atom;
 
         AtomSet &atoms = cx->runtime->atomState.atoms;
@@ -353,33 +346,33 @@ js_AtomizeString(JSContext *cx, JSString
     }
 
     size_t length = str->length();
     const jschar *chars = str->getChars(cx);
     if (!chars)
         return NULL;
 
     JS_ASSERT(length <= JSString::MAX_LENGTH);
-    return Atomize(cx, &chars, length, ib);
+    return AtomizeInline(cx, &chars, length, ib);
 }
 
 JSAtom *
-js_Atomize(JSContext *cx, const char *bytes, size_t length, InternBehavior ib, FlationCoding fc)
+js::Atomize(JSContext *cx, const char *bytes, size_t length, InternBehavior ib, FlationCoding fc)
 {
     CHECK_REQUEST(cx);
 
     if (!JSString::validateLength(cx, length))
         return NULL;
 
     /*
      * Avoiding the malloc in InflateString on shorter strings saves us
      * over 20,000 malloc calls on mozilla browser startup. This compares to
      * only 131 calls where the string is longer than a 31 char (net) buffer.
      * The vast majority of atomized strings are already in the hashtable. So
-     * js_AtomizeString rarely has to copy the temp string we make.
+     * js::AtomizeString rarely has to copy the temp string we make.
      */
     static const unsigned ATOMIZE_BUF_MAX = 32;
     jschar inflated[ATOMIZE_BUF_MAX];
     size_t inflatedLength = ATOMIZE_BUF_MAX - 1;
 
     const jschar *chars;
     OwnCharsBehavior ocb = CopyChars;
     if (length < ATOMIZE_BUF_MAX) {
@@ -392,86 +385,57 @@ js_Atomize(JSContext *cx, const char *by
     } else {
         inflatedLength = length;
         chars = InflateString(cx, bytes, &inflatedLength, fc);
         if (!chars)
             return NULL;
         ocb = TakeCharOwnership;
     }
 
-    JSAtom *atom = Atomize(cx, &chars, inflatedLength, ib, ocb);
+    JSAtom *atom = AtomizeInline(cx, &chars, inflatedLength, ib, ocb);
     if (ocb == TakeCharOwnership && chars)
         cx->free_((void *)chars);
     return atom;
 }
 
 JSAtom *
-js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, InternBehavior ib)
+js::AtomizeChars(JSContext *cx, const jschar *chars, size_t length, InternBehavior ib)
 {
     CHECK_REQUEST(cx);
 
     if (!JSString::validateLength(cx, length))
         return NULL;
 
     return AtomizeInline(cx, &chars, length, ib);
 }
 
-JSAtom *
-js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length)
-{
-    if (JSAtom *atom = cx->runtime->staticStrings.lookup(chars, length))
-        return atom;
-    if (AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(AtomHasher::Lookup(chars, length)))
-        return p->asPtr();
-    return NULL;
-}
-
-#ifdef DEBUG
-JS_FRIEND_API(void)
-js_DumpAtoms(JSContext *cx, FILE *fp)
-{
-    JSAtomState *state = &cx->runtime->atomState;
-
-    fprintf(fp, "atoms table contents:\n");
-    unsigned number = 0;
-    for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) {
-        AtomStateEntry entry = r.front();
-        fprintf(fp, "%3u ", number++);
-        JSAtom *key = entry.asPtr();
-        FileEscapedString(fp, key, '"');
-        if (entry.isTagged())
-            fputs(" interned", fp);
-        putc('\n', fp);
-    }
-    putc('\n', fp);
-}
-#endif
-
 namespace js {
 
 bool
 IndexToIdSlow(JSContext *cx, uint32_t index, jsid *idp)
 {
     JS_ASSERT(index > JSID_INT_MAX);
 
     jschar buf[UINT32_CHAR_BUFFER_LENGTH];
     RangedPtr<jschar> end(ArrayEnd(buf), buf, ArrayEnd(buf));
     RangedPtr<jschar> start = BackfillIndexInCharBuffer(index, end);
 
-    JSAtom *atom = js_AtomizeChars(cx, start.get(), end - start);
+    JSAtom *atom = AtomizeChars(cx, start.get(), end - start);
     if (!atom)
         return false;
 
     *idp = JSID_FROM_BITS((size_t)atom);
     return true;
 }
 
+} /* namespace js */
+
 bool
-InternNonIntElementId(JSContext *cx, JSObject *obj, const Value &idval,
-                      jsid *idp, MutableHandleValue vp)
+js::InternNonIntElementId(JSContext *cx, JSObject *obj, const Value &idval,
+                          jsid *idp, MutableHandleValue vp)
 {
 #if JS_HAS_XML_SUPPORT
     if (idval.isObject()) {
         JSObject *idobj = &idval.toObject();
 
         if (obj && obj->isXML()) {
             *idp = OBJECT_TO_JSID(idobj);
             vp.set(idval);
@@ -495,18 +459,16 @@ InternNonIntElementId(JSContext *cx, JSO
     if (!atom)
         return false;
 
     *idp = AtomToId(atom);
     vp.setString(atom);
     return true;
 }
 
-} /* namespace js */
-
 template<XDRMode mode>
 bool
 js::XDRAtom(XDRState<mode> *xdr, JSAtom **atomp)
 {
     if (mode == XDR_ENCODE) {
         uint32_t nchars = (*atomp)->length();
         if (!xdr->codeUint32(&nchars))
             return false;
@@ -523,17 +485,17 @@ js::XDRAtom(XDRState<mode> *xdr, JSAtom 
     if (!xdr->codeUint32(&nchars))
         return false;
 
     JSContext *cx = xdr->cx();
     JSAtom *atom;
 #if IS_LITTLE_ENDIAN
     /* Directly access the little endian chars in the XDR buffer. */
     const jschar *chars = reinterpret_cast<const jschar *>(xdr->buf.read(nchars * sizeof(jschar)));
-    atom = js_AtomizeChars(cx, chars, nchars);
+    atom = AtomizeChars(cx, chars, nchars);
 #else
     /*
      * We must copy chars to a temporary buffer to convert between little and
      * big endian data.
      */
     jschar *chars;
     jschar stackChars[256];
     if (nchars <= ArrayLength(stackChars)) {
@@ -545,17 +507,17 @@ js::XDRAtom(XDRState<mode> *xdr, JSAtom 
          * chunk size.
          */
         chars = static_cast<jschar *>(cx->runtime->malloc_(nchars * sizeof(jschar)));
         if (!chars)
             return false;
     }
 
     JS_ALWAYS_TRUE(xdr->codeChars(chars, nchars));
-    atom = js_AtomizeChars(cx, chars, nchars);
+    atom = AtomizeChars(cx, chars, nchars);
     if (chars != stackChars)
         Foreground::free_(chars);
 #endif /* !IS_LITTLE_ENDIAN */
 
     if (!atom)
         return false;
     *atomp = atom;
     return true;
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -320,37 +320,36 @@ extern const char   js_undefined_str[];
 extern const char   js_close_str[];
 extern const char   js_send_str[];
 #endif
 
 /* Constant strings that are not atomized. */
 extern const char   js_getter_str[];
 extern const char   js_setter_str[];
 
+namespace js {
+
 /*
  * Initialize atom state. Return true on success, false on failure to allocate
  * memory. The caller must zero rt->atomState before calling this function and
  * only call it after js_InitGC successfully returns.
  */
 extern JSBool
-js_InitAtomState(JSRuntime *rt);
+InitAtomState(JSRuntime *rt);
 
 /*
  * Free and clear atom state including any interned string atoms. This
  * function must be called before js_FinishGC.
  */
 extern void
-js_FinishAtomState(JSRuntime *rt);
+FinishAtomState(JSRuntime *rt);
 
 /*
  * Atom tracing and garbage collection hooks.
  */
-
-namespace js {
-
 extern void
 MarkAtomState(JSTracer *trc, bool markAll);
 
 extern void
 SweepAtomState(JSRuntime *rt);
 
 extern bool
 InitCommonAtoms(JSContext *cx);
@@ -360,45 +359,27 @@ FinishCommonAtoms(JSRuntime *rt);
 
 /* N.B. must correspond to boolean tagging behavior. */
 enum InternBehavior
 {
     DoNotInternAtom = false,
     InternAtom = true
 };
 
-}  /* namespace js */
-
 extern JSAtom *
-js_Atomize(JSContext *cx, const char *bytes, size_t length,
-           js::InternBehavior ib = js::DoNotInternAtom,
-           js::FlationCoding fc = js::NormalEncoding);
-
-extern JSAtom *
-js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length,
-                js::InternBehavior ib = js::DoNotInternAtom);
+Atomize(JSContext *cx, const char *bytes, size_t length,
+        js::InternBehavior ib = js::DoNotInternAtom,
+        js::FlationCoding fc = js::NormalEncoding);
 
 extern JSAtom *
-js_AtomizeString(JSContext *cx, JSString *str, js::InternBehavior ib = js::DoNotInternAtom);
-
-/*
- * Return an existing atom for the given char array or null if the char
- * sequence is currently not atomized.
- */
-extern JSAtom *
-js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length);
+AtomizeChars(JSContext *cx, const jschar *chars, size_t length,
+             js::InternBehavior ib = js::DoNotInternAtom);
 
-#ifdef DEBUG
-
-extern JS_FRIEND_API(void)
-js_DumpAtoms(JSContext *cx, FILE *fp);
-
-#endif
-
-namespace js {
+extern JSAtom *
+AtomizeString(JSContext *cx, JSString *str, js::InternBehavior ib = js::DoNotInternAtom);
 
 inline JSAtom *
 ToAtom(JSContext *cx, const js::Value &v);
 
 bool
 InternNonIntElementId(JSContext *cx, JSObject *obj, const Value &idval,
                       jsid *idp, MutableHandleValue vp);
 
--- a/js/src/jsatominlines.h
+++ b/js/src/jsatominlines.h
@@ -29,25 +29,25 @@ namespace js {
 inline JSAtom *
 ToAtom(JSContext *cx, const js::Value &v)
 {
     if (!v.isString()) {
         JSString *str = js::ToStringSlow(cx, v);
         if (!str)
             return NULL;
         JS::Anchor<JSString *> anchor(str);
-        return js_AtomizeString(cx, str);
+        return AtomizeString(cx, str);
     }
 
     JSString *str = v.toString();
     if (str->isAtom())
         return &str->asAtom();
 
     JS::Anchor<JSString *> anchor(str);
-    return js_AtomizeString(cx, str);
+    return AtomizeString(cx, str);
 }
 
 inline bool
 ValueToId(JSContext* cx, JSObject *obj, const Value &v, jsid *idp)
 {
     int32_t i;
     if (ValueFitsInInt32(v, &i) && INT_FITS_IN_JSID(i)) {
         *idp = INT_TO_JSID(i);
--- a/js/src/jsclone.cpp
+++ b/js/src/jsclone.cpp
@@ -877,17 +877,17 @@ JSStructuredCloneReader::readId(jsid *id
     if (tag == SCTAG_INDEX) {
         *idp = INT_TO_JSID(int32_t(data));
         return true;
     }
     if (tag == SCTAG_STRING) {
         JSString *str = readString(data);
         if (!str)
             return false;
-        JSAtom *atom = js_AtomizeString(context(), str);
+        JSAtom *atom = AtomizeString(context(), str);
         if (!atom)
             return false;
         *idp = NON_INTEGER_ATOM_TO_JSID(atom);
         return true;
     }
     if (tag == SCTAG_NULL) {
         *idp = JSID_VOID;
         return true;
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -536,17 +536,17 @@ js_ReportErrorVA(JSContext *cx, unsigned
 
 namespace js {
 
 /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
 void
 ReportUsageError(JSContext *cx, HandleObject callee, const char *msg)
 {
     const char *usageStr = "usage";
-    PropertyName *usageAtom = js_Atomize(cx, usageStr, strlen(usageStr))->asPropertyName();
+    PropertyName *usageAtom = Atomize(cx, usageStr, strlen(usageStr))->asPropertyName();
     DebugOnly<Shape *> shape = callee->nativeLookup(cx, NameToId(usageAtom));
     JS_ASSERT(!shape->configurable());
     JS_ASSERT(!shape->writable());
     JS_ASSERT(shape->hasDefaultGetter());
 
     jsval usage;
     if (!JS_LookupProperty(cx, callee, "usage", &usage))
         return;
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -476,16 +476,18 @@ JSCompartment::discardJitCode(FreeOp *fo
 
             /*
              * Use counts for scripts are reset on GC. After discarding code we
              * need to let it warm back up to get information such as which
              * opcodes are setting array holes or accessing getter properties.
              */
             script->resetUseCount();
         }
+
+        types.sweepCompilerOutputs(fop);
     }
 
 #endif /* JS_METHODJIT */
 }
 
 void
 JSCompartment::sweep(FreeOp *fop, bool releaseTypes)
 {
@@ -568,20 +570,16 @@ JSCompartment::sweep(FreeOp *fop, bool r
                 JSScript *script = i.get<JSScript>();
                 script->clearAnalysis();
             }
         }
 
         {
             gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_FREE_TI_ARENA);
             rt->freeLifoAlloc.transferFrom(&oldAlloc);
-            if (types.constrainedOutputs) {
-                fop->delete_(types.constrainedOutputs);
-                types.constrainedOutputs = NULL;
-            }
         }
     }
 
     active = false;
 }
 
 /*
  * Remove dead wrappers from the table. We must sweep all compartments, since
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -841,16 +841,18 @@ JS_GetPropertyDescArray(JSContext *cx, J
         if (!Proxy::enumerate(cx, obj, props))
             return false;
 
         pd = (JSPropertyDesc *)cx->calloc_(props.length() * sizeof(JSPropertyDesc));
         if (!pd)
             return false;
 
         for (i = 0; i < props.length(); ++i) {
+            pd[i].id = JSVAL_NULL;
+            pd[i].value = JSVAL_NULL;
             if (!js_AddRoot(cx, &pd[i].id, NULL))
                 goto bad;
             pd[i].id = IdToValue(props[i]);
             if (!js_AddRoot(cx, &pd[i].value, NULL))
                 goto bad;
             if (!Proxy::get(cx, obj, obj, props[i], &pd[i].value))
                 goto bad;
         }
@@ -876,16 +878,19 @@ JS_GetPropertyDescArray(JSContext *cx, J
         pda->array = NULL;
         return true;
     }
 
     pd = (JSPropertyDesc *)cx->malloc_(obj->propertyCount() * sizeof(JSPropertyDesc));
     if (!pd)
         return false;
     for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) {
+        pd[i].id = JSVAL_NULL;
+        pd[i].value = JSVAL_NULL;
+        pd[i].alias = JSVAL_NULL;
         if (!js_AddRoot(cx, &pd[i].id, NULL))
             goto bad;
         if (!js_AddRoot(cx, &pd[i].value, NULL))
             goto bad;
         Shape *shape = const_cast<Shape *>(&r.front());
         if (!GetPropertyDesc(cx, obj, shape, &pd[i]))
             goto bad;
         if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL))
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -230,17 +230,17 @@ JS_FRIEND_API(void)
 JS_TraceShapeCycleCollectorChildren(JSTracer *trc, void *shape)
 {
     MarkCycleCollectorChildren(trc, (Shape *)shape);
 }
 
 static bool
 DefineHelpProperty(JSContext *cx, HandleObject obj, const char *prop, const char *value)
 {
-    JSAtom *atom = js_Atomize(cx, value, strlen(value));
+    JSAtom *atom = Atomize(cx, value, strlen(value));
     if (!atom)
         return false;
     jsval v = STRING_TO_JSVAL(atom);
     return JS_DefineProperty(cx, obj, prop, v,
                              JS_PropertyStub, JS_StrictPropertyStub,
                              JSPROP_READONLY | JSPROP_PERMANENT);
 }
 
@@ -248,17 +248,17 @@ JS_FRIEND_API(bool)
 JS_DefineFunctionsWithHelp(JSContext *cx, JSObject *objArg, const JSFunctionSpecWithHelp *fs)
 {
     RootedObject obj(cx, objArg);
     JS_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     for (; fs->name; fs++) {
-        JSAtom *atom = js_Atomize(cx, fs->name, strlen(fs->name));
+        JSAtom *atom = Atomize(cx, fs->name, strlen(fs->name));
         if (!atom)
             return false;
 
         Rooted<jsid> id(cx, AtomToId(atom));
         RootedFunction fun(cx, js_DefineFunction(cx, obj, id, fs->call, fs->nargs, fs->flags));
         if (!fun)
             return false;
 
@@ -355,17 +355,17 @@ js::IsOriginalScriptFunction(JSFunction 
 JS_FRIEND_API(JSFunction *)
 js::DefineFunctionWithReserved(JSContext *cx, JSObject *objArg, const char *name, JSNative call,
                                unsigned nargs, unsigned attrs)
 {
     RootedObject obj(cx, objArg);
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
-    JSAtom *atom = js_Atomize(cx, name, strlen(name));
+    JSAtom *atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return NULL;
     Rooted<jsid> id(cx, AtomToId(atom));
     return js_DefineFunction(cx, obj, id, call, nargs, attrs, JSFunction::ExtendedFinalizeKind);
 }
 
 JS_FRIEND_API(JSFunction *)
 js::NewFunctionWithReserved(JSContext *cx, JSNative native, unsigned nargs, unsigned flags,
@@ -376,17 +376,17 @@ js::NewFunctionWithReserved(JSContext *c
     JSAtom *atom;
 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, parent);
 
     if (!name) {
         atom = NULL;
     } else {
-        atom = js_Atomize(cx, name, strlen(name));
+        atom = Atomize(cx, name, strlen(name));
         if (!atom)
             return NULL;
     }
 
     return js_NewFunction(cx, NULL, native, nargs, flags, parent, atom,
                           JSFunction::ExtendedFinalizeKind);
 }
 
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -247,16 +247,20 @@ struct ArenaLists {
     const FreeSpan *getFreeList(AllocKind thingKind) const {
         return &freeLists[thingKind];
     }
 
     ArenaHeader *getFirstArena(AllocKind thingKind) const {
         return arenaLists[thingKind].head;
     }
 
+    ArenaHeader *getFirstArenaToSweep(AllocKind thingKind) const {
+        return arenaListsToSweep[thingKind];
+    }
+
     bool arenaListsAreEmpty() const {
         for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
             /*
              * The arena cannot be empty if the background finalization is not yet
              * done.
              */
             if (backgroundFinalizeState[i] != BFS_DONE)
                 return false;
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -177,54 +177,71 @@ GCPoke(JSRuntime *rt, Value oldval)
 
 #ifdef JS_GC_ZEAL
     /* Schedule a GC to happen "soon" after a GC poke. */
     if (rt->gcZeal() == js::gc::ZealPokeValue)
         rt->gcNextScheduled = 1;
 #endif
 }
 
-/*
- * Invoke ArenaOp and CellOp on every arena and cell in a compartment which
- * have the specified thing kind.
- */
-template <class ArenaOp, class CellOp>
-void
-ForEachArenaAndCell(JSCompartment *compartment, AllocKind thingKind,
-                    ArenaOp arenaOp, CellOp cellOp)
+class ArenaIter
 {
-    size_t thingSize = Arena::thingSize(thingKind);
-    ArenaHeader *aheader = compartment->arenas.getFirstArena(thingKind);
+    ArenaHeader *aheader;
+    ArenaHeader *remainingHeader;
+
+  public:
+    ArenaIter() {
+        init();
+    }
+
+    ArenaIter(JSCompartment *comp, AllocKind kind) {
+        init(comp, kind);
+    }
 
-    for (; aheader; aheader = aheader->next) {
-        Arena *arena = aheader->getArena();
-        arenaOp(arena);
-        FreeSpan firstSpan(aheader->getFirstFreeSpan());
-        const FreeSpan *span = &firstSpan;
+    void init() {
+        aheader = NULL;
+        remainingHeader = NULL;
+    }
 
-        for (uintptr_t thing = arena->thingsStart(thingKind); ; thing += thingSize) {
-            JS_ASSERT(thing <= arena->thingsEnd());
-            if (thing == span->first) {
-                if (!span->hasNext())
-                    break;
-                thing = span->last;
-                span = span->nextSpan();
-            } else {
-                Cell *t = reinterpret_cast<Cell *>(thing);
-                cellOp(t);
-            }
+    void init(ArenaHeader *aheaderArg) {
+        aheader = aheaderArg;
+        remainingHeader = NULL;
+    }
+
+    void init(JSCompartment *comp, AllocKind kind) {
+        aheader = comp->arenas.getFirstArena(kind);
+        remainingHeader = comp->arenas.getFirstArenaToSweep(kind);
+        if (!aheader) {
+            aheader = remainingHeader;
+            remainingHeader = NULL;
         }
     }
-}
+
+    bool done() {
+        return !aheader;
+    }
+
+    ArenaHeader *get() {
+        return aheader;
+    }
+
+    void next() {
+        aheader = aheader->next;
+        if (!aheader) {
+            aheader = remainingHeader;
+            remainingHeader = NULL;
+        }
+    }
+};
 
 class CellIterImpl
 {
     size_t firstThingOffset;
     size_t thingSize;
-    ArenaHeader *aheader;
+    ArenaIter aiter;
     FreeSpan firstSpan;
     const FreeSpan *span;
     uintptr_t thing;
     Cell *cell;
 
   protected:
     CellIterImpl() {
     }
@@ -234,25 +251,25 @@ class CellIterImpl
         firstThingOffset = Arena::firstThingOffset(kind);
         thingSize = Arena::thingSize(kind);
         firstSpan.initAsEmpty();
         span = &firstSpan;
         thing = span->first;
     }
 
     void init(ArenaHeader *singleAheader) {
-        aheader = singleAheader;
-        initSpan(aheader->compartment, aheader->getAllocKind());
+        initSpan(singleAheader->compartment, singleAheader->getAllocKind());
+        aiter.init(singleAheader);
         next();
-        aheader = NULL;
+        aiter.init();
     }
 
     void init(JSCompartment *comp, AllocKind kind) {
         initSpan(comp, kind);
-        aheader = comp->arenas.getFirstArena(kind);
+        aiter.init(comp, kind);
         next();
     }
 
   public:
     bool done() const {
         return !cell;
     }
 
@@ -270,24 +287,25 @@ class CellIterImpl
         for (;;) {
             if (thing != span->first)
                 break;
             if (JS_LIKELY(span->hasNext())) {
                 thing = span->last + thingSize;
                 span = span->nextSpan();
                 break;
             }
-            if (!aheader) {
+            if (aiter.done()) {
                 cell = NULL;
                 return;
             }
+            ArenaHeader *aheader = aiter.get();
             firstSpan = aheader->getFirstFreeSpan();
             span = &firstSpan;
             thing = aheader->arenaAddress() | firstThingOffset;
-            aheader = aheader->next;
+            aiter.next();
         }
         cell = reinterpret_cast<Cell *>(thing);
         thing += thingSize;
     }
 };
 
 class CellIterUnderGC : public CellIterImpl
 {
@@ -345,16 +363,33 @@ class CellIter : public CellIterImpl
         JS_ASSERT(*counter > 0);
         --*counter;
 #endif
         if (lists)
             lists->clearFreeListInArena(kind);
     }
 };
 
+/*
+ * Invoke ArenaOp and CellOp on every arena and cell in a compartment which
+ * have the specified thing kind.
+ */
+template <class ArenaOp, class CellOp>
+void
+ForEachArenaAndCell(JSCompartment *compartment, AllocKind thingKind,
+                    ArenaOp arenaOp, CellOp cellOp)
+{
+    for (ArenaIter aiter(compartment, thingKind); !aiter.done(); aiter.next()) {
+        ArenaHeader *aheader = aiter.get();
+        arenaOp(aheader->getArena());
+        for (CellIterUnderGC iter(aheader); !iter.done(); iter.next())
+            cellOp(iter.getCell());
+    }
+}
+
 /* Signatures for ArenaOp and CellOp above. */
 
 inline void EmptyArenaOp(Arena *arena) {}
 inline void EmptyCellOp(Cell *t) {}
 
 class GCCompartmentsIter {
   private:
     JSCompartment **it, **end;
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2844,17 +2844,17 @@ TypeObject::addPropertyType(JSContext *c
     InlineAddTypeProperty(cx, this, id, GetValueType(cx, value));
 }
 
 void
 TypeObject::addPropertyType(JSContext *cx, const char *name, Type type)
 {
     jsid id = JSID_VOID;
     if (name) {
-        JSAtom *atom = js_Atomize(cx, name, strlen(name));
+        JSAtom *atom = Atomize(cx, name, strlen(name));
         if (!atom) {
             AutoEnterTypeInference enter(cx);
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
         id = AtomToId(atom);
     }
     InlineAddTypeProperty(cx, this, id, type);
@@ -5655,16 +5655,58 @@ TypeCompartment::sweep(FreeOp *fop)
      * The pending array is reset on GC, it can grow large (75+ KB) and is easy
      * to reallocate if the compartment becomes active again.
      */
     if (pendingArray)
         fop->free_(pendingArray);
 
     pendingArray = NULL;
     pendingCapacity = 0;
+
+    sweepCompilerOutputs(fop);
+}
+
+void
+TypeCompartment::sweepCompilerOutputs(FreeOp *fop)
+{
+
+    if (constrainedOutputs) {
+        bool isCompiling = compiledInfo.outputIndex != RecompileInfo::NoCompilerRunning;
+        if (isCompiling && !compartment()->activeAnalysis)
+        {
+#if DEBUG
+            for (unsigned i = 0; i < constrainedOutputs->length(); i++) {
+                CompilerOutput &co = (*constrainedOutputs)[i];
+                JS_ASSERT(!co.isValid());
+            }
+#endif
+            fop->delete_(constrainedOutputs);
+            constrainedOutputs = NULL;
+        } else {
+            // A Compilation is running and the AutoEnterCompilation class has
+            // captured an index into the constrained outputs vector and
+            // potentially created multiple types with this index.  Instead, we
+            // invalidate all compilations except the one running now.
+            size_t len = constrainedOutputs->length();
+            if (isCompiling) {
+                len--;
+                JS_ASSERT(compiledInfo.outputIndex == len);
+            }
+            for (unsigned i = 0; i < len; i++) {
+                CompilerOutput &co = (*constrainedOutputs)[i];
+                JS_ASSERT(!co.isValid());
+                co.invalidate();
+            }
+        }
+    }
+
+    if (pendingRecompiles) {
+        fop->delete_(pendingRecompiles);
+        pendingRecompiles = NULL;
+    }
 }
 
 void
 JSCompartment::sweepNewTypeObjectTable(TypeObjectSet &table)
 {
     if (table.initialized()) {
         for (TypeObjectSet::Enum e(table); !e.empty(); e.popFront()) {
             TypeObject *type = e.front();
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -1155,16 +1155,17 @@ struct TypeCompartment
     /* Monitor future effects on a bytecode. */
     void monitorBytecode(JSContext *cx, JSScript *script, uint32_t offset,
                          bool returnOnly = false);
 
     /* Mark any type set containing obj as having a generic object type. */
     void markSetsUnknown(JSContext *cx, TypeObject *obj);
 
     void sweep(FreeOp *fop);
+    void sweepCompilerOutputs(FreeOp *fop);
     void finalizeObjects();
 };
 
 enum SpewChannel {
     ISpewOps,      /* ops: New constraints and types. */
     ISpewResult,   /* result: Final type sets. */
     SPEW_COUNT
 };
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -3218,18 +3218,19 @@ BEGIN_CASE(JSOP_RETSUB)
          * pc, because pc indexes into script->trynotes.  This turns out not to
          * be necessary, but it seems clearer.  And it points out a FIXME:
          * 350509, due to Igor Bukanov.
          */
         cx->setPendingException(rval);
         goto error;
     }
     JS_ASSERT(rval.isInt32());
-    len = rval.toInt32();
-    regs.pc = script->code;
+
+    /* Increment the PC by this much. */
+    len = rval.toInt32() - int32_t(regs.pc - script->code);
 END_VARLEN_CASE
 }
 
 BEGIN_CASE(JSOP_EXCEPTION)
     PUSH_COPY(cx->getPendingException());
     cx->clearPendingException();
     CHECK_BRANCH();
 END_CASE(JSOP_EXCEPTION)
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -295,16 +295,17 @@ Snapshot(JSContext *cx, RawObject obj_, 
      */
 
     jsid *ids = props->begin();
     size_t n = props->length();
 
     AutoIdVector tmp(cx);
     if (!tmp.resize(n))
         return false;
+    PodCopy(tmp.begin(), ids, n);
 
     if (!MergeSort(ids, n, tmp.begin(), SortComparatorIds(cx)))
         return false;
 
 #endif /* JS_MORE_DETERMINISTIC */
 
     return true;
 }
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3377,17 +3377,17 @@ JSObject *
 js_InitClass(JSContext *cx, HandleObject obj, JSObject *protoProto_,
              Class *clasp, Native constructor, unsigned nargs,
              JSPropertySpec *ps, JSFunctionSpec *fs,
              JSPropertySpec *static_ps, JSFunctionSpec *static_fs,
              JSObject **ctorp, AllocKind ctorKind)
 {
     RootedObject protoProto(cx, protoProto_);
 
-    RootedAtom atom(cx, js_Atomize(cx, clasp->name, strlen(clasp->name)));
+    RootedAtom atom(cx, Atomize(cx, clasp->name, strlen(clasp->name)));
     if (!atom)
         return NULL;
 
     /*
      * All instances of the class will inherit properties from the prototype
      * object we are about to create (in DefineConstructorAndPrototype), which
      * in turn will inherit from protoProto.
      *
@@ -3883,17 +3883,17 @@ js_FindClassObject(JSContext *cx, Handle
         if (!js_GetClassObject(cx, obj, protoKey, &cobj))
             return false;
         if (cobj) {
             vp.set(ObjectValue(*cobj));
             return JS_TRUE;
         }
         id = NameToId(cx->runtime->atomState.classAtoms[protoKey]);
     } else {
-        JSAtom *atom = js_Atomize(cx, clasp->name, strlen(clasp->name));
+        JSAtom *atom = Atomize(cx, clasp->name, strlen(clasp->name));
         if (!atom)
             return false;
         id = AtomToId(atom);
     }
 
     JS_ASSERT(obj->isNative());
     RootedObject pobj(cx);
     RootedShape shape(cx);
--- a/js/src/jsonparser.cpp
+++ b/js/src/jsonparser.cpp
@@ -50,17 +50,17 @@ JSONParser::readString()
      * string directly from the source text.
      */
     RangedPtr<const jschar> start = current;
     for (; current < end; current++) {
         if (*current == '"') {
             size_t length = current - start;
             current++;
             JSFlatString *str = (ST == JSONParser::PropertyName)
-                                ? js_AtomizeChars(cx, start.get(), length)
+                                ? AtomizeChars(cx, start.get(), length)
                                 : js_NewStringCopyN(cx, start.get(), length);
             if (!str)
                 return token(OOM);
             return stringToken(str);
         }
 
         if (*current == '\\')
             break;
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -167,17 +167,17 @@ class NodeBuilder
         }
 
         userv.setObject(*userobj);
 
         RootedValue nullValue(cx, NullValue());
         RootedValue funv(cx);
         for (unsigned i = 0; i < AST_LIMIT; i++) {
             const char *name = callbackNames[i];
-            JSAtom *atom = js_Atomize(cx, name, strlen(name));
+            JSAtom *atom = Atomize(cx, name, strlen(name));
             if (!atom)
                 return false;
             RootedId id(cx, AtomToId(atom));
             if (!baseops::GetPropertyDefault(cx, userobj, id, nullValue, &funv))
                 return false;
 
             if (funv.isNullOrUndefined()) {
                 callbacks[i].setNull();
@@ -278,19 +278,19 @@ class NodeBuilder
 
     Value opt(Value v) {
         JS_ASSERT_IF(v.isMagic(), v.whyMagic() == JS_SERIALIZE_NO_NODE);
         return v.isMagic(JS_SERIALIZE_NO_NODE) ? UndefinedValue() : v;
     }
 
     bool atomValue(const char *s, Value *dst) {
         /*
-         * Bug 575416: instead of js_Atomize, lookup constant atoms in tbl file
+         * Bug 575416: instead of Atomize, lookup constant atoms in tbl file
          */
-        JSAtom *atom = js_Atomize(cx, s, strlen(s));
+        JSAtom *atom = Atomize(cx, s, strlen(s));
         if (!atom)
             return false;
 
         dst->setString(atom);
         return true;
     }
 
     bool newObject(JSObject **dst) {
@@ -412,19 +412,19 @@ class NodeBuilder
         RootedValue val(cx, val_);
         JS_ASSERT_IF(val.isMagic(), val.whyMagic() == JS_SERIALIZE_NO_NODE);
 
         /* Represent "no node" as null and ensure users are not exposed to magic values. */
         if (val.isMagic(JS_SERIALIZE_NO_NODE))
             val.setNull();
 
         /*
-         * Bug 575416: instead of js_Atomize, lookup constant atoms in tbl file
+         * Bug 575416: instead of Atomize, lookup constant atoms in tbl file
          */
-        JSAtom *atom = js_Atomize(cx, name, strlen(name));
+        JSAtom *atom = Atomize(cx, name, strlen(name));
         if (!atom)
             return false;
 
         return obj->defineProperty(cx, atom->asPropertyName(), val);
     }
 
     bool newNodeLoc(TokenPos *pos, Value *dst);
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1544,17 +1544,17 @@ class StringRegExpGuard
                 fm.patstr = cx->runtime->emptyString;
                 return true;
             }
 
             JSString *arg = ArgToRootedString(cx, args, 0);
             if (!arg)
                 return false;
 
-            fm.patstr = js_AtomizeString(cx, arg);
+            fm.patstr = AtomizeString(cx, arg);
             if (!fm.patstr)
                 return false;
         }
         return true;
     }
 
     /*
      * Attempt to match |patstr| to |textstr|. A flags argument, metachars in the
@@ -1931,17 +1931,17 @@ FindReplaceLength(JSContext *cx, RegExpS
         if (!res->createLastMatch(cx, &match))
             return false;
         JSString *str = match.toString();
 
         JSAtom *atom;
         if (str->isAtom()) {
             atom = &str->asAtom();
         } else {
-            atom = js_AtomizeString(cx, str);
+            atom = AtomizeString(cx, str);
             if (!atom)
                 return false;
         }
 
         Value v;
         if (HasDataProperty(cx, base, AtomToId(atom), &v) && v.isString()) {
             rdata.repstr = v.toString()->ensureLinear(cx);
             if (!rdata.repstr)
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -1227,17 +1227,17 @@ ParseNodeToQName(Parser *parser, ParseNo
         if (!uri) {
             Value v = StringValue(prefix);
             JSAutoByteString bytes;
             if (js_ValueToPrintable(parser->context, v, &bytes))
                 parser->reportError(pn, JSMSG_BAD_XML_NAMESPACE, bytes.ptr());
             return NULL;
         }
 
-        localName = js_AtomizeChars(parser->context, colon + 1, length - (offset + 1));
+        localName = AtomizeChars(parser->context, colon + 1, length - (offset + 1));
         if (!localName)
             return NULL;
     } else {
         if (isAttributeName) {
             /*
              * An unprefixed attribute is not in any namespace, so set prefix
              * as well as uri to the empty string.
              */
@@ -2882,17 +2882,17 @@ ToXMLName(JSContext *cx, jsval v, jsid *
             name = cx->runtime->atomState.starAtom;
             goto construct;
         }
         name = ToStringSlow(cx, v);
         if (!name)
             return NULL;
     }
 
-    atomizedName = js_AtomizeString(cx, name);
+    atomizedName = AtomizeString(cx, name);
     if (!atomizedName)
         return NULL;
 
     /*
      * ECMA-357 10.6.1 step 1 seems to be incorrect.  The spec says:
      *
      * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception
      *
@@ -5543,17 +5543,17 @@ ValueToIdForXML(JSContext *cx, jsval v, 
 {
     if (JSVAL_IS_INT(v)) {
         int32_t i = JSVAL_TO_INT(v);
         if (INT_FITS_IN_JSID(i))
             *idp = INT_TO_JSID(i);
         else if (!ValueToId(cx, v, idp))
             return JS_FALSE;
     } else if (JSVAL_IS_STRING(v)) {
-        JSAtom *atom = js_AtomizeString(cx, JSVAL_TO_STRING(v));
+        JSAtom *atom = AtomizeString(cx, JSVAL_TO_STRING(v));
         if (!atom)
             return JS_FALSE;
         *idp = AtomToId(atom);
     } else if (!JSVAL_IS_PRIMITIVE(v)) {
         *idp = OBJECT_TO_JSID(JSVAL_TO_OBJECT(v));
     } else {
         ReportBadXMLName(cx, v);
         return JS_FALSE;
@@ -7995,17 +7995,17 @@ js_NewXMLSpecialObject(JSContext *cx, JS
         return js_NewXMLObject(cx, JSXML_CLASS_TEXT);
     }
 
     obj = js_NewXMLObject(cx, xml_class);
     if (!obj)
         return NULL;
     xml = (JSXML *) obj->getPrivate();
     if (name) {
-        JSAtom *atomName = js_AtomizeString(cx, name);
+        JSAtom *atomName = AtomizeString(cx, name);
         if (!atomName)
             return NULL;
         qn = NewXMLQName(cx, cx->runtime->emptyString, NULL, atomName);
         if (!qn)
             return NULL;
         xml->name = qn;
     }
     xml->xml_value = value;
--- a/js/src/tests/jstests.py
+++ b/js/src/tests/jstests.py
@@ -1,16 +1,17 @@
 #!/usr/bin/env python
 """
 The JS Shell Test Harness.
 
 See the adjacent README.txt for more details.
 """
 
 import os, sys, textwrap
+from copy import copy
 from subprocess import list2cmdline, call
 
 from lib.results import NullTestOutput
 from lib.tests import TestCase
 from lib.results import ResultsSink
 from lib.progressbar import ProgressBar
 
 if (sys.platform.startswith('linux') or
@@ -79,16 +80,18 @@ def parse_args():
 
     harness_og = OptionGroup(op, "Harness Controls", "Control how tests are run.")
     harness_og.add_option('-j', '--worker-count', type=int, default=max(1, get_cpu_count()),
                           help='Number of tests to run in parallel (default %default)')
     harness_og.add_option('-t', '--timeout', type=float, default=150.0,
                           help='Set maximum time a test is allows to run (in seconds).')
     harness_og.add_option('-a', '--args', dest='shell_args', default='',
                           help='Extra args to pass to the JS shell.')
+    harness_og.add_option('--jitflags', default='',
+                          help='Example: --jitflags=m,amd to run each test with -m, -a -m -d [default=%default]')
     harness_og.add_option('-g', '--debug', action='store_true', help='Run a test in debugger.')
     harness_og.add_option('--debugger', default='gdb -q --args', help='Debugger command.')
     harness_og.add_option('--valgrind', action='store_true', help='Run tests in valgrind.')
     harness_og.add_option('--valgrind-args', default='', help='Extra args to pass to valgrind.')
     op.add_option_group(harness_og)
 
     input_og = OptionGroup(op, "Inputs", "Change what tests are run.")
     input_og.add_option('-f', '--file', dest='test_file', action='append',
@@ -183,16 +186,26 @@ def parse_args():
     options.hide_progress = (((options.show_cmd or options.show_output) and
                               options.output_fp == sys.stdout) or
                              options.tinderbox or
                              ProgressBar.conservative_isatty() or
                              options.hide_progress)
 
     return (options, requested_paths, excluded_paths)
 
+def parse_jitflags(op_jitflags):
+    jitflags = [ [ '-' + flag for flag in flags ]
+                 for flags in op_jitflags.split(',') ]
+    for flags in jitflags:
+        for flag in flags:
+            if flag not in ('-m', '-a', '-p', '-d', '-n'):
+                print('Invalid jit flag: "%s"'%flag)
+                sys.exit(1)
+    return jitflags
+
 def load_tests(options, requested_paths, excluded_paths):
     """
     Returns a tuple: (skipped_tests, test_list)
         skip_list: [iterable<Test>] Tests found but skipped.
         test_list: [iterable<Test>] Tests found that should be run.
     """
     import lib.manifest as manifest
 
@@ -210,16 +223,28 @@ def load_tests(options, requested_paths,
     test_dir = os.path.dirname(os.path.abspath(__file__))
     test_list = manifest.load(test_dir, xul_tester)
     skip_list = []
 
     if options.make_manifests:
         manifest.make_manifests(options.make_manifests, test_list)
         sys.exit()
 
+    # Create a new test list. Apply each JIT configuration to every test.
+    if options.jitflags:
+        new_test_list = []
+        jitflags_list = parse_jitflags(options.jitflags)
+        for test in test_list:
+            for jitflags in jitflags_list:
+                tmp_test = copy(test)
+                tmp_test.options = copy(test.options)
+                tmp_test.options.extend(jitflags)
+                new_test_list.append(tmp_test)
+        test_list = new_test_list
+
     if options.test_file:
         paths = set()
         for test_file in options.test_file:
             paths |= set([ line.strip() for line in open(test_file).readlines()])
         test_list = [ _ for _ in test_list if _.path in paths ]
 
     if requested_paths:
         def p(path):
--- a/js/src/tests/lib/tasks_unix.py
+++ b/js/src/tests/lib/tasks_unix.py
@@ -37,27 +37,30 @@ def spawn_test(test):
     os.close(rerr)
 
     os.dup2(wout, 1)
     os.dup2(werr, 2)
 
     cmd = test.get_command(test.js_cmd_prefix)
     os.execvp(cmd[0], cmd)
 
+def total_seconds(td):
+    return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6
+
 def get_max_wait(tasks, results, timeout):
     """
     Return the maximum time we can wait before any task should time out.
     """
     now = datetime.now()
     wait = timedelta(0)
     for task in tasks:
         remaining = timedelta(seconds=timeout) - (now - task.start)
         if remaining > wait:
             wait = remaining
-    wait = wait.total_seconds()
+    wait = total_seconds(wait)
 
     # The test harness uses a timeout of 0 to indicate we should wait forever,
     # but for select(), a timeout of 0 indicates a zero-length wait.  Instead,
     # translate the timeout into None to tell select to wait forever.
     if wait == 0:
         return None
 
     # If we have a progress-meter, we need to wake up to update it frequently.
@@ -158,17 +161,17 @@ def reap_zombies(tasks, results, timeout
             returncode = -os.WTERMSIG(status)
 
         out = TestOutput(
                    ended.test,
                    ended.cmd,
                    ''.join(ended.out),
                    ''.join(ended.err),
                    returncode,
-                   (datetime.now() - ended.start).total_seconds(),
+                   total_seconds(datetime.now() - ended.start),
                    timed_out(ended, timeout))
         results.push(out)
     return tasks
 
 def kill_undead(tasks, results, timeout):
     """
     Signal all children that are over the given timeout.
     """
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -873,17 +873,17 @@ Debugger::parseResumptionValue(AutoCompa
     return shape->propid() == returnId ? JSTRAP_RETURN : JSTRAP_THROW;
 }
 
 bool
 CallMethodIfPresent(JSContext *cx, HandleObject obj, const char *name, int argc, Value *argv,
                     Value *rval)
 {
     rval->setUndefined();
-    JSAtom *atom = js_Atomize(cx, name, strlen(name));
+    JSAtom *atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return false;
 
     Rooted<jsid> id(cx, AtomToId(atom));
     RootedValue fval(cx);
     return GetMethod(cx, obj, id, 0, &fval) &&
            (!js_IsCallable(fval) || Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval));
 }
@@ -3643,17 +3643,17 @@ DebuggerObject_getProto(JSContext *cx, u
     return true;
 }
 
 static JSBool
 DebuggerObject_getClass(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get class", args, refobj);
     const char *s = refobj->getClass()->name;
-    JSAtom *str = js_Atomize(cx, s, strlen(s));
+    JSAtom *str = Atomize(cx, s, strlen(s));
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
 static JSBool
 DebuggerObject_getCallable(JSContext *cx, unsigned argc, Value *vp)
@@ -4302,17 +4302,17 @@ DebuggerEnv_getType(JSContext *cx, unsig
     const char *s;
     if (IsDeclarative(env))
         s = "declarative";
     else if (IsWith(env))
         s = "with";
     else
         s = "object";
 
-    JSAtom *str = js_Atomize(cx, s, strlen(s), InternAtom, NormalEncoding);
+    JSAtom *str = Atomize(cx, s, strlen(s), InternAtom, NormalEncoding);
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
 static JSBool
 DebuggerEnv_getParent(JSContext *cx, unsigned argc, Value *vp)
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -281,17 +281,17 @@ RegExpObject::create(JSContext *cx, RegE
     RegExpFlag staticsFlags = res->getFlags();
     return createNoStatics(cx, chars, length, RegExpFlag(flags | staticsFlags), tokenStream);
 }
 
 RegExpObject *
 RegExpObject::createNoStatics(JSContext *cx, const jschar *chars, size_t length, RegExpFlag flags,
                               TokenStream *tokenStream)
 {
-    RootedAtom source(cx, js_AtomizeChars(cx, chars, length));
+    RootedAtom source(cx, AtomizeChars(cx, chars, length));
     if (!source)
         return NULL;
 
     return createNoStatics(cx, source, flags, tokenStream);
 }
 
 RegExpObject *
 RegExpObject::createNoStatics(JSContext *cx, HandleAtom source, RegExpFlag flags,
--- a/js/src/vm/String-inl.h
+++ b/js/src/vm/String-inl.h
@@ -178,17 +178,17 @@ inline js::PropertyName *
 JSFlatString::toPropertyName(JSContext *cx)
 {
 #ifdef DEBUG
     uint32_t dummy;
     JS_ASSERT(!isIndex(&dummy));
 #endif
     if (isAtom())
         return asAtom().asPropertyName();
-    JSAtom *atom = js_AtomizeString(cx, this);
+    JSAtom *atom = js::AtomizeString(cx, this);
     if (!atom)
         return NULL;
     return atom->asPropertyName();
 }
 
 JS_ALWAYS_INLINE void
 JSFixedString::init(const jschar *chars, size_t length)
 {
--- a/js/src/vm/StringBuffer.cpp
+++ b/js/src/vm/StringBuffer.cpp
@@ -70,17 +70,17 @@ JSAtom *
 StringBuffer::finishAtom()
 {
     JSContext *cx = context();
 
     size_t length = cb.length();
     if (length == 0)
         return cx->runtime->atomState.emptyAtom;
 
-    JSAtom *atom = js_AtomizeChars(cx, cb.begin(), length);
+    JSAtom *atom = AtomizeChars(cx, cb.begin(), length);
     cb.clear();
     return atom;
 }
 
 bool
 js::ValueToStringBufferSlow(JSContext *cx, const Value &arg, StringBuffer &sb)
 {
     Value v = arg;
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -683,8 +683,11 @@ pref("reader.font_size", 4);
 // The default of margin size in reader (5%-25%)
 pref("reader.margin_size", 5);
 
 // The default color scheme in reader (light, dark, sepia)
 pref("reader.color_scheme", "light");
 
 // Used to show a first-launch tip in reader
 pref("reader.has_used_toolbar", false);
+
+// Coalesce touch events to prevent them from flooding the event queue
+pref("dom.event.touch.coalescing.enabled", true);
--- a/mobile/android/base/AwesomeBar.java
+++ b/mobile/android/base/AwesomeBar.java
@@ -314,22 +314,25 @@ public class AwesomeBar extends GeckoAct
         if (text.length() == 0) {
             mGoButton.setVisibility(View.GONE);
             return;
         }
 
         mGoButton.setVisibility(View.VISIBLE);
 
         int imageResource = R.drawable.ic_awesomebar_go;
+        String contentDescription = getString(R.string.go);
         int imeAction = EditorInfo.IME_ACTION_GO;
         if (isSearchUrl(text)) {
             imageResource = R.drawable.ic_awesomebar_search;
+            contentDescription = getString(R.string.search);
             imeAction = EditorInfo.IME_ACTION_SEARCH;
         }
         mGoButton.setImageResource(imageResource);
+        mGoButton.setContentDescription(contentDescription);
 
         int actionBits = mText.getImeOptions() & EditorInfo.IME_MASK_ACTION;
         if (actionBits != imeAction) {
             InputMethodManager imm = (InputMethodManager) mText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
             int optionBits = mText.getImeOptions() & ~EditorInfo.IME_MASK_ACTION;
             mText.setImeOptions(optionBits | imeAction);
             imm.restartInput(mText);
         }
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -1641,17 +1641,16 @@ abstract public class GeckoApp
                     Log.i(LOGTAG, "Launching from debug intent after 5s wait");
                     setLaunchState(LaunchState.Launching);
                     sGeckoThread.reallyStart();
                 }
             }, 1000 * 5 /* 5 seconds */);
             Log.i(LOGTAG, "Intent : ACTION_DEBUG - waiting 5s before launching");
         }
 
-        Tabs.getInstance().setContentResolver(getContentResolver());
         Tabs.registerOnTabsChangedListener(this);
 
         if (cameraView == null) {
             cameraView = new SurfaceView(this);
             cameraView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
         }
 
         if (mLayerClient == null) {
@@ -2418,17 +2417,17 @@ abstract public class GeckoApp
     }
 
     @Override
     public void onBackPressed() {
         if (autoHideTabs()) {
             return;
         }
 
-        if (mDoorHangerPopup.isShowing()) {
+        if (mDoorHangerPopup != null && mDoorHangerPopup.isShowing()) {
             mDoorHangerPopup.dismiss();
             return;
         }
 
         if (mFullScreenPluginView != null) {
             GeckoAppShell.onFullScreenPluginHidden(mFullScreenPluginView);
             removeFullScreenPluginView(mFullScreenPluginView);
             return;
@@ -2510,32 +2509,29 @@ abstract public class GeckoApp
 
     /**
      * Open the url as a new tab, and mark the selected tab as its "parent".
      * If the url is already open in a tab, the existing tab is selected.
      * Use this for tabs opened by the browser chrome, so users can press the
      * "Back" button to return to the previous tab.
      */
     public void loadUrlInTab(String url) {
-        ArrayList<Tab> tabs = Tabs.getInstance().getTabsInOrder();
-        if (tabs != null) {
-            Iterator<Tab> tabsIter = tabs.iterator();
-            while (tabsIter.hasNext()) {
-                Tab tab = tabsIter.next();
-                if (url.equals(tab.getURL())) {
-                    Tabs.getInstance().selectTab(tab.getId());
-                    return;
-                }
+        Tabs tabsInstance = Tabs.getInstance();
+        Iterable<Tab> tabs = tabsInstance.getTabsInOrder();
+        for (Tab tab : tabs) {
+            if (url.equals(tab.getURL())) {
+                tabsInstance.selectTab(tab.getId());
+                return;
             }
         }
 
         JSONObject args = new JSONObject();
         try {
             args.put("url", url);
-            args.put("parentId", Tabs.getInstance().getSelectedTab().getId());
+            args.put("parentId", tabsInstance.getSelectedTab().getId());
         } catch (Exception e) {
             Log.e(LOGTAG, "error building JSON arguments");
         }
         Log.i(LOGTAG, "Sending message to Gecko: " + SystemClock.uptimeMillis() + " - Tab:Add");
         GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tab:Add", args.toString()));
     }
 
     /* This method is referenced by Robocop via reflection. */
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -47,17 +47,16 @@ FENNEC_JAVA_FILES = \
   awesomebar/AllPagesTab.java \
   awesomebar/BookmarksTab.java \
   awesomebar/HistoryTab.java \
   BrowserApp.java \
   BrowserToolbar.java \
   CameraImageResultHandler.java \
   CameraVideoResultHandler.java \
   SyncPreference.java \
-  db/AndroidBrowserDB.java \
   db/BrowserDB.java \
   db/LocalBrowserDB.java \
   db/DBUtils.java \
   DoorHanger.java \
   DoorHangerPopup.java \
   Favicons.java \
   FilePickerResultHandler.java \
   FilePickerResultHandlerSync.java \
@@ -200,24 +199,24 @@ FENNEC_PP_XML_FILES = \
 ifneq (,$(findstring -march=armv7,$(OS_CFLAGS)))
 MIN_CPU_VERSION=7
 else
 MIN_CPU_VERSION=5
 endif
 
 ifeq (,$(ANDROID_VERSION_CODE))
 ifeq ($(MIN_CPU_VERSION),7)
-ANDROID_VERSION_CODE=$(shell $(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid | cut -c1-10)
+ANDROID_VERSION_CODE=$(shell cat $(DEPTH)/config/buildid | cut -c1-10)
 else
 # decrement the version code by 1 for armv6 builds so armv7 builds will win any compatability ties
-ANDROID_VERSION_CODE=$(shell echo `$(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid | cut -c1-10` - 1 | bc)
+ANDROID_VERSION_CODE=$(shell echo $$((`cat $(DEPTH)/config/buildid | cut -c1-10` - 1)))
 endif
 endif
 
-UA_BUILDID=$(shell $(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid | cut -c1-8)
+UA_BUILDID=$(shell echo $(ANDROID_VERSION_CODE) | cut -c1-8)
 
 # Mangle our package name to avoid Bug 750548.
 DEFINES += \
   -DMANGLED_ANDROID_PACKAGE_NAME=$(subst fennec,f3nn3c,$(ANDROID_PACKAGE_NAME)) \
   -DANDROID_PACKAGE_NAME=$(ANDROID_PACKAGE_NAME) \
   -DMOZ_APP_DISPLAYNAME="$(MOZ_APP_DISPLAYNAME)" \
   -DMOZ_APP_NAME=$(MOZ_APP_NAME) \
   -DMOZ_APP_VERSION=$(MOZ_APP_VERSION) \
--- a/mobile/android/base/PropertyAnimator.java
+++ b/mobile/android/base/PropertyAnimator.java
@@ -136,35 +136,41 @@ public class PropertyAnimator extends Ti
 
         if (mListener != null) {
             mListener.onPropertyAnimationEnd();
             mListener = null;
         }
     }
 
     private void invalidate(final ElementHolder element, final int delta) {
-        if (element == null || element.view == null)
+        View v = (element == null ? null : element.view);
+        if (v == null)
             return;
 
         // Post the layout changes on the view's UI thread.
-        element.view.getHandler().post(new Runnable() {
+        v.getHandler().post(new Runnable() {
             @Override
             public void run() {
+                // Check if the element and view still exist
+                View view = (element == null ? null : element.view);
+                if (view == null)
+                     return;
+            
                 if (element.property == Property.SLIDE_TOP) {
-                    element.view.scrollTo(element.view.getScrollX(), delta);
+                    view.scrollTo(view.getScrollX(), delta);
                     return;
                 } else if (element.property == Property.SLIDE_LEFT) {
-                    element.view.scrollTo(delta, element.view.getScrollY());
+                    view.scrollTo(delta, view.getScrollY());
                     return;
                 }
 
-                ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) element.view.getLayoutParams();
+                ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
 
                 if (element.property == Property.SHRINK_TOP)
                     params.setMargins(params.leftMargin, delta, params.rightMargin, params.bottomMargin);
                 else if (element.property == Property.SHRINK_LEFT)
                     params.setMargins(delta, params.topMargin, params.rightMargin, params.bottomMargin);
 
-                element.view.requestLayout();
+                view.requestLayout();
             }
         });
     }
 }
--- a/mobile/android/base/Tab.java
+++ b/mobile/android/base/Tab.java
@@ -30,17 +30,18 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 public final class Tab {
     private static final String LOGTAG = "GeckoTab";
 
     private static Pattern sColorPattern;
-    private int mId;
+    private final int mId;
+    private long mLastUsed;
     private String mUrl;
     private String mTitle;
     private Drawable mFavicon;
     private String mFaviconUrl;
     private int mFaviconSize;
     private JSONObject mIdentityData;
     private boolean mReaderEnabled;
     private BitmapDrawable mThumbnail;
@@ -67,20 +68,21 @@ public final class Tab {
 
     public static final int STATE_DELAYED = 0;
     public static final int STATE_LOADING = 1;
     public static final int STATE_SUCCESS = 2;
     public static final int STATE_ERROR = 3;
 
     public Tab(int id, String url, boolean external, int parentId, String title) {
         mId = id;
+        mLastUsed = 0;
         mUrl = url;
         mExternal = external;
         mParentId = parentId;
-        mTitle = title;
+        mTitle = title == null ? "" : title;
         mFavicon = null;
         mFaviconUrl = null;
         mFaviconSize = 0;
         mIdentityData = null;
         mReaderEnabled = false;
         mThumbnail = null;
         mHistoryIndex = -1;
         mHistorySize = 0;
@@ -105,25 +107,38 @@ public final class Tab {
     public void onDestroy() {
         BrowserDB.unregisterContentObserver(mContentResolver, mContentObserver);
     }
 
     public int getId() {
         return mId;
     }
 
+    public synchronized void onChange() {
+        mLastUsed = System.currentTimeMillis();
+    }
+
+    public synchronized long getLastUsed() {
+        return mLastUsed;
+    }
+
     public int getParentId() {
         return mParentId;
     }
 
     // may be null if user-entered query hasn't yet been resolved to a URI
-    public String getURL() {
+    public synchronized String getURL() {
         return mUrl;
     }
 
+    // mTitle should never be null, but it may be an empty string
+    public synchronized String getTitle() {
+        return mTitle;
+    }
+
     public String getDisplayTitle() {
         if (mTitle != null && mTitle.length() > 0) {
             return mTitle;
         }
 
         return mUrl;
     }
 
@@ -187,17 +202,17 @@ public final class Tab {
                     public void run() {
                         Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.THUMBNAIL);
                     }
                 });
             }
         });
     }
 
-    public String getFaviconURL() {
+    public synchronized String getFaviconURL() {
         return mFaviconUrl;
     }
 
     public String getSecurityMode() {
         try {
             return mIdentityData.getString("mode");
         } catch (Exception e) {
             // If mIdentityData is null, or we get a JSONException
@@ -220,20 +235,20 @@ public final class Tab {
     public boolean isReadingListItem() {
         return mReadingListItem;
     }
 
     public boolean isExternal() {
         return mExternal;
     }
 
-    public void updateURL(String url) {
+    public synchronized void updateURL(String url) {
         if (url != null && url.length() > 0) {
             mUrl = url;
-            Log.i(LOGTAG, "Updated url: " + url + " for tab with id: " + mId);
+            Log.d(LOGTAG, "Updated URL for tab with id: " + mId);
             updateBookmark();
             updateHistory(mUrl, mTitle);
         }
     }
 
     public void setDocumentURI(String documentURI) {
         mDocumentURI = documentURI;
     }
@@ -245,20 +260,20 @@ public final class Tab {
     public void setContentType(String contentType) {
         mContentType = contentType;
     }
 
     public String getContentType() {
         return mContentType;
     }
 
-    public void updateTitle(String title) {
+    public synchronized void updateTitle(String title) {
         mTitle = (title == null ? "" : title);
 
-        Log.i(LOGTAG, "Updated title: " + mTitle + " for tab with id: " + mId);
+        Log.d(LOGTAG, "Updated title for tab with id: " + mId);
         updateHistory(mUrl, mTitle);
         final Tab tab = this;
 
         GeckoAppShell.getMainHandler().post(new Runnable() {
             public void run() {
                 Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.TITLE);
             }
         });
@@ -301,40 +316,39 @@ public final class Tab {
     }
 
     public long getFaviconLoadId() {
         return mFaviconLoadId;
     }
 
     public void updateFavicon(Drawable favicon) {
         mFavicon = favicon;
-        Log.i(LOGTAG, "Updated favicon for tab with id: " + mId);
+        Log.d(LOGTAG, "Updated favicon for tab with id: " + mId);
     }
 
-    public void updateFaviconURL(String faviconUrl, int size) {
+    public synchronized void updateFaviconURL(String faviconUrl, int size) {
         // If we already have an "any" sized icon, don't update the icon.
         if (mFaviconSize == -1)
             return;
 
         // Only update the favicon if it's bigger than the current favicon.
         // We use -1 to represent icons with sizes="any".
         if (size == -1 || size >= mFaviconSize) {
             mFaviconUrl = faviconUrl;
             mFaviconSize = size;
-            Log.i(LOGTAG, "Updated favicon URL for tab with id: " + mId);
+            Log.d(LOGTAG, "Updated favicon URL for tab with id: " + mId);
         }
     }
 
-    public void clearFavicon() {
+    public synchronized void clearFavicon() {
         mFavicon = null;
         mFaviconUrl = null;
         mFaviconSize = 0;
     }
 
-
     public void updateIdentityData(JSONObject identityData) {
         mIdentityData = identityData;
     }
 
     public void setReaderEnabled(boolean readerEnabled) {
         mReaderEnabled = readerEnabled;
         GeckoAppShell.getMainHandler().post(new Runnable() {
             public void run() {
--- a/mobile/android/base/Tabs.java
+++ b/mobile/android/base/Tabs.java
@@ -15,31 +15,36 @@ import android.content.ContentResolver;
 import android.content.Intent;
 import android.os.SystemClock;
 import android.util.Log;
 import android.widget.Toast;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 public class Tabs implements GeckoEventListener {
     private static final String LOGTAG = "GeckoTabs";
 
     private Tab mSelectedTab;
-    private HashMap<Integer, Tab> mTabs;
-    private ArrayList<Tab> mOrder;
-    private ContentResolver mResolver;
+    private final HashMap<Integer, Tab> mTabs = new HashMap<Integer, Tab>();
+    private final CopyOnWriteArrayList<Tab> mOrder = new CopyOnWriteArrayList<Tab>();
     private boolean mRestoringSession;
 
+    // Keeps track of how much has happened since we last updated our persistent tab store.
+    private volatile int mScore = 0;
+
+    private static final int SCORE_INCREMENT_TAB_LOCATION_CHANGE = 5;
+    private static final int SCORE_INCREMENT_TAB_SELECTED = 10;
+    private static final int SCORE_THRESHOLD = 30;
+
     private GeckoApp mActivity;
 
     private Tabs() {
-        mTabs = new HashMap<Integer, Tab>();
-        mOrder = new ArrayList<Tab>();
         GeckoAppShell.registerGeckoEventListener("SessionHistory:New", this);
         GeckoAppShell.registerGeckoEventListener("SessionHistory:Back", this);
         GeckoAppShell.registerGeckoEventListener("SessionHistory:Forward", this);
         GeckoAppShell.registerGeckoEventListener("SessionHistory:Goto", this);
         GeckoAppShell.registerGeckoEventListener("SessionHistory:Purge", this);
         GeckoAppShell.registerGeckoEventListener("Tab:Added", this);
         GeckoAppShell.registerGeckoEventListener("Tab:Close", this);
         GeckoAppShell.registerGeckoEventListener("Tab:Select", this);
@@ -200,36 +205,22 @@ public class Tabs implements GeckoEventL
             if (nextTab != null && nextTab.getParentId() == tab.getParentId())
                 return nextTab;
             else
                 return parent;
         }
         return nextTab;
     }
 
-    public HashMap<Integer, Tab> getTabs() {
-        if (getCount() == 0)
-            return null;
-
-        return mTabs;
-    }
-    
-    public ArrayList<Tab> getTabsInOrder() {
-        if (getCount() == 0)
-            return null;
-
+    public Iterable<Tab> getTabsInOrder() {
         return mOrder;
     }
 
-    public void setContentResolver(ContentResolver resolver) {
-        mResolver = resolver;
-    }
-
     public ContentResolver getContentResolver() {
-        return mResolver;
+        return mActivity.getContentResolver();
     }
 
     //Making Tabs a singleton class
     private static class TabsInstanceHolder {
         private static final Tabs INSTANCE = new Tabs();
     }
 
     public static Tabs getInstance() {
@@ -293,26 +284,26 @@ public class Tabs implements GeckoEventL
     void handleReaderAdded(boolean success, final String title, final String url) {
         if (!success) {
             mActivity.showToast(R.string.reading_list_failed, Toast.LENGTH_SHORT);
             return;
         }
 
         GeckoAppShell.getHandler().post(new Runnable() {
             public void run() {
-                BrowserDB.addReadingListItem(mActivity.getContentResolver(), title, url);
+                BrowserDB.addReadingListItem(getContentResolver(), title, url);
                 mActivity.showToast(R.string.reading_list_added, Toast.LENGTH_SHORT);
             }
         });
     }
 
     void handleReaderRemoved(final String url) {
         GeckoAppShell.getHandler().post(new Runnable() {
             public void run() {
-                BrowserDB.removeReadingListItemWithURL(mResolver, url);
+                BrowserDB.removeReadingListItemWithURL(getContentResolver(), url);
                 mActivity.showToast(R.string.reading_list_removed, Toast.LENGTH_SHORT);
             }
         });
     }
 
     public void refreshThumbnails() {
         Iterator<Tab> iterator = mTabs.values().iterator();
         while (iterator.hasNext()) {
@@ -362,18 +353,52 @@ public class Tabs implements GeckoEventL
         MENU_UPDATED
     }
 
     public void notifyListeners(Tab tab, TabEvents msg) {
         notifyListeners(tab, msg, "");
     }
 
     public void notifyListeners(Tab tab, TabEvents msg, Object data) {
+        onTabChanged(tab, msg, data);
+
         if (mTabsChangedListeners == null)
             return;
 
         Iterator<OnTabsChangedListener> items = mTabsChangedListeners.iterator();
         while (items.hasNext()) {
             items.next().onTabChanged(tab, msg, data);
         }
     }
 
+    private void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
+        switch(msg) {
+            case LOCATION_CHANGE:
+                mScore += SCORE_INCREMENT_TAB_LOCATION_CHANGE;
+                break;
+
+            // When one tab is deselected, another one is always selected, so only
+            // increment the score once. When tabs are added/closed, they are also
+            // selected/unselected, so it would be redundant to also listen
+            // for ADDED/CLOSED events.
+            case SELECTED:
+                mScore += SCORE_INCREMENT_TAB_SELECTED;
+            case UNSELECTED:
+                tab.onChange();
+                break;
+        }
+
+        if (mScore > SCORE_THRESHOLD) {
+            persistAllTabs();
+            mScore = 0;
+        }
+    }
+
+    // This method persists the current ordered list of tabs in our tabs content provider.
+    public void persistAllTabs() {
+        final Iterable<Tab> tabs = getTabsInOrder();
+        GeckoAppShell.getHandler().post(new Runnable() {
+            public void run() {
+                TabsAccessor.persistLocalTabs(getContentResolver(), tabs);
+            }
+        });
+    }
 }
--- a/mobile/android/base/TabsAccessor.java
+++ b/mobile/android/base/TabsAccessor.java
@@ -2,19 +2,27 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.util.GeckoAsyncTask;
 
+import org.json.JSONArray;
+import org.json.JSONException;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.SystemClock;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
 public final class TabsAccessor {
     private static final String LOGTAG = "GeckoTabsAccessor";
 
@@ -35,16 +43,19 @@ public final class TabsAccessor {
         URL,
         GUID,
         NAME
     };
 
     private static final String CLIENTS_SELECTION = BrowserContract.Clients.GUID + " IS NOT NULL";
     private static final String TABS_SELECTION = BrowserContract.Tabs.CLIENT_GUID + " IS NOT NULL";
 
+    private static final String LOCAL_CLIENT_SELECTION = BrowserContract.Clients.GUID + " IS NULL";
+    private static final String LOCAL_TABS_SELECTION = BrowserContract.Tabs.CLIENT_GUID + " IS NULL";
+
     public static class RemoteTab {
         public String title;
         public String url;
         public String guid;
         public String name;
     }
 
     public interface OnQueryTabsCompleteListener {
@@ -144,9 +155,84 @@ public final class TabsAccessor {
            }
 
             @Override
             protected void onPostExecute(List<RemoteTab> tabs) {
                 listener.onQueryTabsComplete(tabs);
             }
         }).execute();
     }
+
+    // Updates the modified time of the local client with the current time.
+    private static void updateLocalClient(final ContentResolver cr) {
+        ContentValues values = new ContentValues();
+        values.put(BrowserContract.Clients.LAST_MODIFIED, System.currentTimeMillis());
+        cr.update(BrowserContract.Clients.CONTENT_URI, values, LOCAL_CLIENT_SELECTION, null);
+    }
+
+    // Deletes all local tabs.
+    private static void deleteLocalTabs(final ContentResolver cr) {
+        cr.delete(BrowserContract.Tabs.CONTENT_URI, LOCAL_TABS_SELECTION, null);
+    }
+
+    /**
+     * Tabs are positioned in the DB in the same order that they appear in the tabs param.
+     *   - URL should never empty or null. Skip this tab if there's no URL.
+     *   - TITLE should always a string, either a page title or empty.
+     *   - LAST_USED should always be numeric.
+     *   - FAVICON should be a URL or null.
+     *   - HISTORY should be serialized JSON array of URLs.
+     *   - POSITION should always be numeric.
+     *   - CLIENT_GUID should always be null to represent the local client.
+     */
+    private static void insertLocalTabs(final ContentResolver cr, final Iterable<Tab> tabs) {
+        // Reuse this for serializing individual history URLs as JSON.
+        JSONArray history = new JSONArray();
+        ArrayList<ContentValues> valuesToInsert = new ArrayList<ContentValues>();
+
+        int position = 0;
+        for (Tab tab : tabs) {
+            // Skip this tab if it has a null URL.
+            String url = tab.getURL();
+            if (url == null)
+                continue;
+
+            ContentValues values = new ContentValues();
+            values.put(BrowserContract.Tabs.URL, url);
+            values.put(BrowserContract.Tabs.TITLE, tab.getTitle());
+            values.put(BrowserContract.Tabs.LAST_USED, tab.getLastUsed());
+
+            String favicon = tab.getFaviconURL();
+            if (favicon != null)
+                values.put(BrowserContract.Tabs.FAVICON, favicon);
+            else
+                values.putNull(BrowserContract.Tabs.FAVICON);
+
+            // We don't have access to session history in Java, so for now, we'll
+            // just use a JSONArray that holds most recent history item.
+            try {
+                history.put(0, tab.getURL());
+                values.put(BrowserContract.Tabs.HISTORY, history.toString());
+            } catch (JSONException e) {
+                Log.e(LOGTAG, "JSONException adding URL to tab history array", e);
+            }
+
+            values.put(BrowserContract.Tabs.POSITION, position++);
+
+            // A null client guid corresponds to the local client.
+            values.putNull(BrowserContract.Tabs.CLIENT_GUID);
+
+            valuesToInsert.add(values);
+        }
+
+        ContentValues[] valuesToInsertArray = valuesToInsert.toArray(new ContentValues[valuesToInsert.size()]);
+        cr.bulkInsert(BrowserContract.Tabs.CONTENT_URI, valuesToInsertArray);
+    }
+
+    // Deletes all local tabs and replaces them with a new list of tabs.
+    public static synchronized void persistLocalTabs(final ContentResolver cr, final Iterable<Tab> tabs) {
+        Log.v(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - start of persistLocalTabs");
+        deleteLocalTabs(cr);
+        insertLocalTabs(cr, tabs);
+        updateLocalClient(cr);
+        Log.v(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - end of persistLocalTabs");
+    }
 }
--- a/mobile/android/base/TabsTray.java
+++ b/mobile/android/base/TabsTray.java
@@ -192,22 +192,20 @@ public class TabsTray extends LinearLayo
                     break;
             }
         }
 
         private void refreshTabsData() {
             // Store a different copy of the tabs, so that we don't have to worry about
             // accidentally updating it on the wrong thread.
             mTabs = new ArrayList<Tab>();
-            ArrayList<Tab> tabs = Tabs.getInstance().getTabsInOrder();
 
-            if (tabs != null) {
-                for (Tab tab : tabs) {
-                    mTabs.add(tab);
-                }
+            Iterable<Tab> tabs = Tabs.getInstance().getTabsInOrder();
+            for (Tab tab : tabs) {
+                mTabs.add(tab);
             }
 
             notifyDataSetChanged(); // Be sure to call this whenever mTabs changes.
             updateSelectedPosition();
         }
 
         // Updates the selected position in the list so that it will be scrolled to the right place.
         private void updateSelectedPosition() {
deleted file mode 100644
--- a/mobile/android/base/db/AndroidBrowserDB.java
+++ /dev/null
@@ -1,428 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko.db;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.CursorWrapper;
-import android.database.sqlite.SQLiteConstraintException;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.BitmapDrawable;
-import android.net.Uri;
-import android.os.Build;
-import android.provider.Browser;
-import android.provider.Browser.BookmarkColumns;
-import android.util.Log;
-
-import java.io.ByteArrayOutputStream;
-
-public class AndroidBrowserDB implements BrowserDB.BrowserDBIface {
-    private static final String LOGTAG = "AndroidBrowserDB";
-    private static final String URL_COLUMN_ID = "_id";
-    private static final String URL_COLUMN_THUMBNAIL = "thumbnail";
-
-    // Only available on Android >= 11
-    private static final String URL_COLUMN_DELETED = "deleted";
-
-    private static final Uri BOOKMARKS_CONTENT_URI_POST_11 = Uri.parse("content://com.android.browser/bookmarks");
-
-    public void invalidateCachedState() {
-        // Do nothing
-    }
-
-    private Cursor filterAllSites(ContentResolver cr, String[] projection, CharSequence constraint, int limit, CharSequence urlFilter) {
-        Cursor c = cr.query(Browser.BOOKMARKS_URI,
-                            projection,
-                            // The length restriction on URL is for the same reason as in the general bookmark query
-                            // (see comment earlier in this file).
-                            (urlFilter != null ? "(" + Browser.BookmarkColumns.URL + " NOT LIKE ? ) AND " : "" ) + 
-                            "(" + Browser.BookmarkColumns.URL + " LIKE ? OR " + Browser.BookmarkColumns.TITLE + " LIKE ?)"
-                            + " AND LENGTH(" + Browser.BookmarkColumns.URL + ") > 0",
-                            urlFilter == null ? new String[] {"%" + constraint.toString() + "%", "%" + constraint.toString() + "%"} :
-                            new String[] {urlFilter.toString(), "%" + constraint.toString() + "%", "%" + constraint.toString() + "%"},
-                            // ORDER BY is number of visits times a multiplier from 1 - 120 of how recently the site
-                            // was accessed with a site accessed today getting 120 and a site accessed 119 or more
-                            // days ago getting 1
-                            Browser.BookmarkColumns.VISITS + " * MAX(1, (" +
-                            Browser.BookmarkColumns.DATE + " - " + System.currentTimeMillis() + ") / 86400000 + 120) DESC LIMIT " + limit);
-
-        return new AndroidDBCursor(c);
-    }
-
-    public Cursor filter(ContentResolver cr, CharSequence constraint, int limit) {
-        return filterAllSites(cr,
-                              new String[] { URL_COLUMN_ID,
-                                             BookmarkColumns.URL,
-                                             BookmarkColumns.TITLE,
-                                             BookmarkColumns.FAVICON },
-                              constraint,
-                              limit,
-                              null);
-    }
-
-    public Cursor getTopSites(ContentResolver cr, int limit) {
-        return filterAllSites(cr,
-                              new String[] { URL_COLUMN_ID,
-                                             BookmarkColumns.URL,
-                                             BookmarkColumns.TITLE,
-                                             URL_COLUMN_THUMBNAIL },
-                              "",
-                              limit,
-                              BrowserDB.ABOUT_PAGES_URL_FILTER);
-    }
-
-    public void updateVisitedHistory(ContentResolver cr, String uri) {
-        Browser.updateVisitedHistory(cr, uri, true);
-    }
-
-    public void updateHistoryTitle(ContentResolver cr, String uri, String title) {
-        ContentValues values = new ContentValues();
-        values.put(Browser.BookmarkColumns.TITLE, title);
-
-        cr.update(Browser.BOOKMARKS_URI,
-                  values,
-                  Browser.BookmarkColumns.URL + " = ?",
-                  new String[] { uri });
-    }
-
-    public void updateHistoryEntry(ContentResolver cr, String uri, String title,
-                                   long date, int visits) {
-        int oldVisits = 0;
-        Cursor cursor = null;
-        try {
-            cursor = cr.query(Browser.BOOKMARKS_URI,
-                              new String[] { Browser.BookmarkColumns.VISITS },
-                              Browser.BookmarkColumns.URL + " = ?",
-                              new String[] { uri },
-                              null);
-
-            if (cursor.moveToFirst()) {
-                oldVisits = cursor.getInt(0);
-            }
-        } finally {
-            if (cursor != null)
-                cursor.close();
-        }
-
-        ContentValues values = new ContentValues();
-        values.put(Browser.BookmarkColumns.DATE, date);
-        values.put(Browser.BookmarkColumns.VISITS, oldVisits + visits);
-        if (title != null) {
-            values.put(Browser.BookmarkColumns.TITLE, title);
-        }
-
-        cr.update(Browser.BOOKMARKS_URI,
-                  values,
-                  Browser.BookmarkColumns.URL + " = ?",
-                  new String[] { uri });
-    }
-
-    public Cursor getAllVisitedHistory(ContentResolver cr) {
-        Cursor c = cr.query(Browser.BOOKMARKS_URI,
-                            new String[] { Browser.BookmarkColumns.URL },
-                            Browser.BookmarkColumns.BOOKMARK + " = 0 AND " +
-                            Browser.BookmarkColumns.VISITS + " > 0",
-                            null,
-                            null);
-
-        return new AndroidDBCursor(c);
-    }
-
-    public Cursor getRecentHistory(ContentResolver cr, int limit) {
-        Cursor c = cr.query(Browser.BOOKMARKS_URI,
-                            new String[] { URL_COLUMN_ID,
-                                           BookmarkColumns.URL,
-                                           BookmarkColumns.TITLE,
-                                           BookmarkColumns.FAVICON,
-                                           BookmarkColumns.DATE,
-                                           BookmarkColumns.VISITS },
-                            // Bookmarks that have not been visited have a date value
-                            // of 0, so don't pick them up in the history view.
-                            Browser.BookmarkColumns.DATE + " > 0",
-                            null,
-                            Browser.BookmarkColumns.DATE + " DESC LIMIT " + limit);
-
-        return new AndroidDBCursor(c);
-    }
-
-    public void removeHistoryEntry(ContentResolver cr, int id) {
-        // Not implemented
-    }
-
-    public void clearHistory(ContentResolver cr) {
-        Browser.clearHistory(cr);
-    }
-
-    public Cursor getBookmarksInFolder(ContentResolver cr, long folderId) {
-        Cursor c = cr.query(null, null, null, null, null);
-        return new AndroidDBCursor(c);
-    }
-
-    public Cursor isBookmarkQueryPre11(ContentResolver cr, String uri) {
-        return cr.query(Browser.BOOKMARKS_URI,
-                        new String[] { BookmarkColumns.URL },
-                        Browser.BookmarkColumns.URL + " = ? and " + Browser.BookmarkColumns.BOOKMARK + " = ?",
-                        new String[] { uri, "1" },
-                        Browser.BookmarkColumns.URL);
-    }
-
-    public Cursor isBookmarkQueryPost11(ContentResolver cr, String uri) {
-        return cr.query(BOOKMARKS_CONTENT_URI_POST_11,
-                        new String[] { BookmarkColumns.URL },
-                        Browser.BookmarkColumns.URL + " = ?",
-                        new String[] { uri },
-                        Browser.BookmarkColumns.URL);
-    }
-
-    public boolean isBookmark(ContentResolver cr, String uri) {
-        Cursor cursor;
-
-        if (Build.VERSION.SDK_INT >= 11)
-            cursor = isBookmarkQueryPost11(cr, uri);
-        else
-            cursor = isBookmarkQueryPre11(cr, uri);
-
-        int count = cursor.getCount();
-        cursor.close();
-
-        return (count > 0);
-    }
-
-    public boolean isReadingListItem(ContentResolver cr, String uri) {
-        return false;
-    }
-
-    public String getUrlForKeyword(ContentResolver cr, String keyword) {
-        return null;
-    }
-
-    public void addBookmarkPre11(ContentResolver cr, String title, String uri) {
-        ContentValues values = new ContentValues();
-        values.put(Browser.BookmarkColumns.BOOKMARK, "1");
-        values.put(Browser.BookmarkColumns.TITLE, title);
-        values.put(Browser.BookmarkColumns.URL, uri);
-
-        int updated = cr.update(Browser.BOOKMARKS_URI,
-                                values,
-                                Browser.BookmarkColumns.URL + " = ?",
-                                new String[] { uri });
-
-        if (updated == 0)
-            cr.insert(Browser.BOOKMARKS_URI, values);
-    }
-
-    public void addBookmarkPost11(ContentResolver cr, String title, String uri) {
-        ContentValues values = new ContentValues();
-        values.put(Browser.BookmarkColumns.TITLE, title);
-        values.put(Browser.BookmarkColumns.URL, uri);
-        values.put(URL_COLUMN_DELETED, "0");
-
-        int updated = cr.update(BOOKMARKS_CONTENT_URI_POST_11,
-                                values,
-                                Browser.BookmarkColumns.URL + " = ?",
-                                new String[] { uri });
-
-        if (updated == 0)
-            cr.insert(BOOKMARKS_CONTENT_URI_POST_11, values);
-    }
-
-    public void addBookmark(ContentResolver cr, String title, String uri) {
-        if (Build.VERSION.SDK_INT >= 11)
-            addBookmarkPost11(cr, title, uri);
-        else
-            addBookmarkPre11(cr, title, uri);
-    }
-
-    public void updateBookmark(ContentResolver cr, int id, String uri, String title, String keyword) {
-        // Not implemented
-    }
-
-    public void removeBookmarkPre11(ContentResolver cr, String uri) {
-        ContentValues values = new ContentValues();
-        values.put(Browser.BookmarkColumns.BOOKMARK, "0");
-
-        cr.update(Browser.BOOKMARKS_URI,
-                  values,
-                  Browser.BookmarkColumns.URL + " = ?",
-                  new String[] { uri });
-    }
-
-    public void removeBookmarkPost11(ContentResolver cr, String uri) {
-        cr.delete(BOOKMARKS_CONTENT_URI_POST_11,
-                  Browser.BookmarkColumns.URL + " = ?",
-                  new String[] { uri });
-    }
-
-    public void removeBookmark(ContentResolver cr, int id) {
-        // Not implemented
-    }
-
-    public void removeBookmarksWithURL(ContentResolver cr, String uri) {
-        if (Build.VERSION.SDK_INT >= 11)
-            removeBookmarkPost11(cr, uri);
-        else
-            removeBookmarkPre11(cr, uri);
-    }
-
-    public void addReadingListItem(ContentResolver cr, String title, String uri) {
-        // Do nothing
-    }
-
-    public void removeReadingListItemWithURL(ContentResolver cr, String uri) {
-        // Do nothing
-    }
-
-    public void registerBookmarkObserverPre11(ContentResolver cr, ContentObserver observer) {
-        cr.registerContentObserver(Browser.BOOKMARKS_URI, false, observer);
-    }
-
-    public void registerBookmarkObserverPost11(ContentResolver cr, ContentObserver observer) {
-        cr.registerContentObserver(BOOKMARKS_CONTENT_URI_POST_11, false, observer);
-    }
-
-    public void registerBookmarkObserver(ContentResolver cr, ContentObserver observer) {
-        if (Build.VERSION.SDK_INT >= 11)
-            registerBookmarkObserverPost11(cr, observer);
-        else
-            registerBookmarkObserverPre11(cr, observer);
-    }
-
-    public void registerHistoryObserver(ContentResolver cr, ContentObserver observer) {
-        // Not implemented
-    }
-
-    public BitmapDrawable getFaviconForUrl(ContentResolver cr, String uri) {
-        Cursor c = cr.query(Browser.BOOKMARKS_URI,
-                            new String[] { Browser.BookmarkColumns.FAVICON },
-                            Browser.BookmarkColumns.URL + " = ?",
-                            new String[] { uri },
-                            null);
-
-        if (!c.moveToFirst()) {
-            c.close();
-            return null;
-        }
-
-        int faviconIndex = c.getColumnIndexOrThrow(Browser.BookmarkColumns.FAVICON);
-
-        byte[] b = c.getBlob(faviconIndex);
-        c.close();
-
-        if (b == null)
-            return null;
-
-        Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
-        return new BitmapDrawable(bitmap);
-    }
-
-    public void updateFaviconForUrl(ContentResolver cr, String uri,
-            BitmapDrawable favicon) {
-        Bitmap bitmap = favicon.getBitmap();
-
-        ByteArrayOutputStream stream = new ByteArrayOutputStream();
-        bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
-
-        ContentValues values = new ContentValues();
-        values.put(Browser.BookmarkColumns.FAVICON, stream.toByteArray());
-        values.put(Browser.BookmarkColumns.URL, uri);
-
-        int updated = cr.update(Browser.BOOKMARKS_URI,
-                                values,
-                                Browser.BookmarkColumns.URL + " = ?",
-                                new String[] { uri });
-
-        if (updated == 0) {
-            try {
-                cr.insert(Browser.BOOKMARKS_URI, values);
-            } catch (SQLiteConstraintException e) {
-                // insert() mysteriously and intermittently fails with "error
-                // code 19: constraint failed" on some Honeycomb and ICS
-                // devices. Bookmark favicons are not a critical feature, so
-                // we can ignore this error for now. bug 711977; bug 712791
-                Log.e(LOGTAG,
-                      String.format("Inserting favicon for \"%s\" failed with SQLiteConstraintException: %s",
-                                    uri, e.getMessage()));
-            }
-        }
-    }
-
-    public void updateThumbnailForUrl(ContentResolver cr, String uri,
-            BitmapDrawable thumbnail) {
-        Bitmap bitmap = thumbnail.getBitmap();
-
-        ContentValues values = new ContentValues();
-        ByteArrayOutputStream bos = new ByteArrayOutputStream();
-        bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
-
-        values.put(URL_COLUMN_THUMBNAIL, bos.toByteArray());
-        values.put(Browser.BookmarkColumns.URL, uri);
-
-        int updated = cr.update(Browser.BOOKMARKS_URI,
-                                values,
-                                Browser.BookmarkColumns.URL + " = ?",
-                                new String[] { uri });
-
-        if (updated == 0)
-            cr.insert(Browser.BOOKMARKS_URI, values);
-    }
-
-    public byte[] getThumbnailForUrl(ContentResolver cr, String uri) {
-        Cursor c = cr.query(Browser.BOOKMARKS_URI,
-                            new String[] { URL_COLUMN_THUMBNAIL },
-                            Browser.BookmarkColumns.URL + " = ?",
-                            new String[] { uri },
-                            null);
-
-        if (!c.moveToFirst()) {
-            c.close();
-            return null;
-        }
-
-        int thumbnailIndex = c.getColumnIndexOrThrow(URL_COLUMN_THUMBNAIL);
-
-        byte[] b = c.getBlob(thumbnailIndex);
-        c.close();
-
-        return b;
-    }
-
-    private static class AndroidDBCursor extends CursorWrapper {
-        public AndroidDBCursor(Cursor c) {
-            super(c);
-        }
-
-        private String translateColumnName(String columnName) {
-            if (columnName.equals(BrowserDB.URLColumns.URL)) {
-                columnName = Browser.BookmarkColumns.URL;
-            } else if (columnName.equals(BrowserDB.URLColumns.TITLE)) {
-                columnName = Browser.BookmarkColumns.TITLE;
-            } else if (columnName.equals(BrowserDB.URLColumns.FAVICON)) {
-                columnName = Browser.BookmarkColumns.FAVICON;
-            } else if (columnName.equals(BrowserDB.URLColumns.THUMBNAIL)) {
-                columnName = URL_COLUMN_THUMBNAIL;
-            } else if (columnName.equals(BrowserDB.URLColumns.DATE_LAST_VISITED)) {
-                columnName = Browser.BookmarkColumns.DATE;
-            } else if (columnName.equals(BrowserDB.URLColumns.VISITS)) {
-                columnName = Browser.BookmarkColumns.VISITS;
-            }
-
-            return columnName;
-        }
-
-        @Override
-        public int getColumnIndex(String columnName) {
-            return super.getColumnIndex(translateColumnName(columnName));
-        }
-
-        @Override
-        public int getColumnIndexOrThrow(String columnName) {
-            return super.getColumnIndexOrThrow(translateColumnName(columnName));
-        }
-    }
-}
--- a/mobile/android/base/db/BrowserProvider.java.in
+++ b/mobile/android/base/db/BrowserProvider.java.in
@@ -1022,22 +1022,25 @@ public class BrowserProvider extends Con
                         break;
 
                     case 7:
                         upgradeDatabaseFrom6to7(db);
                         break;
 
                     case 8:
                         upgradeDatabaseFrom7to8(db);
+                        break;
 
                     case 9:
                         upgradeDatabaseFrom8to9(db);
+                        break;
 
                     case 10:
                         upgradeDatabaseFrom9to10(db);
+                        break;
                  }
              }
 
              db.setTransactionSuccessful();
              db.endTransaction();
         }
 
         @Override
--- a/mobile/android/base/db/TabsProvider.java.in
+++ b/mobile/android/base/db/TabsProvider.java.in
@@ -23,31 +23,32 @@ import org.mozilla.gecko.util.GeckoBackg
 
 import android.content.ContentProvider;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.UriMatcher;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
+import android.database.SQLException;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.net.Uri;
 import android.os.Build;
 import android.text.TextUtils;
 import android.util.Log;
 
 public class TabsProvider extends ContentProvider {
     private static final String LOGTAG = "GeckoTabsProvider";
     private Context mContext;
 
     static final String DATABASE_NAME = "tabs.db";
 
-    static final int DATABASE_VERSION = 1;
+    static final int DATABASE_VERSION = 2;
 
     static final String TABLE_TABS = "tabs";
     static final String TABLE_CLIENTS = "clients";
 
     static final int TABS = 600;
     static final int TABS_ID = 601;
     static final int CLIENTS = 602;
     static final int CLIENTS_ID = 603;
@@ -150,22 +151,48 @@ public class TabsProvider extends Conten
                     Clients.GUID + " TEXT PRIMARY KEY," +
                     Clients.NAME + " TEXT," +
                     Clients.LAST_MODIFIED + " INTEGER" +
                     ");");
 
             // Index on GUID.
             db.execSQL("CREATE INDEX " + INDEX_CLIENTS_GUID + " ON " + TABLE_CLIENTS + "("
                     + Clients.GUID + ")");
+
+            createLocalClient(db);
+        }
+
+        // Insert a client row for our local Fennec client.
+        private void createLocalClient(SQLiteDatabase db) {
+            debug("Inserting local Fennec client into " + TABLE_CLIENTS + " table");
+
+            ContentValues values = new ContentValues();
+            values.put(BrowserContract.Clients.LAST_MODIFIED, System.currentTimeMillis());
+            db.insertOrThrow(TABLE_CLIENTS, null, values);
         }
 
         @Override
         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
             debug("Upgrading tabs.db: " + db.getPath() + " from " +
                     oldVersion + " to " + newVersion);
+
+            db.beginTransaction();
+
+            // We have to do incremental upgrades until we reach the current
+            // database schema version.
+            for (int v = oldVersion + 1; v <= newVersion; v++) {
+                switch(v) {
+                    case 2:
+                        createLocalClient(db);
+                        break;
+                 }
+             }
+
+             db.setTransactionSuccessful();
+             db.endTransaction();
         }
 
         @Override
         public void onOpen(SQLiteDatabase db) {
             debug("Opening tabs.db: " + db.getPath());
 
             // From Honeycomb on, it's possible to run several db
             // commands in parallel using multiple connections.
@@ -585,9 +612,43 @@ public class TabsProvider extends Conten
 
     int deleteValues(Uri uri, String selection, String[] selectionArgs, String table) {
         debug("Deleting tabs for URI: " + uri);
 
         final SQLiteDatabase db = getWritableDatabase(uri);
 
         return db.delete(table, selection, selectionArgs);
     }
+
+    @Override
+    public int bulkInsert(Uri uri, ContentValues[] values) {
+        if (values == null)
+            return 0;
+
+        int numValues = values.length;
+        int successes = 0;
+
+        final SQLiteDatabase db = getWritableDatabase(uri);
+
+        db.beginTransaction();
+        try {
+            for (int i = 0; i < numValues; i++) {
+                try {
+                    insertInTransaction(uri, values[i]);
+                    successes++;
+                } catch (SQLException e) {
+                    Log.e(LOGTAG, "SQLException in bulkInsert", e);
+
+                    // Restart the transaction to continue insertions.
+                    db.setTransactionSuccessful();
+                    db.endTransaction();
+                    db.beginTransaction();
+                }
+            }
+            trace("Flushing DB bulkinsert...");
+            db.setTransactionSuccessful();
+        } finally {
+            db.endTransaction();
+        }
+
+        return successes;
+    }
 }
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -40,16 +40,18 @@
 <!ENTITY bookmark_removed "Bookmark removed">
 <!ENTITY bookmark_updated "Bookmark updated">
 
 <!ENTITY history_today_section "Today">
 <!ENTITY history_yesterday_section "Yesterday">
 <!ENTITY history_week_section "7 days ago">
 <!ENTITY history_older_section "Older than 7 days">
 
+<!ENTITY go "Go">
+<!ENTITY search "Search">
 <!ENTITY reload "Reload">
 <!ENTITY forward "Forward">
 <!ENTITY menu "Menu">
 <!ENTITY back "Back">
 <!ENTITY stop "Stop">
 <!ENTITY site_security "Site Security">
 
 <!ENTITY close_tab "Close Tab">
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -100,16 +100,18 @@
   <string name="pref_private_data_formdata">&pref_private_data_formdata;</string>
   <string name="pref_private_data_cookies2">&pref_private_data_cookies2;</string>
   <string name="pref_private_data_passwords">&pref_private_data_passwords;</string>
   <string name="pref_private_data_cache">&pref_private_data_cache;</string>
   <string name="pref_private_data_offlineApps">&pref_private_data_offlineApps;</string>
   <string name="pref_private_data_siteSettings">&pref_private_data_siteSettings;</string>
   <string name="pref_import_android">&pref_import_android;</string>
 
+  <string name="go">&go;</string>
+  <string name="search">&search;</string>
   <string name="reload">&reload;</string>
   <string name="forward">&forward;</string>
   <string name="menu">&menu;</string>
   <string name="back">&back;</string>
   <string name="stop">&stop;</string>
   <string name="site_security">&site_security;</string>
   <string name="close_tab">&close_tab;</string>
   <string name="new_tab">&new_tab;</string>
--- a/mobile/android/chrome/content/Readability.js
+++ b/mobile/android/chrome/content/Readability.js
@@ -25,16 +25,18 @@ let Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 
 function dump(s) {
   Services.console.logStringMessage("Reader: (Readability) " + s);
 };
 
 var Readability = function(uri, doc) {
+  const ENABLE_LOGGING = false;
+
   this._uri = uri;
   this._doc = doc;
   this._biggestFrame = false;
 
   // Start with all flags set
   this._flags = this.FLAG_STRIP_UNLIKELYS |
                 this.FLAG_WEIGHT_CLASSES |
                 this.FLAG_CLEAN_CONDITIONALLY;
@@ -44,16 +46,25 @@ var Readability = function(uri, doc) {
   this._parsedPages = {};
 
   // A list of the ETag headers of pages we've parsed, in case they happen to match,
   // we'll know it's a duplicate.
   this._pageETags = {};
 
   // Make an AJAX request for each page and append it to the document.
   this._curPageNum = 1;
+
+  // Control whether log messages are sent to the console
+  if (ENABLE_LOGGING) {
+    this.log = function (msg) {
+      dump("Reader: (Readability) " + msg);
+    };
+  } else {
+    this.log = function () {};
+  }
 }
 
 Readability.prototype = {
   FLAG_STRIP_UNLIKELYS: 0x1,
   FLAG_WEIGHT_CLASSES: 0x2,
   FLAG_CLEAN_CONDITIONALLY: 0x4,
   FLAG_READABILITY_CHECK: 0x8,
 
@@ -68,17 +79,16 @@ Readability.prototype = {
   // Defined up here so we don't instantiate them repeatedly in loops.
   REGEXPS: {
     unlikelyCandidates: /combx|comment|community|disqus|extra|foot|header|menu|remark|rss|shoutbox|sidebar|sponsor|ad-break|agegate|pagination|pager|popup|tweet|twitter/i,
     okMaybeItsACandidate: /and|article|body|column|main|shadow/i,
     positive: /article|body|content|entry|hentry|main|page|pagination|post|text|blog|story/i,
     negative: /combx|comment|com-|contact|foot|footer|footnote|masthead|media|meta|outbrain|promo|related|scroll|shoutbox|sidebar|sponsor|shopping|tags|tool|widget/i,
     extraneous: /print|archive|comment|discuss|e[\-]?mail|share|reply|all|login|sign|single/i,
     divToPElements: /<(a|blockquote|dl|div|img|ol|p|pre|table|ul)/i,
-    replaceBrs: /(<br[^>]*>[ \n\r\t]*){2,}/gi,
     replaceFonts: /<(\/?)font[^>]*>/gi,
     trim: /^\s+|\s+$/g,
     normalize: /\s{2,}/g,
     killBreaks: /(<br\s*\/?>(\s|&nbsp;?)*){1,}/g,
     videos: /http:\/\/(www\.)?(youtube|vimeo)\.com/i,
     nextLink: /(next|weiter|continue|>([^\|]|$)|»([^\|]|$))/i,
     prevLink: /(prev|earl|old|new|<|«)/i
   },
@@ -218,17 +228,17 @@ Readability.prototype = {
     // append it to the document.
     if (doc.body === null) {
       let body = doc.createElement("body");
 
       try {
         doc.body = body;
       } catch(e) {
         doc.documentElement.appendChild(body);
-        dump(e);
+        this.log(e);
       }
     }
 
     let frames = doc.getElementsByTagName('frame');
     if (frames.length > 0) {
       let bestFrame = null;
 
       // The frame to try to run readability upon. Must be on same domain.
@@ -240,17 +250,17 @@ Readability.prototype = {
       for (let frameIndex = 0; frameIndex < frames.length; frameIndex += 1) {
         let frameSize = frames[frameIndex].offsetWidth + frames[frameIndex].offsetHeight;
 
         let canAccessFrame = false;
         try {
           let frameBody = frames[frameIndex].contentWindow.document.body;
           canAccessFrame = true;
         } catch(eFrames) {
-          dump(eFrames);
+          this.log(eFrames);
         }
 
         if (frameSize > biggestFrameSize) {
           biggestFrameSize = frameSize;
           this._biggestFrame = frames[frameIndex];
         }
 
         if (canAccessFrame && frameSize > bestFrameSize) {
@@ -278,21 +288,90 @@ Readability.prototype = {
     }
 
     // Remove all style tags in head
     let styleTags = doc.getElementsByTagName("style");
     for (let st = 0; st < styleTags.length; st += 1) {
       styleTags[st].textContent = "";
     }
 
-    // Turn all double br's into p's. Note, this is pretty costly as far
-    // as processing goes. Maybe optimize later.
-    doc.body.innerHTML =
-        doc.body.innerHTML.replace(this.REGEXPS.replaceBrs, '</p><p>').
-            replace(this.REGEXPS.replaceFonts, '<$1span>');
+    this._replaceBrs(doc.body);
+
+    doc.body.innerHTML = doc.body.innerHTML.replace(this.REGEXPS.replaceFonts, '<$1span>');
+  },
+
+  /**
+   * Replaces 2 or more successive <br> elements with a single <p>.
+   * Whitespace between <br> elements are ignored. For example:
+   *   <div>foo<br>bar<br> <br><br>abc</div>
+   * will become:
+   *   <div>foo<br>bar<p>abc</p></div>
+   */
+  _replaceBrs: function (elem) {
+    // ignore whitespace between elements
+    let whitespace = /^\s*$/;
+
+    /**
+     * Finds the next element, starting from the given node, and ignoring
+     * whitespace in between. If the given node is an element, the same node is
+     * returned.
+     */
+    function nextElement(node) {
+      let next = node;
+      while (next
+          && (next.nodeType != Node.ELEMENT_NODE)
+          && !whitespace.test(next.textContent)) {
+        next = next.nextSibling;
+      }
+      return next;
+    }
+
+    let brs = elem.getElementsByTagName("br");
+    for (let i = 0; i < brs.length; i++) {
+      let br = brs[i];
+      let next = br.nextSibling;
+
+      // Whether 2 or more <br> elements have been found and replaced with a
+      // <p> block.
+      let replaced = false;
+
+      // If we find a <br> chain, remove the <br>s until we hit another element
+      // or non-whitespace. This leaves behind the first <br> in the chain
+      // (which will be replaced with a <p> later).
+      while ((next = nextElement(next)) && (next.tagName == "BR")) {
+        replaced = true;
+        let sibling = next.nextSibling;
+        next.parentNode.removeChild(next);
+        next = sibling;
+      }
+
+      // If we removed a <br> chain, replace the remaining <br> with a <p>. Add
+      // all sibling nodes as children of the <p> until we hit another <br>
+      // chain.
+      if (replaced) {
+        let p = this._doc.createElement("p");
+        br.parentNode.replaceChild(p, br);
+
+        next = p.nextSibling;
+        while (next) {
+          // If we've hit another <br><br>, we're done adding children to this <p>.
+          if (next.tagName == "BR") {
+            let nextElem = nextElement(next);
+            if (nextElem && nextElem.tagName == "BR") {
+              break;
+            }
+          }
+          
+          // Otherwise, make this node a child of the new <p>.
+          let sibling = next.nextSibling;
+          p.appendChild(next);
+          next = sibling;
+        }
+      }
+    }
   },
 
   /**
    * Prepare the article node for display. Clean out any inline styles,
    * iframes, forms, strip extraneous <p> tags, etc.
    *
    * @param Element
    * @return void
@@ -439,17 +518,17 @@ Readability.prototype = {
 
       for (let nodeIndex = 0; (node = allElements[nodeIndex]); nodeIndex += 1) {
         // Remove unlikely candidates
         if (stripUnlikelyCandidates) {
           let unlikelyMatchString = node.className + node.id;
           if (unlikelyMatchString.search(this.REGEXPS.unlikelyCandidates) !== -1 &&
             unlikelyMatchString.search(this.REGEXPS.okMaybeItsACandidate) === -1 &&
             node.tagName !== "BODY") {
-            dump("Removing unlikely candidate - " + unlikelyMatchString);
+            this.log("Removing unlikely candidate - " + unlikelyMatchString);
             node.parentNode.removeChild(node);
             nodeIndex -= 1;
             continue;
           }
         }
 
         if (node.tagName === "P" || node.tagName === "TD" || node.tagName === "PRE")
           nodesToScore[nodesToScore.length] = node;
@@ -545,17 +624,17 @@ Readability.prototype = {
       let topCandidate = null;
       for (let c = 0, cl = candidates.length; c < cl; c += 1) {
         // Scale the final candidates score based on link density. Good content
         // should have a relatively small link density (5% or less) and be mostly
         // unaffected by this operation.
         candidates[c].readability.contentScore =
             candidates[c].readability.contentScore * (1 - this._getLinkDensity(candidates[c]));
 
-        dump('Candidate: ' + candidates[c] + " (" + candidates[c].className + ":" +
+        this.log('Candidate: ' + candidates[c] + " (" + candidates[c].className + ":" +
           candidates[c].id + ") with score " +
           candidates[c].readability.contentScore);
 
         if (!topCandidate ||
           candidates[c].readability.contentScore > topCandidate.readability.contentScore) {
           topCandidate = candidates[c];
         }
 
@@ -596,18 +675,18 @@ Readability.prototype = {
 
       let siblingScoreThreshold = Math.max(10, topCandidate.readability.contentScore * 0.2);
       let siblingNodes = topCandidate.parentNode.childNodes;
 
       for (let s = 0, sl = siblingNodes.length; s < sl; s += 1) {
         let siblingNode = siblingNodes[s];
         let append = false;
 
-        dump("Looking at sibling node: " + siblingNode + " (" + siblingNode.className + ":" + siblingNode.id + ")" + ((typeof siblingNode.readability !== 'undefined') ? (" with score " + siblingNode.readability.contentScore) : ''));
-        dump("Sibling has score " + (siblingNode.readability ? siblingNode.readability.contentScore : 'Unknown'));
+        this.log("Looking at sibling node: " + siblingNode + " (" + siblingNode.className + ":" + siblingNode.id + ")" + ((typeof siblingNode.readability !== 'undefined') ? (" with score " + siblingNode.readability.contentScore) : ''));
+        this.log("Sibling has score " + (siblingNode.readability ? siblingNode.readability.contentScore : 'Unknown'));
 
         if (siblingNode === topCandidate)
           append = true;
 
         let contentBonus = 0;
 
         // Give a bonus if sibling nodes and top candidates have the example same classname
         if (siblingNode.className === topCandidate.className && topCandidate.className !== "")
@@ -626,23 +705,23 @@ Readability.prototype = {
           if (nodeLength > 80 && linkDensity < 0.25) {
             append = true;
           } else if (nodeLength < 80 && linkDensity === 0 && nodeContent.search(/\.( |$)/) !== -1) {
             append = true;
           }
         }
 
         if (append) {
-          dump("Appending node: " + siblingNode);
+          this.log("Appending node: " + siblingNode);
 
           let nodeToAppend = null;
           if (siblingNode.nodeName !== "DIV" && siblingNode.nodeName !== "P") {
             // We have a node that isn't a common block level element, like a form or td tag.
             // Turn it into a div so it doesn't get filtered out later by accident. */
-            dump("Altering siblingNode of " + siblingNode.nodeName + ' to div.');
+            this.log("Altering siblingNode of " + siblingNode.nodeName + ' to div.');
 
             nodeToAppend = doc.createElement("DIV");
             nodeToAppend.id = siblingNode.id;
             nodeToAppend.innerHTML = siblingNode.innerHTML;
           } else {
             nodeToAppend = siblingNode;
             s -= 1;
             sl -= 1;
@@ -1006,17 +1085,17 @@ Readability.prototype = {
           topPage = possiblePages[page];
         }
       }
     }
 
     if (topPage) {
       let nextHref = topPage.href.replace(/\/$/,'');
 
-      dump('NEXT PAGE IS ' + nextHref);
+      this.log('NEXT PAGE IS ' + nextHref);
       this._parsedPages[nextHref] = true;
       return nextHref;
     } else {
       return null;
     }
   },
 
   _successfulRequest: function(request) {
@@ -1082,17 +1161,17 @@ Readability.prototype = {
     (function(pageUrl, thisPage) {
       this._ajax(pageUrl, {
         success: function(r) {
 
           // First, check to see if we have a matching ETag in headers - if we do, this is a duplicate page.
           let eTag = r.getResponseHeader('ETag');
           if (eTag) {
             if (eTag in this._pageETags) {
-              dump("Exact duplicate page found via ETag. Aborting.");
+              this.log("Exact duplicate page found via ETag. Aborting.");
               articlePage.style.display = 'none';
               return;
             } else {
               this._pageETags[eTag] = 1;
             }
           }
 
           // TODO: this ends up doubling up page numbers on NYTimes articles. Need to generically parse those away.
@@ -1104,45 +1183,45 @@ Readability.prototype = {
           // - Turn any noscript tags into divs so that we can parse them. This
           //   allows us to find any next page links hidden via javascript.
           // - Turn all double br's into p's - was handled by prepDocument in the original view.
           //   Maybe in the future abstract out prepDocument to work for both the original document
           //   and AJAX-added pages.
           let responseHtml = r.responseText.replace(/\n/g,'\uffff').replace(/<script.*?>.*?<\/script>/gi, '');
           responseHtml = responseHtml.replace(/\n/g,'\uffff').replace(/<script.*?>.*?<\/script>/gi, '');
           responseHtml = responseHtml.replace(/\uffff/g,'\n').replace(/<(\/?)noscript/gi, '<$1div');
-          responseHtml = responseHtml.replace(this.REGEXPS.replaceBrs, '</p><p>');
           responseHtml = responseHtml.replace(this.REGEXPS.replaceFonts, '<$1span>');
 
           page.innerHTML = responseHtml;
+          this._replaceBrs(page);
 
           // Reset all flags for the next page, as they will search through it and
           // disable as necessary at the end of grabArticle.
           this._flags = 0x1 | 0x2 | 0x4;
 
           let nextPageLink = this._findNextPageLink(page);
           
           // NOTE: if we end up supporting _appendNextPage(), we'll need to
           // change this call to be async
           let content = this._grabArticle(page);
 
           if (!content) {
-            dump("No content found in page to append. Aborting.");
+            this.log("No content found in page to append. Aborting.");
             return;
           }
 
           // Anti-duplicate mechanism. Essentially, get the first paragraph of our new page.
           // Compare it against all of the the previous document's we've gotten. If the previous
           // document contains exactly the innerHTML of this first paragraph, it's probably a duplicate.
           let firstP = content.getElementsByTagName("P").length ? content.getElementsByTagName("P")[0] : null;
           if (firstP && firstP.innerHTML.length > 100) {
             for (let i = 1; i <= this._curPageNum; i += 1) {
               let rPage = doc.getElementById('readability-page-' + i);
               if (rPage && rPage.innerHTML.indexOf(firstP.innerHTML) !== -1) {
-                dump('Duplicate of page ' + i + ' - skipping.');
+                this.log('Duplicate of page ' + i + ' - skipping.');
                 articlePage.style.display = 'none';
                 this._parsedPages[pageUrl] = true;
                 return;
               }
             }
           }
 
           this._removeScripts(content);
@@ -1258,17 +1337,17 @@ Readability.prototype = {
     // Traverse backwards so we can remove nodes at the same time
     // without effecting the traversal.
     //
     // TODO: Consider taking into account original contentScore here.
     for (let i = curTagsLength-1; i >= 0; i -= 1) {
       let weight = this._getClassWeight(tagsList[i]);
       let contentScore = (typeof tagsList[i].readability !== 'undefined') ? tagsList[i].this._contentScore : 0;
 
-      dump("Cleaning Conditionally " + tagsList[i] + " (" + tagsList[i].className + ":" + tagsList[i].id + ")" + ((typeof tagsList[i].readability !== 'undefined') ? (" with score " + tagsList[i].this._contentScore) : ''));
+      this.log("Cleaning Conditionally " + tagsList[i] + " (" + tagsList[i].className + ":" + tagsList[i].id + ")" + ((typeof tagsList[i].readability !== 'undefined') ? (" with score " + tagsList[i].this._contentScore) : ''));
 
       if (weight + contentScore < 0) {
         tagsList[i].parentNode.removeChild(tagsList[i]);
       } else if (this._getCharCount(tagsList[i],',') < 10) {
         // If there are not very many commas, and the number of
         // non-paragraph elements is more than paragraphs or other
         // ominous signs, remove the element.
         let p = tagsList[i].getElementsByTagName("p").length;
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -5961,22 +5961,16 @@ var WebappsUI = {
         break;
       case "webapps-sync-install":
         // Create a system notification allowing the user to launch the app
         DOMApplicationRegistry.getManifestFor(data.origin, (function(aManifest) {
           if (!aManifest)
             return;
           let manifest = new DOMApplicationManifest(aManifest, data.origin);
 
-          // The manifest is stored as UTF-8, sendMessageToJava expects UTF-16. Convert before sending
-          let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
-          converter.charset = "UTF-8";
-          let name = manifest.name ? converter.ConvertToUnicode(manifest.name) :
-                                     converter.ConvertToUnicode(manifest.fullLaunchPath());
-
           let observer = {
             observe: function (aSubject, aTopic) {
               if (aTopic == "alertclickcallback") {
                 WebappsUI.openURL(manifest.fullLaunchPath(), data.origin);
               }
             }
           };
 
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -233,20 +233,25 @@ pref("gfx.canvas.azure.enabled", true);
 // e.g., pref("gfx.canvas.azure.backends", "direct2d,skia,cairo");
 pref("gfx.canvas.azure.backends", "direct2d,cairo");
 pref("gfx.content.azure.enabled", true);
 #else
 #ifdef XP_MACOSX
 pref("gfx.canvas.azure.enabled", true);
 pref("gfx.canvas.azure.backends", "cg");
 #else
+#ifdef ANDROID
+pref("gfx.canvas.azure.enabled", true);
+pref("gfx.canvas.azure.backends", "cairo");
+#else
 pref("gfx.canvas.azure.enabled", false);
 pref("gfx.canvas.azure.backends", "cairo");
 #endif
 #endif
+#endif
 
 #ifdef ANDROID
 pref("gfx.textures.poweroftwo.force-enabled", false);
 #endif
 
 pref("gfx.work-around-driver-bugs", true);
 pref("gfx.prefer-mesa-llvmpipe", false);
 
--- a/netwerk/base/src/nsFileStreams.h
+++ b/netwerk/base/src/nsFileStreams.h
@@ -171,16 +171,17 @@ protected:
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 
 class nsPartialFileInputStream : public nsFileInputStream,
                                  public nsIPartialFileInputStream
 {
 public:
+    using nsFileInputStream::Init;
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIPARTIALFILEINPUTSTREAM
     NS_DECL_NSIIPCSERIALIZABLE
 
     NS_IMETHOD Tell(PRInt64 *aResult);
     NS_IMETHOD Available(PRUint32 *aResult);
     NS_IMETHOD Read(char* aBuf, PRUint32 aCount, PRUint32* aResult);
     NS_IMETHOD Seek(PRInt32 aWhence, PRInt64 aOffset);
--- a/netwerk/wifi/osx_corewlan.mm
+++ b/netwerk/wifi/osx_corewlan.mm
@@ -1,13 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #import <Cocoa/Cocoa.h>
+#import <CoreWLAN/CoreWLAN.h>
 
 #include <mach-o/dyld.h>
 #include <dlfcn.h>
 #include <unistd.h>
 
 #include <objc/objc.h>
 #include <objc/objc-runtime.h>
 
--- a/testing/marionette/client/marionette/marionette.py
+++ b/testing/marionette/client/marionette/marionette.py
@@ -24,18 +24,18 @@ class HTMLElement(object):
     def __init__(self, marionette, id):
         self.marionette = marionette
         assert(id is not None)
         self.id = id
 
     def __str__(self):
         return self.id
 
-    def equals(self, other_element):
-        return self.marionette._send_message('elementsEqual', 'value', elements=[self.id, other_element.id])
+    def __eq__(self, other_element):
+        return self.id == other_element.id
 
     def find_element(self, method, target):
         return self.marionette.find_element(method, target, self.id)
 
     def find_elements(self, method, target):
         return self.marionette.find_elements(method, target, self.id)
 
     def get_attribute(self, attribute):
@@ -269,16 +269,21 @@ class Marionette(object):
         response = self._send_message('getTitle', 'value') 
         return response
 
     @property
     def window_handles(self):
         response = self._send_message('getWindows', 'value')
         return response
 
+    @property
+    def page_source(self):
+        response = self._send_message('getPageSource', 'value')
+        return response
+
     def close(self, window_id=None):
         if not window_id:
             window_id = self.current_window_handle
         response = self._send_message('closeWindow', 'ok', value=window_id)
         return response
 
     def set_context(self, context):
         assert(context == self.CONTEXT_CHROME or context == self.CONTEXT_CONTENT)
--- a/testing/marionette/client/marionette/tests/unit/test_findelement.py
+++ b/testing/marionette/client/marionette/tests/unit/test_findelement.py
@@ -9,114 +9,114 @@ from errors import NoSuchElementExceptio
 
 class TestElements(MarionetteTestCase):
     def test_id(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         el = self.marionette.execute_script("return window.document.getElementById('mozLink');")
         found_el = self.marionette.find_element("id", "mozLink")
         self.assertEqual(HTMLElement, type(found_el))
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
 
     def test_child_element(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         el = self.marionette.find_element("id", "divLink")
         div = self.marionette.find_element("id", "testDiv")
         found_el = div.find_element("tag name", "a")
         self.assertEqual("a", found_el.tag_name)
         self.assertEqual(HTMLElement, type(found_el))
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
 
     def test_child_elements(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         el = self.marionette.find_element("id", "divLink2")
         div = self.marionette.find_element("id", "testDiv")
         found_els = div.find_elements("tag name", "a")
         self.assertTrue(el.id in [found_el.id for found_el in found_els])
 
     def test_tag_name(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         el = self.marionette.execute_script("return window.document.getElementsByTagName('body')[0];")
         found_el = self.marionette.find_element("tag name", "body")
         self.assertEqual('body', found_el.tag_name)
         self.assertEqual(HTMLElement, type(found_el))
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
         found_el = self.marionette.find_elements("tag name", "body")[0]
         self.assertEqual('body', found_el.tag_name)
         self.assertEqual(HTMLElement, type(found_el))
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
 
     def test_class_name(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         el = self.marionette.execute_script("return window.document.getElementsByClassName('linkClass')[0];")
         found_el = self.marionette.find_element("class name", "linkClass")
         self.assertEqual(HTMLElement, type(found_el));
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
         found_el = self.marionette.find_elements("class name", "linkClass")[0]
         self.assertEqual(HTMLElement, type(found_el));
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
 
     def test_name(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         el = self.marionette.execute_script("return window.document.getElementsByName('myInput')[0];")
         found_el = self.marionette.find_element("name", "myInput")
         self.assertEqual(HTMLElement, type(found_el))
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
         found_el = self.marionette.find_elements("name", "myInput")[0]
         self.assertEqual(HTMLElement, type(found_el))
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
     
     def test_selector(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         el = self.marionette.execute_script("return window.document.getElementById('testh1');")
         found_el = self.marionette.find_element("css selector", "h1")
         self.assertEqual(HTMLElement, type(found_el))
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
         found_el = self.marionette.find_elements("css selector", "h1")[0]
         self.assertEqual(HTMLElement, type(found_el))
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
 
     def test_link_text(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         el = self.marionette.execute_script("return window.document.getElementById('mozLink');")
         found_el = self.marionette.find_element("link text", "Click me!")
         self.assertEqual(HTMLElement, type(found_el))
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
         found_el = self.marionette.find_elements("link text", "Click me!")[0]
         self.assertEqual(HTMLElement, type(found_el))
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
 
     def test_partial_link_text(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         el = self.marionette.execute_script("return window.document.getElementById('mozLink');")
         found_el = self.marionette.find_element("partial link text", "Click m")
         self.assertEqual(HTMLElement, type(found_el))
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
         found_el = self.marionette.find_elements("partial link text", "Click m")[0]
         self.assertEqual(HTMLElement, type(found_el))
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
 
     def test_xpath(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         el = self.marionette.execute_script("return window.document.getElementById('mozLink');")
         found_el = self.marionette.find_element("xpath", "id('mozLink')")
         self.assertEqual(HTMLElement, type(found_el))
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
         found_el = self.marionette.find_elements("xpath", "id('mozLink')")[0]
         self.assertEqual(HTMLElement, type(found_el))
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
 
     def test_not_found(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         self.assertRaises(NoSuchElementException, self.marionette.find_element, "id", "I'm not on the page")
 
     def test_timeout(self):
         test_html = self.marionette.absolute_url("test.html")
@@ -137,49 +137,49 @@ class TestElementsChrome(MarionetteTestC
         self.marionette.execute_script("window.close();")
         self.marionette.switch_to_window(self.win)
         MarionetteTestCase.tearDown(self)
 
     def test_id(self):
         el = self.marionette.execute_script("return window.document.getElementById('textInput');")
         found_el = self.marionette.find_element("id", "textInput")
         self.assertEqual(HTMLElement, type(found_el))
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
 
     def test_child_element(self):
         el = self.marionette.find_element("id", "textInput")
         parent = self.marionette.find_element("id", "things")
         found_el = parent.find_element("tag name", "textbox")
         self.assertEqual(HTMLElement, type(found_el))
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
 
     def test_child_elements(self):
         el = self.marionette.find_element("id", "textInput3")
         parent = self.marionette.find_element("id", "things")
         found_els = parent.find_elements("tag name", "textbox")
         self.assertTrue(el.id in [found_el.id for found_el in found_els])
 
     def test_tag_name(self):
         el = self.marionette.execute_script("return window.document.getElementsByTagName('vbox')[0];")
         found_el = self.marionette.find_element("tag name", "vbox")
         self.assertEquals('vbox', found_el.tag_name)
         self.assertEqual(HTMLElement, type(found_el))
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
 
     def test_class_name(self):
         el = self.marionette.execute_script("return window.document.getElementsByClassName('asdf')[0];")
         found_el = self.marionette.find_element("class name", "asdf")
         self.assertEqual(HTMLElement, type(found_el));
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
 
     def test_xpath(self):
         el = self.marionette.execute_script("return window.document.getElementById('testBox');")
         found_el = self.marionette.find_element("xpath", "id('testBox')")
         self.assertEqual(HTMLElement, type(found_el));
-        self.assertEqual(el.id, found_el.id)
+        self.assertEqual(el, found_el)
 
     def test_not_found(self):
         self.assertRaises(NoSuchElementException, self.marionette.find_element, "id", "I'm not on the page")
 
 
     def test_timeout(self):
         self.assertRaises(NoSuchElementException, self.marionette.find_element, "id", "myid")
         self.assertTrue(True, self.marionette.set_search_timeout(4000))
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/tests/unit/test_pagesource.py
@@ -0,0 +1,40 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_test import MarionetteTestCase
+
+class TestPageSource(MarionetteTestCase):
+    def testShouldReturnTheSourceOfAPage(self):
+        test_html = self.marionette.absolute_url("testPageSource.html")
+        self.marionette.navigate(test_html)
+        source = self.marionette.page_source
+        self.assertTrue("<html" in source)
+        self.assertTrue("PageSource" in source)
+
+    def testShouldReturnAXMLDocumentSource(self):
+        test_xml = self.marionette.absolute_url("testPageSource.xml")
+        self.marionette.navigate(test_xml)
+        source = self.marionette.page_source
+        import re
+        self.assertEqual(re.sub("\s", "", source), "<xml><foo><bar>baz</bar></foo></xml>")
+
+class TestPageSourceChrome(MarionetteTestCase):
+    def setUp(self):
+        MarionetteTestCase.setUp(self)
+        self.marionette.set_context("chrome")
+        self.win = self.marionette.current_window_handle
+        self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', '_blank', 'chrome,centerscreen');")
+
+    def tearDown(self):
+        self.marionette.execute_script("window.close();")
+        self.marionette.switch_to_window(self.win)
+        MarionetteTestCase.tearDown(self)
+
+    def testShouldReturnXULDetails(self):
+        wins = self.marionette.window_handles
+        wins.remove(self.win)
+        newWin = wins.pop()
+        self.marionette.switch_to_window(newWin)
+        source  = self.marionette.page_source
+        self.assertTrue('<textbox id="textInput"' in source)
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/www/testPageSource.html
@@ -0,0 +1,14 @@
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!DOCTYPE html>
+<html>
+<head>
+<title>PageSource Test</title>
+</head>
+<body>
+  <p> Check the PageSource
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/www/testPageSource.xml
@@ -0,0 +1,5 @@
+<xml>
+  <foo>
+    <bar>baz</bar>
+  </foo>
+</xml>
--- a/testing/marionette/marionette-actors.js
+++ b/testing/marionette/marionette-actors.js
@@ -760,16 +760,31 @@ MarionetteDriverActor.prototype = {
   /**
    * Gets the current title of the window
    */
   getTitle: function MDA_getTitle() {
     this.sendAsync("getTitle", {});
   },
 
   /**
+   * Gets the page source of the content document
+   */
+  getPageSource: function MDA_getPageSource(){
+    if (this.context == "chrome"){
+      var curWindow = this.getCurrentWindow();
+      var XMLSerializer = curWindow.XMLSerializer; 
+      var pageSource = new XMLSerializer().serializeToString(curWindow.document);
+      this.sendResponse(pageSource);
+    }
+    else {
+      this.sendAsync("getPageSource", {});
+    }
+  },
+
+  /**
    * Go back in history
    */
   goBack: function MDA_goBack() {
     this.sendAsync("goBack", {});
   },
 
   /**
    * Go forward in history
@@ -1445,16 +1460,17 @@ MarionetteDriverActor.prototype.requestT
   "getElementText": MarionetteDriverActor.prototype.getElementText,
   "getElementTagName": MarionetteDriverActor.prototype.getElementTagName,
   "isElementDisplayed": MarionetteDriverActor.prototype.isElementDisplayed,
   "isElementEnabled": MarionetteDriverActor.prototype.isElementEnabled,
   "isElementSelected": MarionetteDriverActor.prototype.isElementSelected,
   "sendKeysToElement": MarionetteDriverActor.prototype.sendKeysToElement,
   "clearElement": MarionetteDriverActor.prototype.clearElement,
   "getTitle": MarionetteDriverActor.prototype.getTitle,
+  "getPageSource": MarionetteDriverActor.prototype.getPageSource,
   "goUrl": MarionetteDriverActor.prototype.goUrl,
   "getUrl": MarionetteDriverActor.prototype.getUrl,
   "goBack": MarionetteDriverActor.prototype.goBack,
   "goForward": MarionetteDriverActor.prototype.goForward,
   "refresh":  MarionetteDriverActor.prototype.refresh,
   "getWindow":  MarionetteDriverActor.prototype.getWindow,
   "getWindows":  MarionetteDriverActor.prototype.getWindows,
   "switchToFrame": MarionetteDriverActor.prototype.switchToFrame,
--- a/testing/marionette/marionette-listener.js
+++ b/testing/marionette/marionette-listener.js
@@ -87,16 +87,17 @@ function startListeners() {
   addMessageListenerId("Marionette:executeScript", executeScript);
   addMessageListenerId("Marionette:setScriptTimeout", setScriptTimeout);
   addMessageListenerId("Marionette:executeAsyncScript", executeAsyncScript);
   addMessageListenerId("Marionette:executeJSScript", executeJSScript);
   addMessageListenerId("Marionette:setSearchTimeout", setSearchTimeout);
   addMessageListenerId("Marionette:goUrl", goUrl);
   addMessageListenerId("Marionette:getUrl", getUrl);
   addMessageListenerId("Marionette:getTitle", getTitle);
+  addMessageListenerId("Marionette:getPageSource", getPageSource);
   addMessageListenerId("Marionette:goBack", goBack);
   addMessageListenerId("Marionette:goForward", goForward);
   addMessageListenerId("Marionette:refresh", refresh);
   addMessageListenerId("Marionette:findElementContent", findElementContent);
   addMessageListenerId("Marionette:findElementsContent", findElementsContent);
   addMessageListenerId("Marionette:clickElement", clickElement);
   addMessageListenerId("Marionette:getElementAttribute", getElementAttribute);
   addMessageListenerId("Marionette:getElementText", getElementText);
@@ -147,16 +148,17 @@ function deleteSession(msg) {
   removeMessageListenerId("Marionette:newSession", newSession);
   removeMessageListenerId("Marionette:executeScript", executeScript);
   removeMessageListenerId("Marionette:setScriptTimeout", setScriptTimeout);
   removeMessageListenerId("Marionette:executeAsyncScript", executeAsyncScript);
   removeMessageListenerId("Marionette:executeJSScript", executeJSScript);
   removeMessageListenerId("Marionette:setSearchTimeout", setSearchTimeout);
   removeMessageListenerId("Marionette:goUrl", goUrl);
   removeMessageListenerId("Marionette:getTitle", getTitle);
+  removeMessageListenerId("Marionette:getPageSource", getPageSource);
   removeMessageListenerId("Marionette:getUrl", getUrl);
   removeMessageListenerId("Marionette:goBack", goBack);
   removeMessageListenerId("Marionette:goForward", goForward);
   removeMessageListenerId("Marionette:refresh", refresh);
   removeMessageListenerId("Marionette:findElementContent", findElementContent);
   removeMessageListenerId("Marionette:findElementsContent", findElementsContent);
   removeMessageListenerId("Marionette:clickElement", clickElement);
   removeMessageListenerId("Marionette:getElementAttribute", getElementAttribute);
@@ -518,16 +520,25 @@ function getUrl(msg) {
 /**
  * Get the current Title of the window
  */
 function getTitle(msg) {
   sendResponse({value: curWindow.top.document.title});
 }
 
 /**
+ * Get the current page source 
+ */
+function getPageSource(msg) {
+  var XMLSerializer = curWindow.XMLSerializer;
+  var pageSource = new XMLSerializer().serializeToString(curWindow.document);
+  sendResponse({value: pageSource });
+}
+
+/**
  * Go back in history 
  */
 function goBack(msg) {
   curWindow.history.back();
   sendOk();
 }
 
 /**
--- a/toolkit/components/osfile/osfile_shared_allthreads.jsm
+++ b/toolkit/components/osfile/osfile_shared_allthreads.jsm
@@ -64,65 +64,65 @@
       * Abstraction above js-ctypes types.
       *
       * Use values of this type to register FFI functions. In addition to the
       * usual features of js-ctypes, values of this type perform the necessary
       * transformations to ensure that C errors are handled nicely, to connect
       * resources with their finalizer, etc.
       *
       * @param {string} name The name of the type. Must be unique.
-      * @param {function=} convert_from_c A function to call to construct a
-      * value of this type from a C return value.
+      * @param {CType} implementation The js-ctypes implementation of the type.
       *
       * @constructor
       */
-     function Type(name, implementation, convert_from_c) {
+     function Type(name, implementation) {
        if (!(typeof name == "string")) {
          throw new TypeError("Type expects as first argument a name, got: "
                              + name);
        }
        if (!(implementation instanceof ctypes.CType)) {
          throw new TypeError("Type expects as second argument a ctypes.CType"+
                              ", got: " + implementation);
        }
-       this.name = name;
-       this.implementation = implementation;
-       if (convert_from_c) {
-         this.convert_from_c = convert_from_c;
-       } else {// Optimization: Ensure harmony of shapes
-         this.convert_from_c = Type.prototype.convert_from_c;
-       }
+       Object.defineProperty(this, "name", { value: name });
+       Object.defineProperty(this, "implementation", { value: implementation });
      }
      Type.prototype = {
-       convert_from_c: function(value) {
+       /**
+        * Import a value from C.
+        *
+        * In this default implementation, return the value
+        * unchanged.
+        */
+       importFromC: function default_importFromC(value) {
          return value;
        },
 
        /**
         * A pointer/array used to pass data to the foreign function.
         */
        get in_ptr() {
          delete this.in_ptr;
-         let ptr_t = new Type("[int] " + this.name + "*",
+         let ptr_t = new PtrType("[in] " + this.name + "*",
            this.implementation.ptr);
          Object.defineProperty(this, "in_ptr",
            {
              get: function() {
                return ptr_t;
              }
            });
          return ptr_t;
        },
 
        /**
         * A pointer/array used to receive data from the foreign function.
         */
        get out_ptr() {
          delete this.out_ptr;
-         let ptr_t = new Type("[out] " + this.name + "*",
+         let ptr_t = new PtrType("[out] " + this.name + "*",
            this.implementation.ptr);
          Object.defineProperty(this, "out_ptr",
            {
              get: function() {
                return ptr_t;
              }
            });
          return ptr_t;
@@ -132,50 +132,70 @@
         * A pointer/array used to both pass data to the foreign function
         * and receive data from the foreign function.
         *
         * Whenever possible, prefer using |in_ptr| or |out_ptr|, which
         * are generally faster.
         */
        get inout_ptr() {
          delete this.inout_ptr;
-         let ptr_t = new Type("[inout] " + this.name + "*",
+         let ptr_t = new PtrType("[inout] " + this.name + "*",
            this.implementation.ptr);
          Object.defineProperty(this, "inout_ptr",
            {
              get: function() {
                return ptr_t;
              }
            });
          return ptr_t;
        },
 
        /**
         * Attach a finalizer to a type.
         */
        releaseWith: function releaseWith(finalizer) {
          let parent = this;
-         let type = new Type("[auto " + finalizer +"] " + this.name,
-           this.implementation,
-           function release(value, operation) {
-             return ctypes.CDataFinalizer(
-               parent.convert_from_c(value, operation),
-               finalizer);
-           });
+         let type = this.withName("[auto " + this.name + ", " + finalizer + "] ");
+         type.importFromC = function importFromC(value, operation) {
+           return ctypes.CDataFinalizer(
+             parent.importFromC(value, operation),
+             finalizer);
+         };
          return type;
        },
 
        /**
         * Return an alias to a type with a different name.
         */
        withName: function withName(name) {
          return Object.create(this, {name: {value: name}});
-       }
+       },
+
+       /**
+        * Cast a C value to |this| type.
+        *
+        * Throw an error if the value cannot be casted.
+        */
+       cast: function cast(value) {
+         return ctypes.cast(value, this.implementation);
+        }
      };
 
+
+     /**
+      * A |Type| of pointers.
+      *
+      * @param {string} name The name of this type.
+      * @param {CType} implementation The type of this pointer.
+      */
+     function PtrType(name, implementation) {
+       Type.call(this, name, implementation);
+     }
+     PtrType.prototype = Object.create(Type.prototype);
+
      exports.OS.Shared.Type = Type;
      let Types = Type;
 
      /*
       * Some values are large integers on 64 bit platforms. Unfortunately,
       * in practice, 64 bit integers cannot be manipulated in JS. We
       * therefore project them to regular numbers whenever possible.
       */
@@ -192,23 +212,29 @@
        }
        if (!("value" in x)) { // Sanity check
          throw new TypeError("Number " + x.toSource() + " has no field |value|");
        }
        return x.value;
      };
 
      function projector(type, signed) {
+       LOG("Determining best projection for", type,
+             "(size: ", type.size, ")", signed?"signed":"unsigned");
+       if (type instanceof Type) {
+         type = type.implementation;
+       }
        if (!type.size) {
          throw new TypeError("Argument is not a proper C type");
        }
-       LOG("Determining best projection for", type,
-             "(size: ", type.size, ")", signed?"signed":"unsigned");
        // Determine if type is projected to Int64/Uint64
        if (type.size == 8           // Usual case
+           // The following cases have special treatment in js-ctypes
+           // Regardless of their size, the value getter returns
+           // a Int64/Uint64
            || type == ctypes.size_t // Special cases
            || type == ctypes.ssize_t
            || type == ctypes.intptr_t
            || type == ctypes.uintptr_t
            || type == ctypes.off_t){
           if (signed) {
             LOG("Projected as a large signed integer");
             return projectLargeInt;
@@ -272,180 +298,176 @@
      Types.void_t =
        new Type("void",
                 ctypes.void_t);
 
      /**
       * Shortcut for |void*|.
       */
      Types.voidptr_t =
-       new Type("void*",
+       new PtrType("void*",
                 ctypes.voidptr_t);
 
      // void* is a special case as we can cast any pointer to/from it
      // so we have to shortcut |in_ptr|/|out_ptr|/|inout_ptr| and
      // ensure that js-ctypes' casting mechanism is invoked directly
      ["in_ptr", "out_ptr", "inout_ptr"].forEach(function(key) {
        Object.defineProperty(Types.void_t, key,
        {
-         get: function() {
-           return Types.voidptr_t;
-         }
+         value: Types.voidptr_t
        });
      });
 
      /**
+      * A Type of integers.
+      *
+      * @param {string} name The name of this type.
+      * @param {CType} implementation The underlying js-ctypes implementation.
+      * @param {bool} signed |true| if this is a type of signed integers,
+      * |false| otherwise.
+      *
+      * @constructor
+      */
+     function IntType(name, implementation, signed) {
+       Type.call(this, name, implementation);
+       this.importFromC = projector(implementation, signed);
+     };
+     IntType.prototype = Object.create(Type.prototype);
+
+     /**
       * A C char (one byte)
       */
      Types.char =
        new Type("char",
                 ctypes.char);
 
      /**
       * A C wide char (two bytes)
       */
      Types.jschar =
        new Type("jschar",
                 ctypes.jschar);
 
      /**
-      * A C integer
-      *
-      * Size depends on the platform.
-      */
-     Types.int =
-       new Type("int",
-                ctypes.int,
-                projector(ctypes.int, true));
-
-     Types.unsigned_int =
-       new Type("unsigned int",
-                ctypes.unsigned_int,
-                projector(ctypes.unsigned_int, false));
-
-     /**
       * A C integer (8-bits).
       */
      Types.int8_t =
-       new Type("int8_t",
-                ctypes.int8_t,
-                projectValue);
+       new IntType("int8_t", ctypes.int8_t, true);
 
      Types.uint8_t =
-       new Type("uint8_t",
-                ctypes.uint8_t,
-                projectValue);
+       new IntType("uint8_t", ctypes.uint8_t, false);
 
      /**
       * A C integer (16-bits).
       *
       * Also known as WORD under Windows.
       */
      Types.int16_t =
-       new Type("int16_t",
-                ctypes.int16_t,
-                projectValue);
+       new IntType("int16_t", ctypes.int16_t, true);
 
      Types.uint16_t =
-       new Type("uint16_t",
-                ctypes.uint16_t,
-                projectValue);
+       new IntType("uint16_t", ctypes.uint16_t, false);
 
      /**
       * A C integer (32-bits).
       *
       * Also known as DWORD under Windows.
       */
      Types.int32_t =
-       new Type("int32_t",
-                ctypes.int32_t,
-                projectValue);
+       new IntType("int32_t", ctypes.int32_t, true);
 
      Types.uint32_t =
-       new Type("uint32_t",
-                ctypes.uint32_t,
-                projectValue);
+       new IntType("uint32_t", ctypes.uint32_t, false);
 
      /**
       * A C integer (64-bits).
       */
      Types.int64_t =
-       new Type("int64_t",
-                ctypes.int64_t,
-                projectLargeInt);
+       new IntType("int64_t", ctypes.int64_t, true);
 
      Types.uint64_t =
-       new Type("uint64_t",
-                ctypes.uint64_t,
-                projectLargeUInt);
+       new IntType("uint64_t", ctypes.uint64_t, false);
+
+      /**
+      * A C integer
+      *
+      * Size depends on the platform.
+      */
+     Types.int = Types.intn_t(ctypes.int.size).
+       withName("int");
+
+     Types.unsigned_int = Types.intn_t(ctypes.unsigned_int.size).
+       withName("unsigned int");
 
      /**
-      * A C integer.
+      * A C long integer.
+      *
       * Size depends on the platform.
       */
      Types.long =
-       new Type("long",
-                ctypes.long,
-                projector(ctypes.long, true));
+       Types.intn_t(ctypes.long.size).withName("long");
+
+     Types.unsigned_long =
+       Types.intn_t(ctypes.unsigned_long.size).withName("unsigned long");
+
+     /**
+      * An unsigned integer with the same size as a pointer.
+      *
+      * Used to cast a pointer to an integer, whenever necessary.
+      */
+     Types.uintptr_t =
+       Types.uintn_t(ctypes.uintptr_t.size).withName("uintptr_t");
 
      /**
       * A boolean.
       * Implemented as a C integer.
       */
-     Types.bool =
-       new Type("bool",
-                ctypes.int,
-                function projectBool(x) {
-                  return !!(x.value);
-                });
+     Types.bool = Types.int.withName("bool");
+     Types.bool.importFromC = function projectBool(x) {
+       return !!(x.value);
+     };
 
      /**
       * A user identifier.
+      *
       * Implemented as a C integer.
       */
      Types.uid_t =
-       new Type("uid_t",
-                ctypes.int,
-                projector(ctypes.int, true));
+       Types.int.withName("uid_t");
 
      /**
       * A group identifier.
+      *
       * Implemented as a C integer.
       */
      Types.gid_t =
-       new Type("gid_t",
-                ctypes.int,
-                projector(ctypes.int, true));
+       Types.int.withName("gid_t");
 
      /**
       * An offset (positive or negative).
+      *
       * Implemented as a C integer.
       */
      Types.off_t =
-       new Type("off_t",
-                ctypes.off_t,
-                projector(ctypes.off_t, true));
+       new IntType("off_t", ctypes.off_t, true);
 
      /**
       * A size (positive).
+      *
       * Implemented as a C size_t.
       */
      Types.size_t =
-       new Type("size_t",
-                ctypes.size_t,
-                projector(ctypes.size_t, false));
+       new IntType("size_t", ctypes.size_t, false);
 
      /**
       * An offset (positive or negative).
       * Implemented as a C integer.
       */
      Types.ssize_t =
-       new Type("ssize_t",
-                ctypes.ssize_t,
-                projector(ctypes.ssize_t, true));
+       new IntType("ssize_t", ctypes.ssize_t, true);
 
 
      /**
       * Utility class, used to build a |struct| type
       * from a set of field names, types and offsets.
       *
       * @param {string} name The name of the |struct| type.
       * @param {number} size The total size of the |struct| type in bytes.
@@ -607,16 +629,19 @@
          throw new TypeError("declareFFI expects as first argument a string");
        }
        abi = abi || ctypes.default_abi;
        if (Object.prototype.toString.call(abi) != "[object CABI]") {
          // Note: This is the only known manner of checking whether an object
          // is an abi.
          throw new TypeError("declareFFI expects as second argument an abi or null");
        }
+       if (!returnType.importFromC) {
+         throw new TypeError("declareFFI expects as third argument an instance of Type");
+       }
        let signature = [symbol, abi];
        let argtypes  = [];
        for (let i = 3; i < arguments.length; ++i) {
          let current = arguments[i];
          if (!current) {
            throw new TypeError("Missing type for argument " + ( i - 3 ) +
                                " of symbol " + symbol);
          }
@@ -626,17 +651,17 @@
                                + " ( " + current.name + " )" );
          }
          signature.push(current.implementation);
        }
        try {
          let fun = lib.declare.apply(lib, signature);
          let result = function ffi(/*arguments*/) {
            let result = fun.apply(fun, arguments);
-           return returnType.convert_from_c(result, symbol);
+           return returnType.importFromC(result, symbol);
          };
          if (exports.OS.Shared.DEBUG) {
            result.fun = fun; // Also return the raw FFI function.
          }
          LOG("Function", symbol, "declared");
          return result;
        } catch (x) {
          // Note: Not being able to declare a function is normal.
--- a/toolkit/components/osfile/osfile_unix_back.jsm
+++ b/toolkit/components/osfile/osfile_unix_back.jsm
@@ -54,94 +54,86 @@
 
        // Initialize types that require additional OS-specific
        // support - either finalization or matching against
        // OS-specific constants.
 
        /**
         * A file descriptor.
         */
-       Types.fd =
-         new Type("fd",
-                  ctypes.int,
-                  function(fd_int) {
-                    return ctypes.CDataFinalizer(fd_int, _close);
-                  });
+       Types.fd = Type.int.withName("fd");
+       Types.fd.importFromC = function importFromC(fd_int) {
+         return ctypes.CDataFinalizer(fd_int, _close);
+       };
+
 
        /**
         * A C integer holding -1 in case of error or a file descriptor
         * in case of success.
         */
-       Types.negativeone_or_fd =
-         new Type("negativeone_or_fd",
-                  ctypes.int,
-                  function(fd_int, operation) {
-                    if (fd_int == -1) {
-                      return -1;
-                    }
-                    return ctypes.CDataFinalizer(fd_int, _close);
-                  });
+       Types.negativeone_or_fd = Types.fd.withName("negativeone_or_fd");
+       Types.negativeone_or_fd.importFromC =
+         function importFromC(fd_int) {
+           if (fd_int == -1) {
+             return -1;
+           }
+           return ctypes.CDataFinalizer(fd_int, _close);
+         };
 
        /**
         * A C integer holding -1 in case of error or a meaningless value
         * in case of success.
         */
        Types.negativeone_or_nothing =
-         new Type("negativeone_or_nothing",
-                  ctypes.int);
+         Types.int.withName("negativeone_or_nothing");
 
        /**
         * A C integer holding -1 in case of error or a positive integer
         * in case of success.
         */
        Types.negativeone_or_ssize_t =
-         new Type("negativeone_or_ssize_t",
-                  ctypes.ssize_t,
-                  Type.ssize_t.convert_from_c);
+         Types.ssize_t.withName("negativeone_or_ssize_t");
 
        /**
         * A C string
         */
        Types.null_or_string =
-         new Type("null_or_string",
-                  ctypes.char.ptr);
+         Types.char.in_ptr.withName("null_or_string");
 
        Types.string =
-         new Type("string",
-                  ctypes.char.ptr);
-
-       // Note: support for strings in js-ctypes is very limited.
-       // Once bug 552551 has progressed, we should extend this
-       // type using ctypes.readString/ctypes.writeString
+         Types.char.in_ptr.withName("string");
 
        /**
-        * Type |mode_t|
+        * Various libc integer types
         */
        Types.mode_t =
          Types.intn_t(OS.Constants.libc.OSFILE_SIZEOF_MODE_T).withName("mode_t");
+       Types.uid_t =
+         Types.intn_t(OS.Constants.libc.OSFILE_SIZEOF_UID_T).withName("uid_t");
+       Types.gid_t =
+         Types.intn_t(OS.Constants.libc.OSFILE_SIZEOF_GID_T).withName("gid_t");
 
        /**
         * Type |time_t|
         */
        Types.time_t =
          Types.intn_t(OS.Constants.libc.OSFILE_SIZEOF_TIME_T).withName("time_t");
 
        Types.DIR =
          new Type("DIR",
                   ctypes.StructType("DIR"));
 
        Types.null_or_DIR_ptr =
-         new Type("null_or_DIR*",
-                  Types.DIR.out_ptr.implementation,
-                  function(dir, operation) {
-                    if (dir == null || dir.isNull()) {
-                      return null;
-                    }
-                    return ctypes.CDataFinalizer(dir, _close_dir);
-                  });
+         Types.DIR.out_ptr.withName("null_or_DIR*");
+       Types.null_or_DIR_ptr.importFromC = function importFromC(dir) {
+         if (dir == null || dir.isNull()) {
+           return null;
+         }
+         return ctypes.CDataFinalizer(dir, _close_dir);
+       };
 
        // Structure |dirent|
        // Building this type is rather complicated, as its layout varies between
        // variants of Unix. For this reason, we rely on a number of constants
        // (computed in C from the C data structures) that give us the layout.
        // The structure we compute looks like
        //  { int8_t[...] before_d_type; // ignored content
        //    int8_t      d_type       ;
@@ -153,33 +145,32 @@
            OS.Constants.libc.OSFILE_SIZEOF_DIRENT);
          dirent.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_DIRENT_D_TYPE,
            "d_type", ctypes.uint8_t);
          dirent.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_DIRENT_D_NAME,
            "d_name", ctypes.ArrayType(ctypes.char, OS.Constants.libc.OSFILE_SIZEOF_DIRENT_D_NAME));
 
          // We now have built |dirent|.
          Types.dirent = dirent.getType();
-         LOG("dirent is: " + Types.dirent.implementation.toSource());
        }
        Types.null_or_dirent_ptr =
          new Type("null_of_dirent",
                   Types.dirent.out_ptr.implementation);
 
        // Structure |stat|
        // Same technique
        {
          let stat = new OS.Shared.HollowStructure("stat",
            OS.Constants.libc.OSFILE_SIZEOF_STAT);
          stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_MODE,
                         "st_mode", Types.mode_t.implementation);
          stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_UID,
-                          "st_uid", ctypes.int);
+                          "st_uid", Types.uid_t.implementation);
          stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_GID,
-                          "st_gid", ctypes.int);
+                          "st_gid", Types.gid_t.implementation);
 
          // Here, things get complicated with different data structures.
          // Some platforms have |time_t st_atime| and some platforms have
          // |timespec st_atimespec|. However, since |timespec| starts with
          // a |time_t|, followed by nanoseconds, we just cheat and pretend
          // that everybody has |time_t st_atime|, possibly followed by padding
          stat.add_field_at(OS.Constants.libc.OSFILE_OFFSETOF_STAT_ST_ATIME,
                           "st_atime", Types.time_t.implementation);
@@ -537,19 +528,20 @@
                       /*buf*/    Types.stat.out_ptr
                      );
        }
 
        // We cannot make a C array of CDataFinalizer, so
        // pipe cannot be directly defined as a C function.
 
        let _pipe =
-         libc.declare("pipe", ctypes.default_abi,
-                    /*return*/ ctypes.int,
-                    /*fds*/    ctypes.ArrayType(ctypes.int, 2));
+         declareFFI("pipe", ctypes.default_abi,
+           /*return*/ Types.negativeone_or_nothing,
+           /*fds*/    new Type("two file descriptors",
+             ctypes.ArrayType(ctypes.int, 2)));
 
        // A shared per-thread buffer used to communicate with |pipe|
        let _pipebuf = new (ctypes.ArrayType(ctypes.int, 2))();
 
        UnixFile.pipe = function pipe(array) {
          let result = _pipe(_pipebuf);
          if (result == -1) {
            return result;
--- a/toolkit/components/osfile/osfile_win_back.jsm
+++ b/toolkit/components/osfile/osfile_win_back.jsm
@@ -68,80 +68,66 @@
          exports.OS.Types = {};
        }
        let Type = exports.OS.Shared.Type;
        let Types = Type;
 
        // Initialize types
 
        Types.HANDLE =
-         new Type("HANDLE",
-                  ctypes.voidptr_t);
+         Types.voidptr_t.withName("HANDLE");
 
        /**
         * A C integer holding INVALID_HANDLE_VALUE in case of error or
         * a file descriptor in case of success.
         */
        Types.maybe_HANDLE =
-         new Type("maybe_HANDLE",
-           Types.HANDLE.implementation,
-           function (maybe) {
-             if (ctypes.cast(maybe, ctypes.int).value == invalid_handle) {
-               // Ensure that API clients can effectively compare against
-               // Const.INVALID_HANDLE_VALUE. Without this cast,
-               // == would always return |false|.
-               return invalid_handle;
-             }
-             return ctypes.CDataFinalizer(maybe, _CloseHandle);
-           });
+         Types.HANDLE.withName("maybe_HANDLE");
+       Types.maybe_HANDLE.importFromC =
+         function maybe_HANDLE_importFromC(maybe) {
+           if (Types.int.cast(maybe).value == INVALID_HANDLE) {
+             // Ensure that API clients can effectively compare against
+             // Const.INVALID_HANDLE_VALUE. Without this cast,
+             // == would always return |false|.
+             return INVALID_HANDLE;
+           }
+         return ctypes.CDataFinalizer(maybe, _CloseHandle);
+         };
 
        /**
         * A C integer holding INVALID_HANDLE_VALUE in case of error or
         * a file descriptor in case of success.
         */
        Types.maybe_find_HANDLE =
-         new Type("maybe_find_HANDLE",
-           Types.HANDLE.implementation,
-           function (maybe) {
-             if (ctypes.cast(maybe, ctypes.int).value == invalid_handle) {
-               // Ensure that API clients can effectively compare against
-               // Const.INVALID_HANDLE_VALUE. Without this cast,
-               // == would always return |false|.
-               return invalid_handle;
-             }
-             return ctypes.CDataFinalizer(maybe, _FindClose);
-           });
+         Types.maybe_HANDLE.withName("maybe_find_HANDLE");
 
-       let invalid_handle = exports.OS.Constants.Win.INVALID_HANDLE_VALUE;
+       let INVALID_HANDLE = exports.OS.Constants.Win.INVALID_HANDLE_VALUE;
 
-       Types.DWORD = Types.int32_t;
+       Types.DWORD = Types.int32_t.withName("DWORD");
 
        /**
         * A C integer holding -1 in case of error or a positive integer
         * in case of success.
         */
        Types.negative_or_DWORD =
-         new Type("negative_or_DWORD",
-                  ctypes.int32_t);
+         Types.DWORD.withName("negative_or_DWORD");
 
        /**
         * A C integer holding 0 in case of error or a positive integer
         * in case of success.
         */
        Types.zero_or_DWORD =
-         new Type("zero_or_DWORD",
-                  ctypes.int32_t);
+         Types.DWORD.withName("zero_or_DWORD");
 
        /**
         * A C integer holding 0 in case of error, any other value in
         * case of success.
         */
        Types.zero_or_nothing =
-         new Type("zero_or_nothing",
-                  Types.bool.implementation);
+         Types.int.withName("zero_or_nothing");
 
        Types.FILETIME =
          new Type("FILETIME",
                   ctypes.StructType("FILETIME", [
                   { lo: Types.DWORD.implementation },
                   { hi: Types.DWORD.implementation }]));
 
        Types.FindData =
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -208,19 +208,21 @@ void
 nsAppShell::NotifyNativeEvent()
 {
     MutexAutoLock lock(mCondLock);
     mQueueCond.Notify();
 }
 
 #define PREFNAME_MATCH_OS  "intl.locale.matchOS"
 #define PREFNAME_UA_LOCALE "general.useragent.locale"
+#define PREFNAME_COALESCE_TOUCHES "dom.event.touch.coalescing.enabled"
 static const char* kObservedPrefs[] = {
   PREFNAME_MATCH_OS,
   PREFNAME_UA_LOCALE,
+  PREFNAME_COALESCE_TOUCHES,
   nullptr
 };
 
 nsresult
 nsAppShell::Init()
 {
 #ifdef PR_LOGGING
     if (!gWidgetLog)
@@ -257,16 +259,17 @@ nsAppShell::Init()
 
     nsAutoString locale;
     rv = Preferences::GetLocalizedString(PREFNAME_UA_LOCALE, &locale);
     if (NS_FAILED(rv)) {
         rv = Preferences::GetString(PREFNAME_UA_LOCALE, &locale);
     }
 
     bridge->SetSelectedLocale(locale);
+    mAllowCoalescingTouches = Preferences::GetBool(PREFNAME_COALESCE_TOUCHES, true);
     return rv;
 }
 
 NS_IMETHODIMP
 nsAppShell::Observe(nsISupports* aSubject,
                     const char* aTopic,
                     const PRUnichar* aData)
 {
@@ -274,16 +277,18 @@ nsAppShell::Observe(nsISupports* aSubjec
         // We need to ensure no observers stick around after XPCOM shuts down
         // or we'll see crashes, as the app shell outlives XPConnect.
         mObserversHash.Clear();
         return nsBaseAppShell::Observe(aSubject, aTopic, aData);
     } else if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) && aData && (
                    nsDependentString(aData).Equals(
                        NS_LITERAL_STRING(PREFNAME_UA_LOCALE)) ||
                    nsDependentString(aData).Equals(
+                       NS_LITERAL_STRING(PREFNAME_COALESCE_TOUCHES)) ||
+                   nsDependentString(aData).Equals(
                        NS_LITERAL_STRING(PREFNAME_MATCH_OS)))) {
         AndroidBridge* bridge = AndroidBridge::Bridge();
         if (!bridge) {
             return NS_OK;
         }
 
         bool match;
         nsresult rv = Preferences::GetBool(PREFNAME_MATCH_OS, &match);
@@ -296,16 +301,18 @@ nsAppShell::Observe(nsISupports* aSubjec
 
         nsAutoString locale;
         if (NS_FAILED(Preferences::GetLocalizedString(PREFNAME_UA_LOCALE,
                                                       &locale))) {
             locale = Preferences::GetString(PREFNAME_UA_LOCALE);
         }
 
         bridge->SetSelectedLocale(locale);
+
+        mAllowCoalescingTouches = Preferences::GetBool(PREFNAME_COALESCE_TOUCHES, true);
         return NS_OK;
     }
     return NS_OK;
 }
 
 void
 nsAppShell::ScheduleNativeEventCallback()
 {
@@ -756,17 +763,17 @@ nsAppShell::PostEvent(AndroidGeckoEvent 
             // event as soon as possible after a viewport change
             mAllowCoalescingNextDraw = false;
             allowCoalescingNextViewport = true;
 
             mEventQueue.AppendElement(ae);
             break;
 
         case AndroidGeckoEvent::MOTION_EVENT:
-            if (ae->Action() == AndroidMotionEvent::ACTION_MOVE) {
+            if (ae->Action() == AndroidMotionEvent::ACTION_MOVE && mAllowCoalescingTouches) {
                 int len = mEventQueue.Length();
                 if (len > 0) {
                     AndroidGeckoEvent* event = mEventQueue[len - 1];
                     if (event->Type() == AndroidGeckoEvent::MOTION_EVENT && event->Action() == AndroidMotionEvent::ACTION_MOVE) {
                         // consecutive motion-move events; drop the last one before adding the new one
                         EVLOG("nsAppShell: Dropping old move event at %p in favour of new move event %p", event, ae);
                         mEventQueue.RemoveElementAt(len - 1);
                         delete event;
--- a/widget/android/nsAppShell.h
+++ b/widget/android/nsAppShell.h
@@ -65,16 +65,17 @@ protected:
     virtual ~nsAppShell();
 
     Mutex mQueueLock;
     Mutex mCondLock;
     CondVar mQueueCond;
     mozilla::AndroidGeckoEvent *mQueuedDrawEvent;
     mozilla::AndroidGeckoEvent *mQueuedViewportEvent;
     bool mAllowCoalescingNextDraw;
+    bool mAllowCoalescingTouches;
     nsTArray<mozilla::AndroidGeckoEvent *> mEventQueue;
     nsInterfaceHashtable<nsStringHashKey, nsIObserver> mObserversHash;
 
     mozilla::AndroidGeckoEvent *PopNextEvent();
     mozilla::AndroidGeckoEvent *PeekNextEvent();
 
     nsCOMPtr<nsIAndroidBrowserApp> mBrowserApp;
 };