Merge m-c to b2g-inbound
authorWes Kocher <wkocher@mozilla.com>
Tue, 07 Jan 2014 18:29:03 -0800
changeset 162501 dcd6b97d8132d9d37cda477d93fa7dc8c72e8648
parent 162500 0f44f829b4a4fcd0f52f5afd1fdcd3cd8bd87255 (current diff)
parent 162429 5c548fcd09f9bc1a7defed445a188eff77586e46 (diff)
child 162502 04f703c27b935014638c7e388b2d86373051c40d
push id25955
push usercbook@mozilla.com
push dateWed, 08 Jan 2014 12:17:49 +0000
treeherdermozilla-central@d852bcf897ca [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b2g-inbound
b2g/config/gaia.json
js/xpconnect/tests/mochitest/file_asmjs.js
js/xpconnect/tests/mochitest/test_asmjs.html
js/xpconnect/tests/mochitest/test_asmjs2.html
js/xpconnect/tests/mochitest/test_asmjs3.html
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "53a930e0a23a6d2b0b53baea9a609359683758ab", 
+    "revision": "9960daf91c384990c4bbe7c1221905db7536596b", 
     "repo_path": "/integration/gaia-central"
 }
--- a/browser/devtools/sourceeditor/editor.js
+++ b/browser/devtools/sourceeditor/editor.js
@@ -72,17 +72,19 @@ const CM_MAPPING = [
   "getCursor",
   "setSelection",
   "getSelection",
   "replaceSelection",
   "undo",
   "redo",
   "clearHistory",
   "openDialog",
-  "refresh"
+  "refresh",
+  "getOption",
+  "setOption"
 ];
 
 const { cssProperties, cssValues, cssColors } = getCSSKeywords();
 
 const editors = new WeakMap();
 
 Editor.modes = {
   text: { name: "text" },
@@ -283,27 +285,25 @@ Editor.prototype = {
     return def.promise;
   },
 
   /**
    * Returns the currently active highlighting mode.
    * See Editor.modes for the list of all suppoert modes.
    */
   getMode: function () {
-    let cm = editors.get(this);
-    return cm.getOption("mode");
+    return this.getOption("mode");
   },
 
   /**
    * Changes the value of a currently used highlighting mode.
    * See Editor.modes for the list of all suppoert modes.
    */
   setMode: function (value) {
-    let cm = editors.get(this);
-    cm.setOption("mode", value);
+    this.setOption("mode", value);
   },
 
   /**
    * Returns text from the text area. If line argument is provided
    * the method returns only that line.
    */
   getText: function (line) {
     let cm = editors.get(this);
@@ -644,18 +644,17 @@ Editor.prototype = {
     let cm = editors.get(this);
     return cm.isClean(this.version);
   },
 
   /**
    * True if the editor is in the read-only mode, false otherwise.
    */
   isReadOnly: function () {
-    let cm = editors.get(this);
-    return cm.getOption("readOnly");
+    return this.getOption("readOnly");
   },
 
   /**
    * Displays a context menu at the point x:y. The first
    * argument, container, should be a DOM node that contains
    * a context menu element specified by the ID from
    * config.contextMenu.
    */
--- a/browser/devtools/sourceeditor/test/browser_editor_basic.js
+++ b/browser/devtools/sourceeditor/test/browser_editor_basic.js
@@ -6,16 +6,21 @@
 
 function test() {
   waitForExplicitFinish();
   setup((ed, win) => {
     // appendTo
     let src = win.document.querySelector("iframe").getAttribute("src");
     ok(~src.indexOf(".CodeMirror"), "correct iframe is there");
 
+    // getOption/setOption
+    ok(ed.getOption("styleActiveLine"), "getOption works");
+    ed.setOption("styleActiveLine", false);
+    ok(!ed.getOption("styleActiveLine"), "setOption works");
+
     // Language modes
     is(ed.getMode(), Editor.modes.text, "getMode");
     ed.setMode(Editor.modes.js);
     is(ed.getMode(), Editor.modes.js, "setMode");
 
     // Content
     is(ed.getText(), "Hello.", "getText");
     ed.setText("Hi.\nHow are you?");
index 42ddd55ea7c5e8ca2e50792c46a2875393683753..5730a5c1748b80d832d5545848d5e9683d7e23f8
GIT binary patch
literal 1369
zc$@)Q1*ZCmP)<h;3K|Lk000e1NJLTq004jh001Be1^@s6+9Gfz000FbNkl<ZcmeHO
zUuYaf7@yg@y}RA}*ZL2d);}*o`=Ulc$+b|QEV1-K39+S-g5aMZA}G{WFck7osWc)g
zR-_awra>cW6Gh024^2~BXf1+KA6m7!9>JP&$?fj#?(L4>h%J}9*<&s@TV0%i$?bjf
z&F}Zk_kBA%vzbFk7U==$0qFth0qFthf&ZfiaQ&bwrhNbe8|r1Eud$-c>M3<M3%6o9
z$3T8Rt4*NnTt-W5XW^NabAlkWk<}(pb~3Fcm>fOh#S_58Y!k^p)3^3Ihf)t5cJL_|
z@%>f_7IyHd3ElJ$tK5L#Q`!QH`ybi$z_ifmv}qatvIOW9S@w^f2V??7FJ?k5mI)x#
zozVl62@t)Q3AI=zfKYcv4@@RN^kOE|VwnI!-5EVFnE=s?nNW*m0tj_y^uS~SL@#DS
zEtUx&)Sb}-lL-*Lm<hF5CV)_PMh`5`GC3YYyOYV}EURs$vZW<^gAzjrc=}@>?MSB+
zXL)pabqvSpCLS8&(f0`6l+)E|9$j7?({u|26E4j8sbhn`jgAy~v3c}v-~Qs-HCqPW
zYt&`V05tgUzxiE;X-^nccbo>Vv17a6vswOxqQN(-?#p1q&u~WJJPlsEe2x0)MB-ed
zEa*E8zG=Dp4a*@=4H~>4U0%7at0#GNY4GnoIJxz~BWI379izeH?Hi<bY3wa#LNR9m
z3X)E#M=PeAR(!t~$Iw2@vi4}2b}@)Rv|LNZN6e~|B^d8OKKfwMwg+;_<WHh`g7CCd
z{MD-M=6!+&i9)`vRGiJ3OyYcyzDT);mFcrjcTW~RD*-(jT(jlL*Kh0Hx%u?t>sHR6
zzhgDaM&=Aa6*0ynqpOGD1kbl{P(&vvHf$J17Yif|8Rh?K;>%$~&;%hU8}soI%OZD%
z!NtO){Hdh+0*w7*uptjO!Bn|gy_=;gT(&s3fU;fdzk6=uH=bpD4##NnF#f5B#xJd$
zzx?}EmYsnKK$XD>{(LF^E_^N$Lh=NwFR?(vlJ)oZdudI54T5kLPV3y{6X%VvxLCL_
zZcj_8&qCW3sJF(6a+IYPimXza^(^<|5Bml_f&}=J^2d;P{=i279TWr|TXyYv;*%UW
zql`)I%}@p#01EtyVY$Nyqt%cE3kps!&k_c7?^sOvt*ZMz0Nn!h4-fmhItFbA;L(vE
z?|JNjA-EN923xM!#S1hUg_f2H0Be9j1(S#2?lWEN53SO4=0ZZKMRj1X1{i{AXdA|8
z$#=<9=`8-Wt~vBeSOdHRW8Vo0SSB^}NG_B5C3Mm{g2EG<-i9?mA0$Az_{E`DO2-f9
z>Y9aI20jAlE&zhxixkCHVG+10mAW8=krRaO0tkWyAR$%cp=a9JMQ~EIRHM59x&Q<v
z)AR9GSO9+CvV-8pp7G23=>m}Q&rgl+DNP=ptLYy1PKMVU<_ti$eQ<R8Lja*wNP$)S
zdI%&}q1!$^p&kTfT7{H~<J`x~Pq%%#rtSq|T7?wA?16xCVfB1kEX?$E?M3NzqcO$5
z3)Z7<EpDGe^9u$S07I$p$sF)E7iI#`7dG$?pjo>U31{#r1$|)yGf*dN&3)V9hZi=m
z|8K_VK*AgRzbP=9hTjNuz}DQA6@03->=8hJw9(vEtOP5l)m~os6xU=uA+4rz?w2)9
zxcq{dAXVadr-Rx=$%$7xQ;!9UYgzs)9*AYTw%rI@yrs(%*LarA0M~@_^WX6R-Rg$T
b+{p14hOE4M$#V;A00000NkvXXu0mjfW5<rx
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -8816,16 +8816,20 @@ nsDocument::SetScrollToRef(nsIURI *aDocu
     mScrollToRef = Substring(start, end);
   }
 }
 
 void
 nsDocument::ScrollToRef()
 {
   if (mScrolledToRefAlready) {
+    nsCOMPtr<nsIPresShell> shell = GetShell();
+    if (shell) {
+      shell->ScrollToAnchor();
+    }
     return;
   }
 
   if (mScrollToRef.IsEmpty()) {
     return;
   }
 
   char* tmpstr = ToNewCString(mScrollToRef);
--- a/dom/asmjscache/AsmJSCache.cpp
+++ b/dom/asmjscache/AsmJSCache.cpp
@@ -5,61 +5,144 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AsmJSCache.h"
 
 #include <stdio.h>
 
 #include "js/RootingAPI.h"
 #include "jsfriendapi.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/CondVar.h"
 #include "mozilla/dom/asmjscache/PAsmJSCacheEntryChild.h"
 #include "mozilla/dom/asmjscache/PAsmJSCacheEntryParent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/quota/Client.h"
 #include "mozilla/dom/quota/OriginOrPatternString.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/quota/QuotaObject.h"
 #include "mozilla/dom/quota/UsageInfo.h"
+#include "mozilla/HashFunctions.h"
 #include "mozilla/unused.h"
 #include "nsIAtom.h"
 #include "nsIFile.h"
 #include "nsIPrincipal.h"
 #include "nsIRunnable.h"
+#include "nsISimpleEnumerator.h"
 #include "nsIThread.h"
 #include "nsIXULAppInfo.h"
 #include "nsJSPrincipals.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "prio.h"
 #include "private/pprio.h"
 
-#define ASMJSCACHE_FILE_NAME "module"
+#define ASMJSCACHE_METADATA_FILE_NAME "metadata"
+#define ASMJSCACHE_ENTRY_FILE_NAME_BASE "module"
 
 using mozilla::dom::quota::AssertIsOnIOThread;
 using mozilla::dom::quota::OriginOrPatternString;
 using mozilla::dom::quota::PersistenceType;
 using mozilla::dom::quota::QuotaManager;
 using mozilla::dom::quota::QuotaObject;
 using mozilla::dom::quota::UsageInfo;
 using mozilla::unused;
+using mozilla::HashString;
 
 namespace mozilla {
+
+MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, PR_Close);
+
 namespace dom {
 namespace asmjscache {
 
 namespace {
 
 bool
 IsMainProcess()
 {
   return XRE_GetProcessType() == GeckoProcessType_Default;
 }
 
+// Anything smaller should compile fast enough that caching will just add
+// overhead.
+static const size_t sMinCachedModuleLength = 10000;
+
+// The number of characters to hash into the Metadata::Entry::mFastHash.
+static const unsigned sNumFastHashChars = 4096;
+
+nsresult
+WriteMetadataFile(nsIFile* aMetadataFile, const Metadata& aMetadata)
+{
+  int32_t openFlags = PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE;
+
+  JS::BuildIdCharVector buildId;
+  bool ok = GetBuildId(&buildId);
+  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
+
+  ScopedPRFileDesc fd;
+  nsresult rv = aMetadataFile->OpenNSPRFileDesc(openFlags, 0644, &fd.rwget());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uint32_t length = buildId.length();
+  int32_t bytesWritten = PR_Write(fd, &length, sizeof(length));
+  NS_ENSURE_TRUE(bytesWritten == sizeof(length), NS_ERROR_UNEXPECTED);
+
+  bytesWritten = PR_Write(fd, buildId.begin(), length);
+  NS_ENSURE_TRUE(bytesWritten == int32_t(length), NS_ERROR_UNEXPECTED);
+
+  bytesWritten = PR_Write(fd, &aMetadata, sizeof(aMetadata));
+  NS_ENSURE_TRUE(bytesWritten == sizeof(aMetadata), NS_ERROR_UNEXPECTED);
+
+  return NS_OK;
+}
+
+nsresult
+ReadMetadataFile(nsIFile* aMetadataFile, Metadata& aMetadata)
+{
+  int32_t openFlags = PR_RDONLY;
+
+  ScopedPRFileDesc fd;
+  nsresult rv = aMetadataFile->OpenNSPRFileDesc(openFlags, 0644, &fd.rwget());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Read the buildid and check that it matches the current buildid
+
+  JS::BuildIdCharVector currentBuildId;
+  bool ok = GetBuildId(&currentBuildId);
+  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
+
+  uint32_t length;
+  int32_t bytesRead = PR_Read(fd, &length, sizeof(length));
+  NS_ENSURE_TRUE(bytesRead == sizeof(length), NS_ERROR_UNEXPECTED);
+
+  NS_ENSURE_TRUE(currentBuildId.length() == length, NS_ERROR_UNEXPECTED);
+
+  JS::BuildIdCharVector fileBuildId;
+  ok = fileBuildId.resize(length);
+  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
+
+  bytesRead = PR_Read(fd, fileBuildId.begin(), length);
+  NS_ENSURE_TRUE(bytesRead == int32_t(length), NS_ERROR_UNEXPECTED);
+
+  for (uint32_t i = 0; i < length; i++) {
+    if (currentBuildId[i] != fileBuildId[i]) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  // Read the Metadata struct
+
+  bytesRead = PR_Read(fd, &aMetadata, sizeof(aMetadata));
+  NS_ENSURE_TRUE(bytesRead == sizeof(aMetadata), NS_ERROR_UNEXPECTED);
+
+  return NS_OK;
+}
+
 // FileDescriptorHolder owns a file descriptor and its memory mapping.
 // FileDescriptorHolder is derived by all three runnable classes (that is,
 // (Single|Parent|Child)ProcessRunnable. To avoid awkward workarouds,
 // FileDescriptorHolder is derived virtually by File and MainProcessRunnable for
 // the benefit of SingleProcessRunnable, which derives both. Since File and
 // MainProcessRunnable both need to be runnables, FileDescriptorHolder also
 // derives nsRunnable.
 class FileDescriptorHolder : public nsRunnable
@@ -304,59 +387,78 @@ class MainProcessRunnable : public virtu
 public:
   NS_DECL_NSIRUNNABLE
 
   // MainProcessRunnable runnable assumes that the derived class ensures
   // (through ref-counting or preconditions) that aPrincipal is kept alive for
   // the lifetime of the MainProcessRunnable.
   MainProcessRunnable(nsIPrincipal* aPrincipal,
                       OpenMode aOpenMode,
-                      size_t aSizeToWrite)
+                      WriteParams aWriteParams)
   : mPrincipal(aPrincipal),
     mOpenMode(aOpenMode),
-    mSizeToWrite(aSizeToWrite),
+    mWriteParams(aWriteParams),
     mNeedAllowNextSynchronizedOp(false),
     mState(eInitial)
   {
     MOZ_ASSERT(IsMainProcess());
   }
 
   virtual ~MainProcessRunnable()
   {
     MOZ_ASSERT(mState == eFinished);
     MOZ_ASSERT(!mNeedAllowNextSynchronizedOp);
   }
 
 protected:
-  // This method is be called by the derived class (either on the JS
-  // compilation thread or the main thread) when JS engine is finished
-  // reading/writing the cache entry.
+  // This method is called by the derived class (either on the JS compilation
+  // thread or the main thread) when a cache entry has been selected to open.
+  void
+  OpenForRead(unsigned aModuleIndex)
+  {
+    MOZ_ASSERT(mState == eWaitingToOpenCacheFileForRead);
+    MOZ_ASSERT(mOpenMode == eOpenForRead);
+
+    mModuleIndex = aModuleIndex;
+    mState = eReadyToOpenCacheFileForRead;
+    DispatchToIOThread();
+  }
+
+  // This method is called by the derived class (either on the JS compilation
+  // thread or the main thread) when the JS engine is finished reading/writing
+  // the cache entry.
   void
   Close()
   {
     MOZ_ASSERT(mState == eOpened);
     mState = eClosing;
     NS_DispatchToMainThread(this);
   }
 
   // This method is called both internally and by derived classes upon any
   // failure that prevents the eventual opening of the cache entry.
   void
   Fail()
   {
-    MOZ_ASSERT(mState == eInitial || mState == eWaitingToOpen ||
-               mState == eReadyToOpen || mState == eNotifying);
+    MOZ_ASSERT(mState != eOpened &&
+               mState != eClosing &&
+               mState != eFailing &&
+               mState != eFinished);
 
     mState = eFailing;
     NS_DispatchToMainThread(this);
   }
 
+  // Called by MainProcessRunnable on the main thread after metadata is open:
+  virtual void
+  OnOpenMetadataForRead(const Metadata& aMetadata) = 0;
+
   // Called by MainProcessRunnable on the main thread after the entry is open:
   virtual void
-  OnOpen() = 0;
+  OnOpenCacheFile() = 0;
 
   // This method may be overridden, but it must be called from the overrider.
   // Called by MainProcessRunnable on the main thread after a call to Fail():
   virtual void
   OnFailure()
   {
     FinishOnMainThread();
   }
@@ -369,37 +471,71 @@ protected:
     FinishOnMainThread();
   }
 
 private:
   nsresult
   InitOnMainThread();
 
   nsresult
-  OpenFileOnIOThread();
+  ReadMetadata();
+
+  nsresult
+  OpenCacheFileForWrite();
+
+  nsresult
+  OpenCacheFileForRead();
 
   void
   FinishOnMainThread();
 
+  void
+  DispatchToIOThread()
+  {
+    // If shutdown just started, the QuotaManager may have been deleted.
+    QuotaManager* qm = QuotaManager::Get();
+    if (!qm) {
+      Fail();
+      return;
+    }
+
+    nsresult rv = qm->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
+    if (NS_FAILED(rv)) {
+      Fail();
+      return;
+    }
+  }
+
   nsIPrincipal* const mPrincipal;
   const OpenMode mOpenMode;
-  const size_t mSizeToWrite;
+  const WriteParams mWriteParams;
 
   // State initialized during eInitial:
   bool mNeedAllowNextSynchronizedOp;
   nsCString mGroup;
   nsCString mOrigin;
   nsCString mStorageId;
 
+  // State initialized during eReadyToReadMetadata
+  nsCOMPtr<nsIFile> mDirectory;
+  nsCOMPtr<nsIFile> mMetadataFile;
+  Metadata mMetadata;
+
+  // State initialized during eWaitingToOpenCacheFileForRead
+  unsigned mModuleIndex;
+
   enum State {
     eInitial, // Just created, waiting to be dispatched to main thread
-    eWaitingToOpen, // Waiting to be called back from WaitForOpenAllowed
-    eReadyToOpen, // Waiting to be dispatched to the IO thread
-    eNotifying, // Waiting to be dispatched to main thread to notify of success
-    eOpened, // Finished calling OnOpen from main thread, waiting to be closed
+    eWaitingToOpenMetadata, // Waiting to be called back from WaitForOpenAllowed
+    eReadyToReadMetadata, // Waiting to read the metadata file on the IO thread
+    eSendingMetadataForRead, // Waiting to send OnOpenMetadataForRead
+    eWaitingToOpenCacheFileForRead, // Waiting to hear back from child
+    eReadyToOpenCacheFileForRead, // Waiting to open cache file for read
+    eSendingCacheFile, // Waiting to send OnOpenCacheFile on the main thread
+    eOpened, // Finished calling OnOpen, waiting to be closed
     eClosing, // Waiting to be dispatched to main thread again
     eFailing, // Just failed, waiting to be dispatched to the main thread
     eFinished, // Terminal state
   };
   State mState;
 };
 
 nsresult
@@ -412,80 +548,188 @@ MainProcessRunnable::InitOnMainThread()
   NS_ENSURE_STATE(qm);
 
   nsresult rv = QuotaManager::GetInfoFromPrincipal(mPrincipal, &mGroup,
                                                    &mOrigin, nullptr, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   QuotaManager::GetStorageId(quota::PERSISTENCE_TYPE_TEMPORARY,
                              mOrigin, quota::Client::ASMJS,
-                             NS_LITERAL_STRING(ASMJSCACHE_FILE_NAME),
+                             NS_LITERAL_STRING("asmjs"),
                              mStorageId);
 
   return NS_OK;
 }
 
 nsresult
-MainProcessRunnable::OpenFileOnIOThread()
+MainProcessRunnable::ReadMetadata()
 {
   AssertIsOnIOThread();
-  MOZ_ASSERT(mState == eReadyToOpen);
+  MOZ_ASSERT(mState == eReadyToReadMetadata);
 
   QuotaManager* qm = QuotaManager::Get();
   MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread");
 
-  nsCOMPtr<nsIFile> path;
   nsresult rv = qm->EnsureOriginIsInitialized(quota::PERSISTENCE_TYPE_TEMPORARY,
                                               mGroup, mOrigin, true,
-                                              getter_AddRefs(path));
+                                              getter_AddRefs(mDirectory));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = path->Append(NS_LITERAL_STRING(ASMJSCACHE_DIRECTORY_NAME));
+  rv = mDirectory->Append(NS_LITERAL_STRING(ASMJSCACHE_DIRECTORY_NAME));
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool exists;
-  rv = path->Exists(&exists);
+  rv = mDirectory->Exists(&exists);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!exists) {
-    rv = path->Create(nsIFile::DIRECTORY_TYPE, 0755);
+    rv = mDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
     NS_ENSURE_SUCCESS(rv, rv);
   } else {
     DebugOnly<bool> isDirectory;
-    MOZ_ASSERT(NS_SUCCEEDED(path->IsDirectory(&isDirectory)));
+    MOZ_ASSERT(NS_SUCCEEDED(mDirectory->IsDirectory(&isDirectory)));
     MOZ_ASSERT(isDirectory, "Should have caught this earlier!");
   }
 
-  rv = path->Append(NS_LITERAL_STRING(ASMJSCACHE_FILE_NAME));
+  rv = mDirectory->Clone(getter_AddRefs(mMetadataFile));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mMetadataFile->Append(NS_LITERAL_STRING(ASMJSCACHE_METADATA_FILE_NAME));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mMetadataFile->Exists(&exists);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mQuotaObject = qm->GetQuotaObject(quota::PERSISTENCE_TYPE_TEMPORARY,
-                                    mGroup, mOrigin, path);
-  NS_ENSURE_STATE(mQuotaObject);
+  if (exists && NS_FAILED(ReadMetadataFile(mMetadataFile, mMetadata))) {
+    exists = false;
+  }
 
-  int32_t openFlags;
-  if (mOpenMode == eOpenForRead) {
-    rv = path->GetFileSize(&mFileSize);
-    if (NS_FAILED(rv)) {
-      return rv;
+  if (!exists) {
+    // If we are reading, we can't possibly have a cache hit.
+    if (mOpenMode == eOpenForRead) {
+      return NS_ERROR_FILE_NOT_FOUND;
     }
 
-    openFlags = PR_RDONLY | nsIFile::OS_READAHEAD;
-  } else {
-    if (!mQuotaObject->MaybeAllocateMoreSpace(0, mSizeToWrite)) {
-      return NS_ERROR_FAILURE;
+    // Initialize Metadata with a valid empty state for the LRU cache.
+    for (unsigned i = 0; i < Metadata::kNumEntries; i++) {
+      Metadata::Entry& entry = mMetadata.mEntries[i];
+      entry.mFastHash = -1;
+      entry.mNumChars = -1;
+      entry.mFullHash = -1;
+      entry.mModuleIndex = i;
     }
-
-    mFileSize = mSizeToWrite;
-
-    MOZ_ASSERT(mOpenMode == eOpenForWrite);
-    openFlags = PR_RDWR | PR_TRUNCATE | PR_CREATE_FILE;
   }
 
-  rv = path->OpenNSPRFileDesc(openFlags, 0644, &mFileDesc);
+  return NS_OK;
+}
+
+nsresult
+GetCacheFile(nsIFile* aDirectory, unsigned aModuleIndex, nsIFile** aCacheFile)
+{
+  nsCOMPtr<nsIFile> cacheFile;
+  nsresult rv = aDirectory->Clone(getter_AddRefs(cacheFile));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsString cacheFileName = NS_LITERAL_STRING(ASMJSCACHE_ENTRY_FILE_NAME_BASE);
+  cacheFileName.AppendInt(aModuleIndex);
+  rv = cacheFile->Append(cacheFileName);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  cacheFile.forget(aCacheFile);
+  return NS_OK;
+}
+
+nsresult
+MainProcessRunnable::OpenCacheFileForWrite()
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(mState == eReadyToReadMetadata);
+  MOZ_ASSERT(mOpenMode == eOpenForWrite);
+
+  mFileSize = mWriteParams.mSize;
+
+  // Kick out the oldest entry in the LRU queue in the metadata.
+  mModuleIndex = mMetadata.mEntries[Metadata::kLastEntry].mModuleIndex;
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = GetCacheFile(mDirectory, mModuleIndex, getter_AddRefs(file));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  QuotaManager* qm = QuotaManager::Get();
+  MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread");
+
+  // Create the QuotaObject before all file IO to get maximum assertion coverage
+  // in QuotaManager against concurrent removal, etc.
+  mQuotaObject = qm->GetQuotaObject(quota::PERSISTENCE_TYPE_TEMPORARY,
+                                    mGroup, mOrigin, file);
+  NS_ENSURE_STATE(mQuotaObject);
+
+  // Let the QuotaManager know we're about to consume more storage. The
+  // QuotaManager may veto this or schedule other storage to get evicted.
+  if (!mQuotaObject->MaybeAllocateMoreSpace(0, mWriteParams.mSize)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  int32_t openFlags = PR_RDWR | PR_TRUNCATE | PR_CREATE_FILE;
+  rv = file->OpenNSPRFileDesc(openFlags, 0644, &mFileDesc);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Move the mModuleIndex's LRU entry to the recent end of the queue.
+  PodMove(mMetadata.mEntries + 1, mMetadata.mEntries, Metadata::kLastEntry);
+  Metadata::Entry& entry = mMetadata.mEntries[0];
+  entry.mFastHash = mWriteParams.mFastHash;
+  entry.mNumChars = mWriteParams.mNumChars;
+  entry.mFullHash = mWriteParams.mFullHash;
+  entry.mModuleIndex = mModuleIndex;
+
+  rv = WriteMetadataFile(mMetadataFile, mMetadata);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+MainProcessRunnable::OpenCacheFileForRead()
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(mState == eReadyToOpenCacheFileForRead);
+  MOZ_ASSERT(mOpenMode == eOpenForRead);
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = GetCacheFile(mDirectory, mModuleIndex, getter_AddRefs(file));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  QuotaManager* qm = QuotaManager::Get();
+  MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread");
+
+  // Create the QuotaObject before all file IO to get maximum assertion coverage
+  // in QuotaManager against concurrent removal, etc.
+  mQuotaObject = qm->GetQuotaObject(quota::PERSISTENCE_TYPE_TEMPORARY,
+                                    mGroup, mOrigin, file);
+  NS_ENSURE_STATE(mQuotaObject);
+
+  rv = file->GetFileSize(&mFileSize);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  int32_t openFlags = PR_RDONLY | nsIFile::OS_READAHEAD;
+  rv = file->OpenNSPRFileDesc(openFlags, 0644, &mFileDesc);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Move the mModuleIndex's LRU entry to the recent end of the queue.
+  unsigned lruIndex = 0;
+  while (mMetadata.mEntries[lruIndex].mModuleIndex != mModuleIndex) {
+    if (++lruIndex == Metadata::kNumEntries) {
+      return NS_ERROR_UNEXPECTED;
+    }
+  }
+  Metadata::Entry entry = mMetadata.mEntries[lruIndex];
+  PodMove(mMetadata.mEntries + 1, mMetadata.mEntries, lruIndex);
+  mMetadata.mEntries[0] = entry;
+
+  rv = WriteMetadataFile(mMetadataFile, mMetadata);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 void
 MainProcessRunnable::FinishOnMainThread()
 {
@@ -517,69 +761,93 @@ MainProcessRunnable::Run()
       MOZ_ASSERT(NS_IsMainThread());
 
       rv = InitOnMainThread();
       if (NS_FAILED(rv)) {
         Fail();
         return NS_OK;
       }
 
-      mState = eWaitingToOpen;
+      mState = eWaitingToOpenMetadata;
       rv = QuotaManager::Get()->WaitForOpenAllowed(
                                      OriginOrPatternString::FromOrigin(mOrigin),
                                      Nullable<PersistenceType>(), mStorageId,
                                      this);
       if (NS_FAILED(rv)) {
         Fail();
         return NS_OK;
       }
 
       mNeedAllowNextSynchronizedOp = true;
       return NS_OK;
     }
 
-    case eWaitingToOpen: {
+    case eWaitingToOpenMetadata: {
       MOZ_ASSERT(NS_IsMainThread());
 
-      mState = eReadyToOpen;
+      mState = eReadyToReadMetadata;
+      DispatchToIOThread();
+      return NS_OK;
+    }
 
-      QuotaManager* qm = QuotaManager::Get();
-      if (!qm) {
+    case eReadyToReadMetadata: {
+      AssertIsOnIOThread();
+
+      rv = ReadMetadata();
+      if (NS_FAILED(rv)) {
         Fail();
         return NS_OK;
       }
 
-      rv = qm->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
+      if (mOpenMode == eOpenForRead) {
+        mState = eSendingMetadataForRead;
+        NS_DispatchToMainThread(this);
+        return NS_OK;
+      }
+
+      rv = OpenCacheFileForWrite();
       if (NS_FAILED(rv)) {
         Fail();
         return NS_OK;
       }
 
+      mState = eSendingCacheFile;
+      NS_DispatchToMainThread(this);
       return NS_OK;
     }
 
-    case eReadyToOpen: {
+    case eSendingMetadataForRead: {
+      MOZ_ASSERT(NS_IsMainThread());
+      MOZ_ASSERT(mOpenMode == eOpenForRead);
+
+      mState = eWaitingToOpenCacheFileForRead;
+      OnOpenMetadataForRead(mMetadata);
+      return NS_OK;
+    }
+
+    case eReadyToOpenCacheFileForRead: {
       AssertIsOnIOThread();
+      MOZ_ASSERT(mOpenMode == eOpenForRead);
 
-      rv = OpenFileOnIOThread();
+      rv = OpenCacheFileForRead();
       if (NS_FAILED(rv)) {
         Fail();
         return NS_OK;
       }
 
-      mState = eNotifying;
+      mState = eSendingCacheFile;
       NS_DispatchToMainThread(this);
       return NS_OK;
     }
 
-    case eNotifying: {
+    case eSendingCacheFile: {
       MOZ_ASSERT(NS_IsMainThread());
 
       mState = eOpened;
-      OnOpen();
+      OnOpenCacheFile();
       return NS_OK;
     }
 
     case eFailing: {
       MOZ_ASSERT(NS_IsMainThread());
 
       mState = eFinished;
       OnFailure();
@@ -589,65 +857,124 @@ MainProcessRunnable::Run()
     case eClosing: {
       MOZ_ASSERT(NS_IsMainThread());
 
       mState = eFinished;
       OnClose();
       return NS_OK;
     }
 
+    case eWaitingToOpenCacheFileForRead:
     case eOpened:
     case eFinished: {
       MOZ_ASSUME_UNREACHABLE("Shouldn't Run() in this state");
     }
   }
 
   MOZ_ASSUME_UNREACHABLE("Corrupt state");
   return NS_OK;
 }
 
+bool
+FindHashMatch(const Metadata& aMetadata, const ReadParams& aReadParams,
+              uint32_t* aModuleIndex)
+{
+  // Perform a fast hash of the first sNumFastHashChars chars. Each cache entry
+  // also stores an mFastHash of its first sNumFastHashChars so this gives us a
+  // fast way to probabilistically determine whether we have a cache hit. We
+  // still do a full hash of all the chars before returning the cache file to
+  // the engine to avoid penalizing the case where there are multiple cached
+  // asm.js modules where the first sNumFastHashChars are the same. The
+  // mFullHash of each cache entry can have a different mNumChars so the fast
+  // hash allows us to avoid performing up to Metadata::kNumEntries separate
+  // full hashes.
+  uint32_t numChars = aReadParams.mLimit - aReadParams.mBegin;
+  MOZ_ASSERT(numChars > sNumFastHashChars);
+  uint32_t fastHash = HashString(aReadParams.mBegin, sNumFastHashChars);
+
+  for (unsigned i = 0; i < Metadata::kNumEntries ; i++) {
+    // Compare the "fast hash" first to see whether it is worthwhile to
+    // hash all the chars.
+    Metadata::Entry entry = aMetadata.mEntries[i];
+    if (entry.mFastHash != fastHash) {
+      continue;
+    }
+
+    // Assuming we have enough characters, hash all the chars it would take
+    // to match this cache entry and compare to the cache entry. If we get a
+    // hit we'll still do a full source match later (in the JS engine), but
+    // the full hash match means this is probably the cache entry we want.
+    if (numChars < entry.mNumChars) {
+      continue;
+    }
+    uint32_t fullHash = HashString(aReadParams.mBegin, entry.mNumChars);
+    if (entry.mFullHash != fullHash) {
+      continue;
+    }
+
+    *aModuleIndex = entry.mModuleIndex;
+    return true;
+  }
+
+  return false;
+}
+
 // A runnable that executes for a cache access originating in the main process.
 class SingleProcessRunnable MOZ_FINAL : public File,
                                         private MainProcessRunnable
 {
 public:
   // In the single-process case, the calling JS compilation thread holds the
   // nsIPrincipal alive indirectly (via the global object -> compartment ->
   // principal) so we don't have to ref-count it here. This is fortunate since
   // we are off the main thread and nsIPrincipals can only be ref-counted on
   // the main thread.
   SingleProcessRunnable(nsIPrincipal* aPrincipal,
                         OpenMode aOpenMode,
-                        size_t aSizeToWrite)
-  : MainProcessRunnable(aPrincipal, aOpenMode, aSizeToWrite)
+                        WriteParams aWriteParams,
+                        ReadParams aReadParams)
+  : MainProcessRunnable(aPrincipal, aOpenMode, aWriteParams),
+    mReadParams(aReadParams)
   {
     MOZ_ASSERT(IsMainProcess());
     MOZ_ASSERT(!NS_IsMainThread());
     MOZ_COUNT_CTOR(SingleProcessRunnable);
   }
 
   ~SingleProcessRunnable()
   {
     MOZ_COUNT_DTOR(SingleProcessRunnable);
   }
 
 private:
   void
+  OnOpenMetadataForRead(const Metadata& aMetadata) MOZ_OVERRIDE
+  {
+    uint32_t moduleIndex;
+    if (!FindHashMatch(aMetadata, mReadParams, &moduleIndex)) {
+      MainProcessRunnable::Fail();
+      return;
+    }
+
+    MainProcessRunnable::OpenForRead(moduleIndex);
+  }
+
+  void
+  OnOpenCacheFile() MOZ_OVERRIDE
+  {
+    File::OnOpen();
+  }
+
+  void
   Close() MOZ_OVERRIDE MOZ_FINAL
   {
     MainProcessRunnable::Close();
   }
 
   void
-  OnOpen() MOZ_OVERRIDE
-  {
-    File::OnOpen();
-  }
-
-  void
   OnFailure() MOZ_OVERRIDE
   {
     MainProcessRunnable::OnFailure();
     File::OnFailure();
   }
 
   void
   OnClose() MOZ_OVERRIDE MOZ_FINAL
@@ -657,32 +984,34 @@ private:
   }
 
   // Avoid MSVC 'dominance' warning by having clear Run() override.
   NS_IMETHODIMP
   Run() MOZ_OVERRIDE
   {
     return MainProcessRunnable::Run();
   }
+
+  ReadParams mReadParams;
 };
 
 // A runnable that executes in a parent process for a cache access originating
 // in the content process. This runnable gets registered as an IPDL subprotocol
 // actor so that it can communicate with the corresponding ChildProcessRunnable.
 class ParentProcessRunnable MOZ_FINAL : public PAsmJSCacheEntryParent,
                                         public MainProcessRunnable
 {
 public:
   // The given principal comes from an IPC::Principal which will be dec-refed
   // at the end of the message, so we must ref-count it here. Fortunately, we
   // are on the main thread (where PContent messages are delivered).
   ParentProcessRunnable(nsIPrincipal* aPrincipal,
                         OpenMode aOpenMode,
-                        size_t aSizeToWrite)
-  : MainProcessRunnable(aPrincipal, aOpenMode, aSizeToWrite),
+                        WriteParams aWriteParams)
+  : MainProcessRunnable(aPrincipal, aOpenMode, aWriteParams),
     mPrincipalHolder(aPrincipal),
     mActorDestroyed(false),
     mOpened(false),
     mFinished(false)
   {
     MOZ_ASSERT(IsMainProcess());
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_COUNT_CTOR(ParentProcessRunnable);
@@ -727,26 +1056,43 @@ private:
     if (mOpened) {
       MainProcessRunnable::Close();
     } else {
       MainProcessRunnable::Fail();
     }
   }
 
   void
-  OnOpen() MOZ_OVERRIDE
+  OnOpenMetadataForRead(const Metadata& aMetadata) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (!SendOnOpenMetadataForRead(aMetadata)) {
+      unused << Send__delete__(this);
+    }
+  }
+
+  bool
+  RecvSelectCacheFileToRead(const uint32_t& aModuleIndex) MOZ_OVERRIDE
+  {
+    MainProcessRunnable::OpenForRead(aModuleIndex);
+    return true;
+  }
+
+  void
+  OnOpenCacheFile() MOZ_OVERRIDE
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     MOZ_ASSERT(!mOpened);
     mOpened = true;
 
     FileDescriptor::PlatformHandleType handle =
       FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(mFileDesc));
-    if (!SendOnOpen(mFileSize, handle)) {
+    if (!SendOnOpenCacheFile(mFileSize, handle)) {
       unused << Send__delete__(this);
     }
   }
 
   void
   OnClose() MOZ_OVERRIDE MOZ_FINAL
   {
     MOZ_ASSERT(NS_IsMainThread());
@@ -783,21 +1129,21 @@ private:
   bool mOpened;
   bool mFinished;
 };
 
 } // unnamed namespace
 
 PAsmJSCacheEntryParent*
 AllocEntryParent(OpenMode aOpenMode,
-                 uint32_t aSizeToWrite,
+                 WriteParams aWriteParams,
                  nsIPrincipal* aPrincipal)
 {
   ParentProcessRunnable* runnable =
-    new ParentProcessRunnable(aPrincipal, aOpenMode, aSizeToWrite);
+    new ParentProcessRunnable(aPrincipal, aOpenMode, aWriteParams);
 
   // AddRef to keep the runnable alive until DeallocEntryParent.
   runnable->AddRef();
 
   nsresult rv = NS_DispatchToMainThread(runnable);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   return runnable;
@@ -820,20 +1166,22 @@ public:
 
   // In the single-process case, the calling JS compilation thread holds the
   // nsIPrincipal alive indirectly (via the global object -> compartment ->
   // principal) so we don't have to ref-count it here. This is fortunate since
   // we are off the main thread and nsIPrincipals can only be ref-counted on
   // the main thread.
   ChildProcessRunnable(nsIPrincipal* aPrincipal,
                        OpenMode aOpenMode,
-                       size_t aSizeToWrite)
+                       WriteParams aWriteParams,
+                       ReadParams aReadParams)
   : mPrincipal(aPrincipal),
     mOpenMode(aOpenMode),
-    mSizeToWrite(aSizeToWrite),
+    mWriteParams(aWriteParams),
+    mReadParams(aReadParams),
     mActorDestroyed(false),
     mState(eInitial)
   {
     MOZ_ASSERT(!IsMainProcess());
     MOZ_ASSERT(!NS_IsMainThread());
     MOZ_COUNT_CTOR(ChildProcessRunnable);
   }
 
@@ -841,17 +1189,34 @@ public:
   {
     MOZ_ASSERT(mState == eFinished);
     MOZ_ASSERT(mActorDestroyed);
     MOZ_COUNT_DTOR(ChildProcessRunnable);
   }
 
 private:
   bool
-  RecvOnOpen(const int64_t& aFileSize, const FileDescriptor& aFileDesc)
+  RecvOnOpenMetadataForRead(const Metadata& aMetadata) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mState == eOpening);
+
+    uint32_t moduleIndex;
+    if (!FindHashMatch(aMetadata, mReadParams, &moduleIndex)) {
+      Fail();
+      Send__delete__(this);
+      return true;
+    }
+
+    return SendSelectCacheFileToRead(moduleIndex);
+  }
+
+  bool
+  RecvOnOpenCacheFile(const int64_t& aFileSize,
+                      const FileDescriptor& aFileDesc) MOZ_OVERRIDE
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mState == eOpening);
 
     mFileSize = aFileSize;
 
     mFileDesc = PR_ImportFile(PROsfd(aFileDesc.PlatformHandle()));
     if (!mFileDesc) {
@@ -864,18 +1229,17 @@ private:
   }
 
   bool
   Recv__delete__() MOZ_OVERRIDE
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mState == eOpening);
 
-    mState = eFinished;
-    File::OnFailure();
+    Fail();
     return true;
   }
 
   void
   ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE
   {
     MOZ_ASSERT(NS_IsMainThread());
     mActorDestroyed = true;
@@ -886,19 +1250,30 @@ private:
   {
     MOZ_ASSERT(mState == eOpened);
 
     mState = eClosing;
     NS_DispatchToMainThread(this);
   }
 
 private:
+  void
+  Fail()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mState == eInitial || mState == eOpening);
+
+    mState = eFinished;
+    File::OnFailure();
+  }
+
   nsIPrincipal* const mPrincipal;
   const OpenMode mOpenMode;
-  size_t mSizeToWrite;
+  WriteParams mWriteParams;
+  ReadParams mReadParams;
   bool mActorDestroyed;
 
   enum State {
     eInitial, // Just created, waiting to dispatched to the main thread
     eOpening, // Waiting for the parent process to respond
     eOpened, // Parent process opened the entry and sent it back
     eClosing, // Waiting to be dispatched to the main thread to Send__delete__
     eFinished // Terminal state
@@ -913,26 +1288,25 @@ ChildProcessRunnable::Run()
     case eInitial: {
       MOZ_ASSERT(NS_IsMainThread());
 
       // AddRef to keep this runnable alive until IPDL deallocates the
       // subprotocol (DeallocEntryChild).
       AddRef();
 
       if (!ContentChild::GetSingleton()->SendPAsmJSCacheEntryConstructor(
-        this, mOpenMode, mSizeToWrite, IPC::Principal(mPrincipal)))
+        this, mOpenMode, mWriteParams, IPC::Principal(mPrincipal)))
       {
         // On failure, undo the AddRef (since DeallocEntryChild will not be
         // called) and unblock the parsing thread with a failure. The main
         // thread event loop still holds an outstanding ref which will keep
         // 'this' alive until returning to the event loop.
         Release();
 
-        mState = eFinished;
-        File::OnFailure();
+        Fail();
         return NS_OK;
       }
 
       mState = eOpening;
       return NS_OK;
     }
 
     case eClosing: {
@@ -971,20 +1345,22 @@ DeallocEntryChild(PAsmJSCacheEntryChild*
   static_cast<ChildProcessRunnable*>(aActor)->Release();
 }
 
 namespace {
 
 bool
 OpenFile(nsIPrincipal* aPrincipal,
          OpenMode aOpenMode,
-         size_t aSizeToWrite,
+         WriteParams aWriteParams,
+         ReadParams aReadParams,
          File::AutoClose* aFile)
 {
-  MOZ_ASSERT_IF(aOpenMode == eOpenForRead, aSizeToWrite == 0);
+  MOZ_ASSERT_IF(aOpenMode == eOpenForRead, aWriteParams.mSize == 0);
+  MOZ_ASSERT_IF(aOpenMode == eOpenForWrite, aReadParams.mBegin == nullptr);
 
   // There are three reasons we don't attempt caching from the main thread:
   //  1. In the parent process: QuotaManager::WaitForOpenAllowed prevents
   //     synchronous waiting on the main thread requiring a runnable to be
   //     dispatched to the main thread.
   //  2. In the child process: the IPDL PContent messages we need to
   //     synchronously wait on are dispatched to the main thread.
   //  3. While a cache lookup *should* be much faster than compilation, IO
@@ -997,51 +1373,54 @@ OpenFile(nsIPrincipal* aPrincipal,
     return false;
   }
 
   // If we are in a child process, we need to synchronously call into the
   // parent process to open the file and interact with the QuotaManager. The
   // child can then map the file into its address space to perform I/O.
   nsRefPtr<File> file;
   if (IsMainProcess()) {
-    file = new SingleProcessRunnable(aPrincipal, aOpenMode, aSizeToWrite);
+    file = new SingleProcessRunnable(aPrincipal, aOpenMode, aWriteParams,
+                                     aReadParams);
   } else {
-    file = new ChildProcessRunnable(aPrincipal, aOpenMode, aSizeToWrite);
+    file = new ChildProcessRunnable(aPrincipal, aOpenMode, aWriteParams,
+                                    aReadParams);
   }
 
   if (!file->BlockUntilOpen(aFile)) {
     return false;
   }
 
   return file->MapMemory(aOpenMode);
 }
 
 } // anonymous namespace
 
 typedef uint32_t AsmJSCookieType;
 static const uint32_t sAsmJSCookie = 0x600d600d;
 
-// Anything smaller should compile fast enough that caching will just add
-// overhead.
-static const size_t sMinCachedModuleLength = 10000;
-
 bool
 OpenEntryForRead(nsIPrincipal* aPrincipal,
                  const jschar* aBegin,
                  const jschar* aLimit,
                  size_t* aSize,
                  const uint8_t** aMemory,
                  intptr_t* aFile)
 {
   if (size_t(aLimit - aBegin) < sMinCachedModuleLength) {
     return false;
   }
 
+  ReadParams readParams;
+  readParams.mBegin = aBegin;
+  readParams.mLimit = aLimit;
+
   File::AutoClose file;
-  if (!OpenFile(aPrincipal, eOpenForRead, 0, &file)) {
+  WriteParams notAWrite;
+  if (!OpenFile(aPrincipal, eOpenForRead, notAWrite, readParams, &file)) {
     return false;
   }
 
   // Although we trust that the stored cache files have not been arbitrarily
   // corrupted, it is possible that a previous execution aborted in the middle
   // of writing a cache file (crash, OOM-killer, etc). To protect against
   // partially-written cache files, we use the following scheme:
   //  - Allocate an extra word at the beginning of every cache file which
@@ -1087,18 +1466,27 @@ OpenEntryForWrite(nsIPrincipal* aPrincip
 {
   if (size_t(aEnd - aBegin) < sMinCachedModuleLength) {
     return false;
   }
 
   // Add extra space for the AsmJSCookieType (see OpenEntryForRead).
   aSize += sizeof(AsmJSCookieType);
 
+  static_assert(sNumFastHashChars < sMinCachedModuleLength, "HashString safe");
+
+  WriteParams writeParams;
+  writeParams.mSize = aSize;
+  writeParams.mFastHash = HashString(aBegin, sNumFastHashChars);
+  writeParams.mNumChars = aEnd - aBegin;
+  writeParams.mFullHash = HashString(aBegin, writeParams.mNumChars);
+
   File::AutoClose file;
-  if (!OpenFile(aPrincipal, eOpenForWrite, aSize, &file)) {
+  ReadParams notARead;
+  if (!OpenFile(aPrincipal, eOpenForWrite, writeParams, notARead, &file)) {
     return false;
   }
 
   // Strip off the AsmJSCookieType from the buffer returned to the caller,
   // which expects a buffer of aSize, not a buffer of sizeWithCookie starting
   // with a cookie.
   *aMemory = (uint8_t*) file->MappedMemory() + sizeof(AsmJSCookieType);
 
@@ -1123,17 +1511,17 @@ CloseEntryForWrite(JS::Handle<JSObject*>
   if (PR_SyncMemMap(file->FileDesc(),
                     file->MappedMemory(),
                     file->FileSize()) == PR_SUCCESS) {
     *(AsmJSCookieType*)file->MappedMemory() = sAsmJSCookie;
   }
 }
 
 bool
-GetBuildId(js::Vector<char>* aBuildID)
+GetBuildId(JS::BuildIdCharVector* aBuildID)
 {
   nsCOMPtr<nsIXULAppInfo> info = do_GetService("@mozilla.org/xre/app-info;1");
   if (!info) {
     return false;
   }
 
   nsCString buildID;
   nsresult rv = info->GetPlatformBuildID(buildID);
@@ -1187,29 +1575,34 @@ public:
     nsresult rv = qm->GetDirectoryForOrigin(aPersistenceType, aOrigin,
                                             getter_AddRefs(directory));
     NS_ENSURE_SUCCESS(rv, rv);
     MOZ_ASSERT(directory, "We're here because the origin directory exists");
 
     rv = directory->Append(NS_LITERAL_STRING(ASMJSCACHE_DIRECTORY_NAME));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    bool exists;
+    DebugOnly<bool> exists;
     MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)) && exists);
 
-    nsIFile* path = directory;
-    rv = path->Append(NS_LITERAL_STRING(ASMJSCACHE_FILE_NAME));
+    nsCOMPtr<nsISimpleEnumerator> entries;
+    rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = path->Exists(&exists);
-    NS_ENSURE_SUCCESS(rv, rv);
+    bool more;
+    while (NS_SUCCEEDED((rv = entries->HasMoreElements(&more))) && more) {
+      nsCOMPtr<nsISupports> entry;
+      rv = entries->GetNext(getter_AddRefs(entry));
+      NS_ENSURE_SUCCESS(rv, rv);
 
-    if (exists) {
+      nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
+      NS_ENSURE_TRUE(file, NS_NOINTERFACE);
+
       int64_t fileSize;
-      rv = path->GetFileSize(&fileSize);
+      rv = file->GetFileSize(&fileSize);
       NS_ENSURE_SUCCESS(rv, rv);
 
       MOZ_ASSERT(fileSize >= 0, "Negative size?!");
 
       // Since the client is not explicitly storing files, append to database
       // usage which represents implicit storage allocation.
       aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
     }
@@ -1274,8 +1667,84 @@ quota::Client*
 CreateClient()
 {
   return new Client();
 }
 
 } // namespace asmjscache
 } // namespace dom
 } // namespace mozilla
+
+namespace IPC {
+
+using mozilla::dom::asmjscache::Metadata;
+using mozilla::dom::asmjscache::WriteParams;
+
+void
+ParamTraits<Metadata>::Write(Message* aMsg, const paramType& aParam)
+{
+  for (unsigned i = 0; i < Metadata::kNumEntries; i++) {
+    const Metadata::Entry& entry = aParam.mEntries[i];
+    WriteParam(aMsg, entry.mFastHash);
+    WriteParam(aMsg, entry.mNumChars);
+    WriteParam(aMsg, entry.mFullHash);
+    WriteParam(aMsg, entry.mModuleIndex);
+  }
+}
+
+bool
+ParamTraits<Metadata>::Read(const Message* aMsg, void** aIter,
+                            paramType* aResult)
+{
+  for (unsigned i = 0; i < Metadata::kNumEntries; i++) {
+    Metadata::Entry& entry = aResult->mEntries[i];
+    if (!ReadParam(aMsg, aIter, &entry.mFastHash) ||
+        !ReadParam(aMsg, aIter, &entry.mNumChars) ||
+        !ReadParam(aMsg, aIter, &entry.mFullHash) ||
+        !ReadParam(aMsg, aIter, &entry.mModuleIndex))
+    {
+      return false;
+    }
+  }
+  return true;
+}
+
+void
+ParamTraits<Metadata>::Log(const paramType& aParam, std::wstring* aLog)
+{
+  for (unsigned i = 0; i < Metadata::kNumEntries; i++) {
+    const Metadata::Entry& entry = aParam.mEntries[i];
+    LogParam(entry.mFastHash, aLog);
+    LogParam(entry.mNumChars, aLog);
+    LogParam(entry.mFullHash, aLog);
+    LogParam(entry.mModuleIndex, aLog);
+  }
+}
+
+void
+ParamTraits<WriteParams>::Write(Message* aMsg, const paramType& aParam)
+{
+  WriteParam(aMsg, aParam.mSize);
+  WriteParam(aMsg, aParam.mFastHash);
+  WriteParam(aMsg, aParam.mNumChars);
+  WriteParam(aMsg, aParam.mFullHash);
+}
+
+bool
+ParamTraits<WriteParams>::Read(const Message* aMsg, void** aIter,
+                               paramType* aResult)
+{
+  return ReadParam(aMsg, aIter, &aResult->mSize) &&
+         ReadParam(aMsg, aIter, &aResult->mFastHash) &&
+         ReadParam(aMsg, aIter, &aResult->mNumChars) &&
+         ReadParam(aMsg, aIter, &aResult->mFullHash);
+}
+
+void
+ParamTraits<WriteParams>::Log(const paramType& aParam, std::wstring* aLog)
+{
+  LogParam(aParam.mSize, aLog);
+  LogParam(aParam.mFastHash, aLog);
+  LogParam(aParam.mNumChars, aLog);
+  LogParam(aParam.mFullHash, aLog);
+}
+
+} // namespace IPC
--- a/dom/asmjscache/AsmJSCache.h
+++ b/dom/asmjscache/AsmJSCache.h
@@ -5,16 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_asmjscache_asmjscache_h
 #define mozilla_dom_asmjscache_asmjscache_h
 
 #include "ipc/IPCMessageUtils.h"
 #include "js/TypeDecls.h"
 #include "js/Vector.h"
+#include "jsapi.h"
 
 class nsIPrincipal;
 
 namespace mozilla {
 namespace dom {
 
 namespace quota {
 class Client;
@@ -27,16 +28,64 @@ class PAsmJSCacheEntryParent;
 
 enum OpenMode
 {
   eOpenForRead,
   eOpenForWrite,
   NUM_OPEN_MODES
 };
 
+// Each origin stores a fixed size (kNumEntries) LRU cache of compiled asm.js
+// modules. Each compiled asm.js module is stored in a separate file with one
+// extra metadata file that stores the LRU cache and enough information for a
+// client to pick which cached module's file to open.
+struct Metadata
+{
+  static const unsigned kNumEntries = 16;
+  static const unsigned kLastEntry = kNumEntries - 1;
+
+  struct Entry
+  {
+    uint32_t mFastHash;
+    uint32_t mNumChars;
+    uint32_t mFullHash;
+    uint32_t mModuleIndex;
+  };
+
+  Entry mEntries[kNumEntries];
+};
+
+// Parameters specific to opening a cache entry for writing
+struct WriteParams
+{
+  int64_t mSize;
+  int64_t mFastHash;
+  int64_t mNumChars;
+  int64_t mFullHash;
+
+  WriteParams()
+  : mSize(0),
+    mFastHash(0),
+    mNumChars(0),
+    mFullHash(0)
+  { }
+};
+
+// Parameters specific to opening a cache entry for reading
+struct ReadParams
+{
+  const jschar* mBegin;
+  const jschar* mLimit;
+
+  ReadParams()
+  : mBegin(nullptr),
+    mLimit(nullptr)
+  { }
+};
+
 // Implementation of AsmJSCacheOps, installed for the main JSRuntime by
 // nsJSEnvironment.cpp and DOM Worker JSRuntimes in RuntimeService.cpp.
 //
 // The Open* functions cannot be called directly from AsmJSCacheOps: they take
 // an nsIPrincipal as the first argument instead of a Handle<JSObject*>. The
 // caller must map the object to an nsIPrincipal.
 //
 // These methods may be called off the main thread and guarantee not to
@@ -63,28 +112,29 @@ OpenEntryForWrite(nsIPrincipal* aPrincip
                   size_t aSize,
                   uint8_t** aMemory,
                   intptr_t* aHandle);
 void
 CloseEntryForWrite(JS::Handle<JSObject*> aGlobal,
                    size_t aSize,
                    uint8_t* aMemory,
                    intptr_t aHandle);
+
 bool
-GetBuildId(js::Vector<char>* aBuildId);
+GetBuildId(JS::BuildIdCharVector* aBuildId);
 
 // Called from QuotaManager.cpp:
 
 quota::Client*
 CreateClient();
 
 // Called from ipc/ContentParent.cpp:
 
 PAsmJSCacheEntryParent*
-AllocEntryParent(OpenMode aOpenMode, uint32_t aSizeToWrite,
+AllocEntryParent(OpenMode aOpenMode, WriteParams aWriteParams,
                  nsIPrincipal* aPrincipal);
 
 void
 DeallocEntryParent(PAsmJSCacheEntryParent* aActor);
 
 // Called from ipc/ContentChild.cpp:
 
 void
@@ -98,11 +148,29 @@ namespace IPC {
 
 template <>
 struct ParamTraits<mozilla::dom::asmjscache::OpenMode> :
   public EnumSerializer<mozilla::dom::asmjscache::OpenMode,
                         mozilla::dom::asmjscache::eOpenForRead,
                         mozilla::dom::asmjscache::NUM_OPEN_MODES>
 { };
 
+template <>
+struct ParamTraits<mozilla::dom::asmjscache::Metadata>
+{
+  typedef mozilla::dom::asmjscache::Metadata paramType;
+  static void Write(Message* aMsg, const paramType& aParam);
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult);
+  static void Log(const paramType& aParam, std::wstring* aLog);
+};
+
+template <>
+struct ParamTraits<mozilla::dom::asmjscache::WriteParams>
+{
+  typedef mozilla::dom::asmjscache::WriteParams paramType;
+  static void Write(Message* aMsg, const paramType& aParam);
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult);
+  static void Log(const paramType& aParam, std::wstring* aLog);
+};
+
 } // namespace IPC
 
 #endif  // mozilla_dom_asmjscache_asmjscache_h
--- a/dom/asmjscache/PAsmJSCacheEntry.ipdl
+++ b/dom/asmjscache/PAsmJSCacheEntry.ipdl
@@ -1,24 +1,36 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PContent;
 
+using mozilla::dom::asmjscache::Metadata from "mozilla/dom/asmjscache/AsmJSCache.h";
+
 namespace mozilla {
 namespace dom {
 namespace asmjscache {
 
 protocol PAsmJSCacheEntry
 {
   manager PContent;
 
+  // When the cache is opened to read, the parent process sends over the
+  // origin's Metadata so the child process can select the cache entry to open
+  // (based on hash) and notify the parent (via SelectCacheFileToRead).
 child:
-  OnOpen(int64_t fileSize, FileDescriptor fileDesc);
+  OnOpenMetadataForRead(Metadata metadata);
+parent:
+  SelectCacheFileToRead(uint32_t moduleIndex);
+
+child:
+  // Once the cache file has been opened, the child is notified and sent an
+  // open file descriptor.
+  OnOpenCacheFile(int64_t fileSize, FileDescriptor fileDesc);
 
 both:
   __delete__();
 };
 
 } // namespace asmjscache
 } // namespace dom
 } // namespace mozilla
--- a/dom/asmjscache/moz.build
+++ b/dom/asmjscache/moz.build
@@ -1,14 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+TEST_DIRS += ['test']
+
 EXPORTS.mozilla.dom.asmjscache += [
     'AsmJSCache.h'
 ]
 
 SOURCES += [
     'AsmJSCache.cpp'
 ]
 
rename from js/xpconnect/tests/mochitest/file_asmjs.js
rename to dom/asmjscache/test/file_slow.js
new file mode 100644
--- /dev/null
+++ b/dom/asmjscache/test/mochitest.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+support-files =
+  file_slow.js
+
+[test_cachingBasic.html]
+[test_workers.html]
+[test_cachingMulti.html]
+[test_slow.html]
+# bug 929498
+skip-if = os == 'android'
new file mode 100644
--- /dev/null
+++ b/dom/asmjscache/test/moz.build
@@ -0,0 +1,8 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+MOCHITEST_MANIFESTS += ['mochitest.ini']
+
rename from js/xpconnect/tests/mochitest/test_asmjs2.html
rename to dom/asmjscache/test/test_cachingBasic.html
new file mode 100644
--- /dev/null
+++ b/dom/asmjscache/test/test_cachingMulti.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=944821
+-->
+<head>
+  <meta charset="utf-8">
+  <title>asm.js browser tests</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=944821">asm.js browser tests</a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test"></pre>
+
+  <script>
+  var jsFuns = SpecialPowers.Cu.getJSTestingFunctions();
+  ok(jsFuns.isAsmJSCompilationAvailable(), "compilation is available");
+
+  // generate four slightly different big asm.js modules and compile them async
+  // so that we can hit the asm.js cache.
+
+  var code = "function f() { 'use asm';\n";
+  for (var i = 0; i < 1000; i++)
+    code += "function g" + i + "() { return " + i + "}\n";
+  ok(code.length > 10000, "code is long enough");
+
+  const N = 4;
+
+  var codes = [];
+  for (var i = 0; i < N; i++) {
+    var code2 = code;
+    code2 += "return g" + i + ";\n";
+    code2 += "}\n";
+    code2 += "ok(jsFuns.isAsmJSModule(f), 'f is an asm.js module')\n";
+    code2 += "if (assertCacheHit) ok(jsFuns.isAsmJSModuleLoadedFromCache(f), 'cache hit');\n";
+    code2 += "var gX = f();\n";
+    code2 += "ok(jsFuns.isAsmJSFunction(gX), 'gX is an asm.js function')\n";
+    code2 += "ok(gX() === " + i + ", 'gX returns the correct result')\n";
+    code2 += "finishedEvalAsync();\n";
+    codes.push(code2);
+  }
+
+  function evalAsync(code) {
+    var blob = new Blob([code], {type:"application/javascript"});
+    var script = document.createElement('script');
+    script.src = URL.createObjectURL(blob);
+    document.body.appendChild(script);
+  }
+
+  for (var i = 0; i < N; i++)
+    evalAsync(codes[i]);
+
+  var finishedCount = 0;
+  var assertCacheHit = false;
+  function finishedEvalAsync() {
+    finishedCount++;
+
+    if (finishedCount < 1 || finishedCount > 2*N) {
+      throw "Huh?!";
+    } else if (finishedCount == N) {
+      assertCacheHit = true;
+      for (var i = 0; i < N; i++)
+        evalAsync(codes[i]);
+    } else if (finishedCount == 2*N) {
+      SimpleTest.finish();
+    }
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  </script>
+
+</body>
+</html>
+
rename from js/xpconnect/tests/mochitest/test_asmjs.html
rename to dom/asmjscache/test/test_slow.html
--- a/js/xpconnect/tests/mochitest/test_asmjs.html
+++ b/dom/asmjscache/test/test_slow.html
@@ -15,20 +15,20 @@ https://bugzilla.mozilla.org/show_bug.cg
   <div id="content" style="display: none"></div>
   <pre id="test"></pre>
 
   <script>
   var jsFuns = SpecialPowers.Cu.getJSTestingFunctions();
   ok(jsFuns.isAsmJSCompilationAvailable(), "asm.js compilation is available");
   </script>
 
-  <script src="http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_asmjs.js"></script>
+  <script src="http://mochi.test:8888/tests/dom/asmjscache/test/file_slow.js"></script>
 
   <script>
-  var w = new Worker('http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_asmjs.js');
+  var w = new Worker('http://mochi.test:8888/tests/dom/asmjscache/test/file_slow.js');
   w.onmessage = function(e) {
       ok(e.data === "ok", "Worker asm.js tests");
       SimpleTest.finish();
   }
 
   SimpleTest.waitForExplicitFinish();
   </script>
 
rename from js/xpconnect/tests/mochitest/test_asmjs3.html
rename to dom/asmjscache/test/test_workers.html
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -10,16 +10,17 @@
 
 #include "jsapi.h"
 #include "xpcpublic.h"
 #include "nsIGlobalObject.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsContentUtils.h"
 #include "nsTArray.h"
+#include "nsJSUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 class ScriptSettingsStack;
 static mozilla::ThreadLocal<ScriptSettingsStack*> sScriptSettingsTLS;
 
 ScriptSettingsStackEntry ScriptSettingsStackEntry::SystemSingleton;
@@ -87,16 +88,35 @@ InitScriptSettings()
 void DestroyScriptSettings()
 {
   ScriptSettingsStack* ptr = sScriptSettingsTLS.get();
   MOZ_ASSERT(ptr);
   sScriptSettingsTLS.set(nullptr);
   delete ptr;
 }
 
+// This mostly gets the entry global, but doesn't entirely match the spec in
+// certain edge cases. It's good enough for some purposes, but not others. If
+// you want to call this function, ping bholley and describe your use-case.
+nsIGlobalObject*
+BrokenGetEntryGlobal()
+{
+  // We need the current JSContext in order to check the JS for
+  // scripted frames that may have appeared since anyone last
+  // manipulated the stack. If it's null, that means that there
+  // must be no entry point on the stack.
+  JSContext *cx = nsContentUtils::GetCurrentJSContextForThread();
+  if (!cx) {
+    MOZ_ASSERT(ScriptSettingsStack::Ref().EntryPoint() == nullptr);
+    return nullptr;
+  }
+
+  return nsJSUtils::GetDynamicScriptGlobal(cx);
+}
+
 // Note: When we're ready to expose it, GetEntryGlobal will look similar to
 // GetIncumbentGlobal below.
 
 nsIGlobalObject*
 GetIncumbentGlobal()
 {
   // We need the current JSContext in order to check the JS for
   // scripted frames that may have appeared since anyone last
--- a/dom/base/ScriptSettings.h
+++ b/dom/base/ScriptSettings.h
@@ -22,16 +22,21 @@ namespace dom {
 
 /*
  * System-wide setup/teardown routines. Init and Destroy should be invoked
  * once each, at startup and shutdown (respectively).
  */
 void InitScriptSettings();
 void DestroyScriptSettings();
 
+// This mostly gets the entry global, but doesn't entirely match the spec in
+// certain edge cases. It's good enough for some purposes, but not others. If
+// you want to call this function, ping bholley and describe your use-case.
+nsIGlobalObject* BrokenGetEntryGlobal();
+
 // Note: We don't yet expose GetEntryGlobal, because in order for it to be
 // correct, we first need to replace a bunch of explicit cx pushing in the
 // browser with AutoEntryScript. But GetIncumbentGlobal is simpler, because it
 // can mostly be inferred from the JS stack.
 nsIGlobalObject* GetIncumbentGlobal();
 
 class ScriptSettingsStack;
 struct ScriptSettingsStackEntry {
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -79,16 +79,17 @@ UNIFIED_SOURCES += [
     'nsContentPermissionHelper.cpp',
     'nsDOMClassInfo.cpp',
     'nsDOMNavigationTiming.cpp',
     'nsDOMScriptObjectFactory.cpp',
     'nsDOMWindowList.cpp',
     'nsFocusManager.cpp',
     'nsGlobalWindowCommands.cpp',
     'nsHistory.cpp',
+    'nsIGlobalObject.cpp',
     'nsJSTimeoutHandler.cpp',
     'nsJSUtils.cpp',
     'nsLocation.cpp',
     'nsMimeTypeArray.cpp',
     'nsPerformance.cpp',
     'nsQueryContentEventResult.cpp',
     'nsScreen.cpp',
     'nsScriptNameSpaceManager.cpp',
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -7488,24 +7488,24 @@ nsGlobalWindow::CallerInnerWindow()
 class PostMessageEvent : public nsRunnable
 {
   public:
     NS_DECL_NSIRUNNABLE
 
     PostMessageEvent(nsGlobalWindow* aSource,
                      const nsAString& aCallerOrigin,
                      nsGlobalWindow* aTargetWindow,
-                     nsIURI* aProvidedOrigin,
+                     nsIPrincipal* aProvidedPrincipal,
                      bool aTrustedCaller)
     : mSource(aSource),
       mCallerOrigin(aCallerOrigin),
       mMessage(nullptr),
       mMessageLen(0),
       mTargetWindow(aTargetWindow),
-      mProvidedOrigin(aProvidedOrigin),
+      mProvidedPrincipal(aProvidedPrincipal),
       mTrustedCaller(aTrustedCaller)
     {
       MOZ_COUNT_CTOR(PostMessageEvent);
     }
     
     ~PostMessageEvent()
     {
       NS_ASSERTION(!mMessage, "Message should have been deserialized!");
@@ -7525,17 +7525,17 @@ class PostMessageEvent : public nsRunnab
     }
 
   private:
     nsRefPtr<nsGlobalWindow> mSource;
     nsString mCallerOrigin;
     uint64_t* mMessage;
     size_t mMessageLen;
     nsRefPtr<nsGlobalWindow> mTargetWindow;
-    nsCOMPtr<nsIURI> mProvidedOrigin;
+    nsCOMPtr<nsIPrincipal> mProvidedPrincipal;
     bool mTrustedCaller;
     nsTArray<nsCOMPtr<nsISupports> > mSupportsArray;
 };
 
 namespace {
 
 struct StructuredCloneInfo {
   PostMessageEvent* event;
@@ -7694,42 +7694,32 @@ PostMessageEvent::Run()
   // Ensure that any origin which might have been provided is the origin of this
   // window's document.  Note that we do this *now* instead of when postMessage
   // is called because the target window might have been navigated to a
   // different location between then and now.  If this check happened when
   // postMessage was called, it would be fairly easy for a malicious webpage to
   // intercept messages intended for another site by carefully timing navigation
   // of the target window so it changed location after postMessage but before
   // now.
-  if (mProvidedOrigin) {
+  if (mProvidedPrincipal) {
     // Get the target's origin either from its principal or, in the case the
     // principal doesn't carry a URI (e.g. the system principal), the target's
     // document.
     nsIPrincipal* targetPrin = targetWindow->GetPrincipal();
-    if (!targetPrin)
-      return NS_OK;
-    nsCOMPtr<nsIURI> targetURI;
-    if (NS_FAILED(targetPrin->GetURI(getter_AddRefs(targetURI))))
+    if (NS_WARN_IF(!targetPrin))
       return NS_OK;
-    if (!targetURI) {
-      targetURI = targetWindow->mDoc->GetDocumentURI();
-      if (!targetURI)
-        return NS_OK;
-    }
 
     // Note: This is contrary to the spec with respect to file: URLs, which
     //       the spec groups into a single origin, but given we intentionally
     //       don't do that in other places it seems better to hold the line for
     //       now.  Long-term, we want HTML5 to address this so that we can
     //       be compliant while being safer.
-    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
-    nsresult rv =
-      ssm->CheckSameOriginURI(mProvidedOrigin, targetURI, true);
-    if (NS_FAILED(rv))
+    if (!targetPrin->EqualsIgnoringDomain(mProvidedPrincipal)) {
       return NS_OK;
+    }
   }
 
   // Deserialize the structured clone data
   JS::Rooted<JS::Value> messageData(cx);
   {
     StructuredCloneInfo scInfo;
     scInfo.event = this;
     scInfo.window = targetWindow;
@@ -7852,38 +7842,70 @@ nsGlobalWindow::PostMessageMoz(JSContext
   else {
     // in case of a sandbox with a system principal origin can be empty
     if (!nsContentUtils::IsSystemPrincipal(callerPrin)) {
       return;
     }
   }
 
   // Convert the provided origin string into a URI for comparison purposes.
+  nsCOMPtr<nsIPrincipal> providedPrincipal;
+
+  if (aTargetOrigin.EqualsASCII("/")) {
+    providedPrincipal = BrokenGetEntryGlobal()->PrincipalOrNull();
+    if (NS_WARN_IF(!providedPrincipal))
+      return;
+  }
+
   // "*" indicates no specific origin is required.
-  nsCOMPtr<nsIURI> providedOrigin;
-  if (!aTargetOrigin.EqualsASCII("*")) {
-    if (NS_FAILED(NS_NewURI(getter_AddRefs(providedOrigin), aTargetOrigin))) {
+  else if (!aTargetOrigin.EqualsASCII("*")) {
+    nsCOMPtr<nsIURI> originURI;
+    if (NS_FAILED(NS_NewURI(getter_AddRefs(originURI), aTargetOrigin))) {
       aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
       return;
     }
-    if (NS_FAILED(providedOrigin->SetUserPass(EmptyCString())) ||
-        NS_FAILED(providedOrigin->SetPath(EmptyCString()))) {
+
+    if (NS_FAILED(originURI->SetUserPass(EmptyCString())) ||
+        NS_FAILED(originURI->SetPath(EmptyCString()))) {
+      return;
+    }
+
+    nsCOMPtr<nsIScriptSecurityManager> ssm =
+      nsContentUtils::GetSecurityManager();
+    MOZ_ASSERT(ssm);
+
+    nsCOMPtr<nsIPrincipal> principal = nsContentUtils::GetSubjectPrincipal();
+    MOZ_ASSERT(principal);
+
+    uint32_t appId;
+    if (NS_WARN_IF(NS_FAILED(principal->GetAppId(&appId))))
+      return;
+
+    bool isInBrowser;
+    if (NS_WARN_IF(NS_FAILED(principal->GetIsInBrowserElement(&isInBrowser))))
+      return;
+
+    // Create a nsIPrincipal inheriting the app/browser attributes from the
+    // caller.
+    nsresult rv = ssm->GetAppCodebasePrincipal(originURI, appId, isInBrowser,
+                                             getter_AddRefs(providedPrincipal));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
   }
 
   // Create and asynchronously dispatch a runnable which will handle actual DOM
   // event creation and dispatch.
   nsRefPtr<PostMessageEvent> event =
     new PostMessageEvent(nsContentUtils::IsCallerChrome() || !callerInnerWin
                          ? nullptr
                          : callerInnerWin->GetOuterWindowInternal(),
                          origin,
                          this,
-                         providedOrigin,
+                         providedPrincipal,
                          nsContentUtils::IsCallerChrome());
 
   // We *must* clone the data here, or the JS::Value could be modified
   // by script
   JSAutoStructuredCloneBuffer buffer;
   StructuredCloneInfo scInfo;
   scInfo.event = event;
   scInfo.window = this;
new file mode 100644
--- /dev/null
+++ b/dom/base/nsIGlobalObject.cpp
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIGlobalObject.h"
+#include "nsContentUtils.h"
+
+nsIPrincipal*
+nsIGlobalObject::PrincipalOrNull()
+{
+  JSObject *global = GetGlobalJSObject();
+  if (NS_WARN_IF(!global))
+    return nullptr;
+
+  return nsContentUtils::GetObjectPrincipal(global);
+}
--- a/dom/base/nsIGlobalObject.h
+++ b/dom/base/nsIGlobalObject.h
@@ -8,20 +8,25 @@
 
 #include "nsISupports.h"
 #include "js/TypeDecls.h"
 
 #define NS_IGLOBALOBJECT_IID \
 { 0xe2538ded, 0x13ef, 0x4f4d, \
 { 0x94, 0x6b, 0x65, 0xd3, 0x33, 0xb4, 0xf0, 0x3c } }
 
+class nsIPrincipal;
+
 class nsIGlobalObject : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IGLOBALOBJECT_IID)
 
   virtual JSObject* GetGlobalJSObject() = 0;
+
+  // This method is not meant to be overridden.
+  nsIPrincipal* PrincipalOrNull();
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIGlobalObject,
                               NS_IGLOBALOBJECT_IID)
 
 #endif // nsIGlobalObject_h__
new file mode 100644
--- /dev/null
+++ b/dom/base/test/iframe_postMessage_solidus.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+  <script type="application/javascript">
+
+  window.addEventListener('message', receiveMessage, false);
+  function receiveMessage(evt) {
+    window.parent.postMessage(evt.data, '*');
+  }
+
+  </script>
+</body>
+</html>
+
+
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -1,14 +1,15 @@
 [DEFAULT]
 support-files =
   iframe_messageChannel_cloning.html
   iframe_messageChannel_pingpong.html
   iframe_messageChannel_post.html
   file_empty.html
+  iframe_postMessage_solidus.html
 
 [test_Image_constructor.html]
 [test_appname_override.html]
 [test_bug913761.html]
 [test_constructor-assignment.html]
 [test_constructor.html]
 [test_document.all_unqualified.html]
 [test_domcursor.html]
@@ -40,9 +41,10 @@ support-files =
 [test_window_enumeration.html]
 [test_window_extensible.html]
 [test_window_indexing.html]
 [test_writable-replaceable.html]
 [test_urlExceptions.html]
 [test_openDialogChromeOnly.html]
 [test_messagemanager_targetchain.html]
 [test_url_empty_port.html]
+[test_postMessage_solidus.html]
 [test_urlSearchParams.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_postMessage_solidus.html
@@ -0,0 +1,93 @@
+
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=949488
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 949488 - basic support</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=949488">Mozilla Bug 949488</a>
+  <div id="content"></div>
+  <script type="application/javascript">
+
+  function selfMessage() {
+    addEventListener('message', receiveMessage);
+    function receiveMessage(evt) {
+      is(evt.data, 1, "Message received");
+      removeEventListener('message', receiveMessage);
+      runTest();
+    }
+
+    postMessage(1, '/');
+  }
+
+  function frameOk() {
+    var ifr = document.createElement("iframe");
+    ifr.addEventListener("load", iframeLoaded, false);
+    ifr.setAttribute('src', "iframe_postMessage_solidus.html");
+
+    var div = document.getElementById("content");
+    div.appendChild(ifr);
+
+    function iframeLoaded() {
+      addEventListener('message', receiveMessage);
+      function receiveMessage(evt) {
+        is(evt.data, 2, "Message received");
+        removeEventListener('message', receiveMessage);
+        runTest();
+      }
+
+      ifr.contentWindow.postMessage(2, '/');
+    }
+  }
+
+  function frameWrong() {
+    var ifr = document.createElement("iframe");
+    ifr.addEventListener("load", iframeLoaded, false);
+    ifr.setAttribute('src', "http://www.example.com/tests/dom/base/test/iframe_postMessage_solidus.html");
+
+    var div = document.getElementById("content");
+    div.appendChild(ifr);
+
+    function iframeLoaded() {
+      addEventListener('message', receiveMessage);
+      function receiveMessage(evt) {
+        ok(evt.data, 3, "Message received");
+        removeEventListener('message', receiveMessage);
+        runTest();
+      }
+
+      ifr.contentWindow.postMessage(4, '/');
+      SimpleTest.executeSoon(function() {
+        ifr.contentWindow.postMessage(3, '*');
+      });
+    }
+  }
+
+  var tests = [
+    selfMessage,
+    frameOk,
+    frameWrong
+  ];
+
+  function runTest() {
+    if (!tests.length) {
+      SimpleTest.finish();
+      return;
+    }
+
+    var test = tests.shift();
+    test();
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  runTest();
+
+  </script>
+</body>
+</html>
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -65,70 +65,73 @@ CallbackObject::CallSetup::CallSetup(Cal
   // we'll need to do the script entry point bits once we have an actual
   // callable.
 
   // First, find the real underlying callback.
   JSObject* realCallback = js::UncheckedUnwrap(aCallback->CallbackPreserveColor());
   JSContext* cx = nullptr;
   nsIGlobalObject* globalObject = nullptr;
 
-  if (mIsMainThread) {
-    // Now get the global and JSContext for this callback.
-    nsGlobalWindow* win = xpc::WindowGlobalOrNull(realCallback);
-    if (win) {
-      // Make sure that if this is a window it's the current inner, since the
-      // nsIScriptContext and hence JSContext are associated with the outer
-      // window.  Which means that if someone holds on to a function from a
-      // now-unloaded document we'd have the new document as the script entry
-      // point...
-      MOZ_ASSERT(win->IsInnerWindow());
-      nsPIDOMWindow* outer = win->GetOuterWindow();
-      if (!outer || win != outer->GetCurrentInnerWindow()) {
-        // Just bail out from here
-        return;
+  {
+    JS::AutoAssertNoGC nogc;
+    if (mIsMainThread) {
+      // Now get the global and JSContext for this callback.
+      nsGlobalWindow* win = xpc::WindowGlobalOrNull(realCallback);
+      if (win) {
+        // Make sure that if this is a window it's the current inner, since the
+        // nsIScriptContext and hence JSContext are associated with the outer
+        // window.  Which means that if someone holds on to a function from a
+        // now-unloaded document we'd have the new document as the script entry
+        // point...
+        MOZ_ASSERT(win->IsInnerWindow());
+        nsPIDOMWindow* outer = win->GetOuterWindow();
+        if (!outer || win != outer->GetCurrentInnerWindow()) {
+          // Just bail out from here
+          return;
+        }
+        cx = win->GetContext() ? win->GetContext()->GetNativeContext()
+                               // This happens - Removing it causes
+                               // test_bug293235.xul to go orange.
+                               : nsContentUtils::GetSafeJSContext();
+        globalObject = win;
+      } else {
+        // No DOM Window. Store the global and use the SafeJSContext.
+        JSObject* glob = js::GetGlobalForObjectCrossCompartment(realCallback);
+        globalObject = xpc::GetNativeForGlobal(glob);
+        MOZ_ASSERT(globalObject);
+        cx = nsContentUtils::GetSafeJSContext();
       }
-      cx = win->GetContext() ? win->GetContext()->GetNativeContext()
-                             // This happens - Removing it causes
-                             // test_bug293235.xul to go orange.
-                             : nsContentUtils::GetSafeJSContext();
-      globalObject = win;
     } else {
-      // No DOM Window. Store the global and use the SafeJSContext.
-      JSObject* glob = js::GetGlobalForObjectCrossCompartment(realCallback);
-      globalObject = xpc::GetNativeForGlobal(glob);
-      MOZ_ASSERT(globalObject);
-      cx = nsContentUtils::GetSafeJSContext();
+      cx = workers::GetCurrentThreadJSContext();
+      globalObject = workers::GetCurrentThreadWorkerPrivate()->GlobalScope();
     }
-  } else {
-    cx = workers::GetCurrentThreadJSContext();
-    globalObject = workers::GetCurrentThreadWorkerPrivate()->GlobalScope();
-  }
 
-  // Bail out if there's no useful global. This seems to happen intermittently
-  // on gaia-ui tests, probably because nsInProcessTabChildGlobal is returning
-  // null in some kind of teardown state.
-  if (!globalObject->GetGlobalJSObject()) {
-    return;
-  }
+    // Bail out if there's no useful global. This seems to happen intermittently
+    // on gaia-ui tests, probably because nsInProcessTabChildGlobal is returning
+    // null in some kind of teardown state.
+    if (!globalObject->GetGlobalJSObject()) {
+      return;
+    }
 
-  mAutoEntryScript.construct(globalObject, mIsMainThread, cx);
-  if (aCallback->IncumbentGlobalOrNull()) {
-    mAutoIncumbentScript.construct(aCallback->IncumbentGlobalOrNull());
-  }
+    mAutoEntryScript.construct(globalObject, mIsMainThread, cx);
+    if (aCallback->IncumbentGlobalOrNull()) {
+      mAutoIncumbentScript.construct(aCallback->IncumbentGlobalOrNull());
+    }
 
-  // Unmark the callable (by invoking Callback() and not the CallbackPreserveColor()
-  // variant), and stick it in a Rooted before it can go gray again.
-  // Nothing before us in this function can trigger a CC, so it's safe to wait
-  // until here it do the unmark. This allows us to order the following two
-  // operations _after_ the Push() above, which lets us take advantage of the
-  // JSAutoRequest embedded in the pusher.
-  //
-  // We can do this even though we're not in the right compartment yet, because
-  // Rooted<> does not care about compartments.
-  mRootedCallable.construct(cx, aCallback->Callback());
+    // Unmark the callable (by invoking Callback() and not the CallbackPreserveColor()
+    // variant), and stick it in a Rooted before it can go gray again.
+    // Nothing before us in this function can trigger a CC, so it's safe to wait
+    // until here it do the unmark. This allows us to order the following two
+    // operations _after_ the Push() above, which lets us take advantage of the
+    // JSAutoRequest embedded in the pusher.
+    //
+    // We can do this even though we're not in the right compartment yet, because
+    // Rooted<> does not care about compartments.
+    mRootedCallable.construct(cx, aCallback->Callback());
+  }
 
   if (mIsMainThread) {
     // Check that it's ok to run this callback at all.
     // Make sure to use realCallback to get the global of the callback object,
     // not the wrapper.
     bool allowed = nsContentUtils::GetSecurityManager()->
       ScriptAllowed(js::GetGlobalForObjectCrossCompartment(realCallback));
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -858,19 +858,20 @@ ContentChild::AllocPIndexedDBChild()
 bool
 ContentChild::DeallocPIndexedDBChild(PIndexedDBChild* aActor)
 {
   delete aActor;
   return true;
 }
 
 asmjscache::PAsmJSCacheEntryChild*
-ContentChild::AllocPAsmJSCacheEntryChild(const asmjscache::OpenMode& aOpenMode,
-                                         const int64_t& aSizeToWrite,
-                                         const IPC::Principal& aPrincipal)
+ContentChild::AllocPAsmJSCacheEntryChild(
+                                    const asmjscache::OpenMode& aOpenMode,
+                                    const asmjscache::WriteParams& aWriteParams,
+                                    const IPC::Principal& aPrincipal)
 {
   NS_NOTREACHED("Should never get here!");
   return nullptr;
 }
 
 bool
 ContentChild::DeallocPAsmJSCacheEntryChild(PAsmJSCacheEntryChild* aActor)
 {
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -162,17 +162,17 @@ public:
     virtual PBluetoothChild* AllocPBluetoothChild();
     virtual bool DeallocPBluetoothChild(PBluetoothChild* aActor);
 
     virtual PFMRadioChild* AllocPFMRadioChild();
     virtual bool DeallocPFMRadioChild(PFMRadioChild* aActor);
 
     virtual PAsmJSCacheEntryChild* AllocPAsmJSCacheEntryChild(
                                  const asmjscache::OpenMode& aOpenMode,
-                                 const int64_t& aSizeToWrite,
+                                 const asmjscache::WriteParams& aWriteParams,
                                  const IPC::Principal& aPrincipal) MOZ_OVERRIDE;
     virtual bool DeallocPAsmJSCacheEntryChild(
                                     PAsmJSCacheEntryChild* aActor) MOZ_OVERRIDE;
 
     virtual PSpeechSynthesisChild* AllocPSpeechSynthesisChild();
     virtual bool DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor);
 
     virtual bool RecvRegisterChrome(const InfallibleTArray<ChromePackage>& packages,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2501,21 +2501,21 @@ ContentParent::DeallocPFMRadioParent(PFM
 #else
     NS_WARNING("No support for FMRadio on this platform!");
     return false;
 #endif
 }
 
 asmjscache::PAsmJSCacheEntryParent*
 ContentParent::AllocPAsmJSCacheEntryParent(
-                                          const asmjscache::OpenMode& aOpenMode,
-                                          const int64_t& aSizeToWrite,
-                                          const IPC::Principal& aPrincipal)
+                                    const asmjscache::OpenMode& aOpenMode,
+                                    const asmjscache::WriteParams& aWriteParams,
+                                    const IPC::Principal& aPrincipal)
 {
-  return asmjscache::AllocEntryParent(aOpenMode, aSizeToWrite, aPrincipal);
+  return asmjscache::AllocEntryParent(aOpenMode, aWriteParams, aPrincipal);
 }
 
 bool
 ContentParent::DeallocPAsmJSCacheEntryParent(PAsmJSCacheEntryParent* aActor)
 {
   asmjscache::DeallocEntryParent(aActor);
   return true;
 }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -379,17 +379,17 @@ private:
     virtual bool DeallocPBluetoothParent(PBluetoothParent* aActor);
     virtual bool RecvPBluetoothConstructor(PBluetoothParent* aActor);
 
     virtual PFMRadioParent* AllocPFMRadioParent();
     virtual bool DeallocPFMRadioParent(PFMRadioParent* aActor);
 
     virtual PAsmJSCacheEntryParent* AllocPAsmJSCacheEntryParent(
                                  const asmjscache::OpenMode& aOpenMode,
-                                 const int64_t& aSizeToWrite,
+                                 const asmjscache::WriteParams& aWriteParams,
                                  const IPC::Principal& aPrincipal) MOZ_OVERRIDE;
     virtual bool DeallocPAsmJSCacheEntryParent(
                                    PAsmJSCacheEntryParent* aActor) MOZ_OVERRIDE;
 
     virtual PSpeechSynthesisParent* AllocPSpeechSynthesisParent();
     virtual bool DeallocPSpeechSynthesisParent(PSpeechSynthesisParent* aActor);
     virtual bool RecvPSpeechSynthesisConstructor(PSpeechSynthesisParent* aActor);
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -37,16 +37,17 @@ using struct ChromePackage from "mozilla
 using struct ResourceMapping from "mozilla/chrome/RegistryMessageUtils.h";
 using struct OverrideMapping from "mozilla/chrome/RegistryMessageUtils.h";
 using base::ChildPrivileges from "base/process_util.h";
 using struct IPC::Permission from "mozilla/net/NeckoMessageUtils.h";
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
 using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 using mozilla::dom::asmjscache::OpenMode from "mozilla/dom/asmjscache/AsmJSCache.h";
+using mozilla::dom::asmjscache::WriteParams from "mozilla/dom/asmjscache/AsmJSCache.h";
 using mozilla::dom::AudioChannelType from "AudioChannelCommon.h";
 using mozilla::dom::AudioChannelState from "AudioChannelCommon.h";
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 using mozilla::hal::ProcessPriority from "mozilla/HalTypes.h";
 using gfxIntSize from "nsSize.h";
 
 namespace mozilla {
 namespace dom {
@@ -367,18 +368,17 @@ parent:
     PStorage();
 
     PTelephony();
 
     PBluetooth();
 
     PFMRadio();
 
-    PAsmJSCacheEntry(OpenMode openMode, int64_t sizeToWrite,
-                     Principal principal);
+    PAsmJSCacheEntry(OpenMode openMode, WriteParams write, Principal principal);
 
     // Services remoting
 
     async StartVisitedQuery(URIParams uri);
     async VisitURI(URIParams uri, OptionalURIParams referrer, uint32_t flags);
     async SetURITitle(URIParams uri, nsString title);
     
     // filepicker remoting
--- a/dom/tests/mochitest/localstorage/test_clear_browser_data.html
+++ b/dom/tests/mochitest/localstorage/test_clear_browser_data.html
@@ -186,17 +186,17 @@ function browserLoadEvent() {
   gBrowserStorage.localStorage = window.frames[1].frames[0].localStorage;
   gBrowserStorage.sessionStorage = window.frames[1].frames[0].sessionStorage;
 
   setupStorage(gAppStorage.localStorage);
   setupStorage(gAppStorage.sessionStorage);
   setupStorage(gBrowserStorage.localStorage);
   setupStorage(gBrowserStorage.sessionStorage);
 
-  frames[1].postMessage("clear", "http://www.example.com");
+  frames[1].postMessage("clear", "*");
 
   waitForClearBrowserData();
 };
 
 function waitForClearBrowserData() {
   SimpleTest.executeSoon(function() {
     if (frames[1].document.getElementsByTagName('done').length == 0) {
       waitForClearBrowserData();
--- a/dom/tests/mochitest/storageevent/interOriginTest2.js
+++ b/dom/tests/mochitest/storageevent/interOriginTest2.js
@@ -23,21 +23,21 @@ function onMessageReceived(event)
       if (--frameLoadsPending)
         break;
 
       // Just fall through...
 
     // Indication of successfully finished step of a test
     case "perf":
       if (callMasterFrame)
-        masterFrame.postMessage("step", masterFrameOrigin);
+        masterFrame.postMessage("step", "*");
       else if (slaveFrame)
-        slaveFrame.postMessage("step", slaveFrameOrigin);
+        slaveFrame.postMessage("step", "*");
       else if (SpecialPowers.wrap(masterFrame).slaveFrame)
-        SpecialPowers.wrap(masterFrame).slaveFrame.postMessage("step", slaveFrameOrigin);
+        SpecialPowers.wrap(masterFrame).slaveFrame.postMessage("step", "*");
       callMasterFrame = !callMasterFrame;
       break;
 
     // Indication of all test parts finish (from any of the frames)
     case "done":
       if (testDone)
         break;
 
--- a/js/src/devtools/rootAnalysis/expect.browser.json
+++ b/js/src/devtools/rootAnalysis/expect.browser.json
@@ -1,3 +1,3 @@
 {
-  "expect-hazards": 1
+  "expect-hazards": 0
 }
--- a/js/src/jit/AsmJSModule.cpp
+++ b/js/src/jit/AsmJSModule.cpp
@@ -746,21 +746,19 @@ GetCPUID(uint32_t *cpuId)
 #else
     return false;
 #endif
 }
 
 class MachineId
 {
     uint32_t cpuId_;
-    js::Vector<char> buildId_;
+    JS::BuildIdCharVector buildId_;
 
   public:
-    MachineId(ExclusiveContext *cx) : buildId_(cx) {}
-
     bool extractCurrentState(ExclusiveContext *cx) {
         if (!cx->asmJSCacheOps().buildId)
             return false;
         if (!cx->asmJSCacheOps().buildId(&buildId_))
             return false;
         if (!GetCPUID(&cpuId_))
             return false;
         return true;
@@ -935,17 +933,17 @@ struct ScopedCacheEntryOpenedForWrite
 };
 
 bool
 js::StoreAsmJSModuleInCache(AsmJSParser &parser,
                             const AsmJSModule &module,
                             const AsmJSStaticLinkData &linkData,
                             ExclusiveContext *cx)
 {
-    MachineId machineId(cx);
+    MachineId machineId;
     if (!machineId.extractCurrentState(cx))
         return false;
 
     ModuleChars moduleChars;
     if (!moduleChars.initFromParsedModule(parser, module))
         return false;
 
     size_t serializedSize = machineId.serializedSize() +
@@ -994,34 +992,34 @@ struct ScopedCacheEntryOpenedForRead
 bool
 js::LookupAsmJSModuleInCache(ExclusiveContext *cx,
                              AsmJSParser &parser,
                              ScopedJSDeletePtr<AsmJSModule> *moduleOut,
                              ScopedJSFreePtr<char> *compilationTimeReport)
 {
     int64_t usecBefore = PRMJ_Now();
 
-    MachineId machineId(cx);
+    MachineId machineId;
     if (!machineId.extractCurrentState(cx))
         return true;
 
     JS::OpenAsmJSCacheEntryForReadOp open = cx->asmJSCacheOps().openEntryForRead;
     if (!open)
         return true;
 
     const jschar *begin = parser.tokenStream.rawBase() + ModuleChars::beginOffset(parser);
     const jschar *limit = parser.tokenStream.rawLimit();
 
     ScopedCacheEntryOpenedForRead entry(cx);
     if (!open(cx->global(), begin, limit, &entry.serializedSize, &entry.memory, &entry.handle))
         return true;
 
     const uint8_t *cursor = entry.memory;
 
-    MachineId cachedMachineId(cx);
+    MachineId cachedMachineId;
     cursor = cachedMachineId.deserialize(cx, cursor);
     if (!cursor)
         return false;
     if (machineId != cachedMachineId)
         return true;
 
     ModuleChars moduleChars;
     cursor = moduleChars.deserialize(cx, cursor);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4678,23 +4678,26 @@ typedef void
  */
 typedef bool
 (* OpenAsmJSCacheEntryForWriteOp)(HandleObject global, const jschar *begin, const jschar *end,
                                   size_t size, uint8_t **memory, intptr_t *handle);
 typedef void
 (* CloseAsmJSCacheEntryForWriteOp)(HandleObject global, size_t size, uint8_t *memory,
                                    intptr_t handle);
 
+typedef js::Vector<char, 0, js::SystemAllocPolicy> BuildIdCharVector;
+
 // Return the buildId (represented as a sequence of characters) associated with
 // the currently-executing build. If the JS engine is embedded such that a
 // single cache entry can be observed by different compiled versions of the JS
 // engine, it is critical that the buildId shall change for each new build of
 // the JS engine.
+
 typedef bool
-(* BuildIdOp)(js::Vector<char> *buildId);
+(* BuildIdOp)(BuildIdCharVector *buildId);
 
 struct AsmJSCacheOps
 {
     OpenAsmJSCacheEntryForReadOp openEntryForRead;
     CloseAsmJSCacheEntryForReadOp closeEntryForRead;
     OpenAsmJSCacheEntryForWriteOp openEntryForWrite;
     CloseAsmJSCacheEntryForWriteOp closeEntryForWrite;
     BuildIdOp buildId;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5231,17 +5231,17 @@ ShellCloseAsmJSCacheEntryForWrite(Handle
 #endif
 
     JS_ASSERT(jsCacheOpened == true);
     jsCacheOpened = false;
     close(handle);
 }
 
 static bool
-ShellBuildId(js::Vector<char> *buildId)
+ShellBuildId(JS::BuildIdCharVector *buildId)
 {
     // The browser embeds the date into the buildid and the buildid is embedded
     // in the binary, so every 'make' necessarily builds a new firefox binary.
     // Fortunately, the actual firefox executable is tiny -- all the code is in
     // libxul.so and other shared modules -- so this isn't a big deal. Not so
     // for the statically-linked JS shell. To avoid recompmiling js.cpp and
     // re-linking 'js' on every 'make', we use a constant buildid and rely on
     // the shell user to manually clear the cache (deleting the dir passed to
--- a/js/xpconnect/tests/mochitest/mochitest.ini
+++ b/js/xpconnect/tests/mochitest/mochitest.ini
@@ -3,17 +3,16 @@ support-files =
   bug500931_helper.html
   bug504877_helper.html
   bug571849_helper.html
   bug589028_helper.html
   bug92773_helper.html
   chrome_wrappers_helper.html
   file1_bug629227.html
   file2_bug629227.html
-  file_asmjs.js
   file_bug505915.html
   file_bug650273.html
   file_bug658560.html
   file_bug706301.html
   file_bug720619.html
   file_bug738244.html
   file_bug760131.html
   file_bug781476.html
@@ -31,21 +30,16 @@ support-files =
   file_expandosharing.html
   file_mozMatchesSelector.html
   file_nodelists.html
   file_wrappers-2.html
   inner.html
   test1_bug629331.html
   test2_bug629331.html
 
-[test_asmjs.html]
-# bug 929498
-skip-if = os == 'android'
-[test_asmjs2.html]
-[test_asmjs3.html]
 [test_bug384632.html]
 [test_bug390488.html]
 [test_bug393269.html]
 [test_bug396851.html]
 [test_bug428021.html]
 [test_bug446584.html]
 [test_bug462428.html]
 [test_bug478438.html]
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -1020,25 +1020,30 @@ nsDocumentViewer::LoadComplete(nsresult 
       bool isInUnload;
       if (docShell && NS_SUCCEEDED(docShell->GetIsInUnload(&isInUnload)) &&
           !isInUnload) {
         mDocument->OnPageShow(restoring, nullptr);
       }
     }
   }
 
-  // Now that the document has loaded, we can tell the presshell
-  // to unsuppress painting.
-  if (mPresShell && !mStopped) {
-    nsCOMPtr<nsIPresShell> shellDeathGrip(mPresShell);
-    mPresShell->UnsuppressPainting();
-    // mPresShell could have been removed now, see bug 378682/421432
+  if (!mStopped) {
+    if (mDocument) {
+      mDocument->ScrollToRef();
+    }
+
+    // Now that the document has loaded, we can tell the presshell
+    // to unsuppress painting.
     if (mPresShell) {
-      mPresShell->ScrollToAnchor();
-      mPresShell->LoadComplete();
+      nsCOMPtr<nsIPresShell> shellDeathGrip(mPresShell);
+      mPresShell->UnsuppressPainting();
+      // mPresShell could have been removed now, see bug 378682/421432
+      if (mPresShell) {
+        mPresShell->LoadComplete();
+      }
     }
   }
 
   nsJSContext::LoadEnd();
 
 #ifdef NS_PRINTING
   // Check to see if someone tried to print during the load
   if (mPrintIsPending) {
new file mode 100644
--- /dev/null
+++ b/layout/reftests/scrolling/deferred-anchor-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta charset="UTF-8" />
+</head>
+<body style="margin: 0;">
+<div>
+<div style="height: 50px; width: 50px; background-color: red;"></div>
+<div style="height: 1000px;"></div>
+<div id="d" style="height: 50px; width: 50px; background-color: green;"></div>
+<div style="height: 1000px;"></div>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/scrolling/deferred-anchor.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta charset="UTF-8" />
+<script>
+var xhr = new XMLHttpRequest();
+xhr.onload = function() {
+  document.body.appendChild(document.importNode(this.responseXML.getElementsByTagName("div")[0], true));
+};
+xhr.open("get", "deferred-anchor-ref.html");
+xhr.responseType = "document";
+xhr.send();
+</script>
+</head>
+<body style="margin: 0;">
+</body>
+</html>
--- a/layout/reftests/scrolling/reftest.list
+++ b/layout/reftests/scrolling/reftest.list
@@ -1,8 +1,9 @@
+== deferred-anchor.html#d deferred-anchor-ref.html#d
 HTTP == fixed-1.html fixed-1.html?ref
 HTTP == fixed-opacity-1.html fixed-opacity-1.html?ref
 skip-if(B2G) HTTP == fixed-opacity-2.html fixed-opacity-2.html?ref
 skip-if(B2G) random-if(gtk2Widget) fuzzy-if(Android,3,60) HTTP == fixed-text-1.html fixed-text-1.html?ref
 HTTP == fixed-text-2.html fixed-text-2.html?ref
 random-if(Android&&!browserIsRemote) == iframe-border-radius.html iframe-border-radius-ref.html # bug 760269
 random-if(Android) HTTP == image-1.html image-1.html?ref
 random-if(Android&&!browserIsRemote) HTTP == opacity-mixed-scrolling-1.html opacity-mixed-scrolling-1.html?ref # bug 760269
--- a/netwerk/protocol/http/nsHttpConnectionInfo.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.cpp
@@ -11,18 +11,17 @@
 #include "mozilla/net/DNS.h"
 #include "prnetdb.h"
 
 using namespace mozilla::net;
 
 nsHttpConnectionInfo::nsHttpConnectionInfo(const nsACString &host, int32_t port,
                                            nsProxyInfo* proxyInfo,
                                            bool usingSSL)
-    : mRef(0)
-    , mProxyInfo(proxyInfo)
+    : mProxyInfo(proxyInfo)
     , mUsingSSL(usingSSL)
     , mUsingConnect(false)
 {
     LOG(("Creating nsHttpConnectionInfo @%x\n", this));
 
     mUsingHttpProxy = (proxyInfo && proxyInfo->IsHTTP());
 
     if (mUsingHttpProxy) {
--- a/netwerk/protocol/http/nsHttpConnectionInfo.h
+++ b/netwerk/protocol/http/nsHttpConnectionInfo.h
@@ -20,37 +20,21 @@ extern PRLogModuleInfo *gHttpLog;
 
 class nsHttpConnectionInfo
 {
 public:
     nsHttpConnectionInfo(const nsACString &host, int32_t port,
                          nsProxyInfo* proxyInfo,
                          bool usingSSL=false);
 
-   ~nsHttpConnectionInfo()
+    virtual ~nsHttpConnectionInfo()
     {
         PR_LOG(gHttpLog, 4, ("Destroying nsHttpConnectionInfo @%x\n", this));
     }
 
-    nsrefcnt AddRef()
-    {
-        nsrefcnt n = ++mRef;
-        NS_LOG_ADDREF(this, n, "nsHttpConnectionInfo", sizeof(*this));
-        return n;
-    }
-
-    nsrefcnt Release()
-    {
-        nsrefcnt n = --mRef;
-        NS_LOG_RELEASE(this, n, "nsHttpConnectionInfo");
-        if (n == 0)
-            delete this;
-        return n;
-    }
-
     const nsAFlatCString &HashKey() const { return mHashKey; }
 
     void SetOriginServer(const nsACString &host, int32_t port);
 
     void SetOriginServer(const char *host, int32_t port)
     {
         SetOriginServer(nsDependentCString(host), port);
     }
@@ -91,19 +75,21 @@ public:
 
     // Returns true for any kind of proxy (http, socks, etc..)
     bool UsingProxy();
 
     // Returns true when mHost is an RFC1918 literal.
     bool HostIsLocalIPLiteral() const;
 
 private:
-    mozilla::ThreadSafeAutoRefCnt mRef;
     nsCString              mHashKey;
     nsCString              mHost;
     int32_t                mPort;
     nsCOMPtr<nsProxyInfo>  mProxyInfo;
     bool                   mUsingHttpProxy;
     bool                   mUsingSSL;
     bool                   mUsingConnect;  // if will use CONNECT with http proxy
+
+// for nsRefPtr
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsHttpConnectionInfo)
 };
 
 #endif // nsHttpConnectionInfo_h__