diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index e26bfcab85e..1d95fbdd34f 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "d5d2b5614c32e17c5c3c736181a66bfa3fc5c3ef", + "revision": "9960daf91c384990c4bbe7c1221905db7536596b", "repo_path": "/integration/gaia-central" } diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 955c90bff84..56602282b99 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -8821,6 +8821,10 @@ void nsDocument::ScrollToRef() { if (mScrolledToRefAlready) { + nsCOMPtr shell = GetShell(); + if (shell) { + shell->ScrollToAnchor(); + } return; } diff --git a/dom/asmjscache/AsmJSCache.cpp b/dom/asmjscache/AsmJSCache.cpp index 8f9e95cc0c2..98f182922b7 100644 --- a/dom/asmjscache/AsmJSCache.cpp +++ b/dom/asmjscache/AsmJSCache.cpp @@ -10,6 +10,7 @@ #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" @@ -20,11 +21,13 @@ #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" @@ -33,7 +36,8 @@ #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; @@ -42,8 +46,12 @@ 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 { @@ -55,6 +63,81 @@ 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(¤tBuildId); + 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, @@ -309,10 +392,10 @@ public: // 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) { @@ -326,9 +409,22 @@ public: } 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() { @@ -342,16 +438,22 @@ protected: 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(): @@ -374,14 +476,37 @@ private: 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; @@ -389,12 +514,23 @@ private: nsCString mOrigin; nsCString mStorageId; + // State initialized during eReadyToReadMetadata + nsCOMPtr mDirectory; + nsCOMPtr 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 @@ -417,70 +553,178 @@ MainProcessRunnable::InitOnMainThread() 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 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 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); - mQuotaObject = qm->GetQuotaObject(quota::PERSISTENCE_TYPE_TEMPORARY, - mGroup, mOrigin, path); - NS_ENSURE_STATE(mQuotaObject); + rv = mMetadataFile->Append(NS_LITERAL_STRING(ASMJSCACHE_METADATA_FILE_NAME)); + NS_ENSURE_SUCCESS(rv, rv); - int32_t openFlags; - if (mOpenMode == eOpenForRead) { - rv = path->GetFileSize(&mFileSize); - if (NS_FAILED(rv)) { - return rv; - } + rv = mMetadataFile->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); - openFlags = PR_RDONLY | nsIFile::OS_READAHEAD; - } else { - if (!mQuotaObject->MaybeAllocateMoreSpace(0, mSizeToWrite)) { - return NS_ERROR_FAILURE; - } - - mFileSize = mSizeToWrite; - - MOZ_ASSERT(mOpenMode == eOpenForWrite); - openFlags = PR_RDWR | PR_TRUNCATE | PR_CREATE_FILE; + if (exists && NS_FAILED(ReadMetadataFile(mMetadataFile, mMetadata))) { + exists = false; } - rv = path->OpenNSPRFileDesc(openFlags, 0644, &mFileDesc); + if (!exists) { + // If we are reading, we can't possibly have a cache hit. + if (mOpenMode == eOpenForRead) { + return NS_ERROR_FILE_NOT_FOUND; + } + + // 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; + } + } + + return NS_OK; +} + +nsresult +GetCacheFile(nsIFile* aDirectory, unsigned aModuleIndex, nsIFile** aCacheFile) +{ + nsCOMPtr 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 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 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; @@ -522,7 +766,7 @@ MainProcessRunnable::Run() return NS_OK; } - mState = eWaitingToOpen; + mState = eWaitingToOpenMetadata; rv = QuotaManager::Get()->WaitForOpenAllowed( OriginOrPatternString::FromOrigin(mOrigin), Nullable(), mStorageId, @@ -536,45 +780,69 @@ MainProcessRunnable::Run() return NS_OK; } - case eWaitingToOpen: { + case eWaitingToOpenMetadata: { MOZ_ASSERT(NS_IsMainThread()); - mState = eReadyToOpen; - - QuotaManager* qm = QuotaManager::Get(); - if (!qm) { - Fail(); - return NS_OK; - } - - rv = qm->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL); - if (NS_FAILED(rv)) { - Fail(); - return NS_OK; - } - + mState = eReadyToReadMetadata; + DispatchToIOThread(); return NS_OK; } - case eReadyToOpen: { + case eReadyToReadMetadata: { AssertIsOnIOThread(); - rv = OpenFileOnIOThread(); + rv = ReadMetadata(); if (NS_FAILED(rv)) { Fail(); return NS_OK; } - mState = eNotifying; + 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 eNotifying: { + 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 = OpenCacheFileForRead(); + if (NS_FAILED(rv)) { + Fail(); + return NS_OK; + } + + mState = eSendingCacheFile; + NS_DispatchToMainThread(this); + return NS_OK; + } + + case eSendingCacheFile: { MOZ_ASSERT(NS_IsMainThread()); mState = eOpened; - OnOpen(); + OnOpenCacheFile(); return NS_OK; } @@ -594,6 +862,7 @@ MainProcessRunnable::Run() return NS_OK; } + case eWaitingToOpenCacheFileForRead: case eOpened: case eFinished: { MOZ_ASSUME_UNREACHABLE("Shouldn't Run() in this state"); @@ -604,6 +873,50 @@ MainProcessRunnable::Run() 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 @@ -616,8 +929,10 @@ public: // 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()); @@ -631,17 +946,29 @@ public: private: void - Close() MOZ_OVERRIDE MOZ_FINAL + OnOpenMetadataForRead(const Metadata& aMetadata) MOZ_OVERRIDE { - MainProcessRunnable::Close(); + uint32_t moduleIndex; + if (!FindHashMatch(aMetadata, mReadParams, &moduleIndex)) { + MainProcessRunnable::Fail(); + return; + } + + MainProcessRunnable::OpenForRead(moduleIndex); } void - OnOpen() MOZ_OVERRIDE + OnOpenCacheFile() MOZ_OVERRIDE { File::OnOpen(); } + void + Close() MOZ_OVERRIDE MOZ_FINAL + { + MainProcessRunnable::Close(); + } + void OnFailure() MOZ_OVERRIDE { @@ -662,6 +989,8 @@ private: { return MainProcessRunnable::Run(); } + + ReadParams mReadParams; }; // A runnable that executes in a parent process for a cache access originating @@ -676,8 +1005,8 @@ public: // 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), @@ -732,7 +1061,24 @@ private: } 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()); @@ -741,7 +1087,7 @@ private: FileDescriptor::PlatformHandleType handle = FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(mFileDesc)); - if (!SendOnOpen(mFileSize, handle)) { + if (!SendOnOpenCacheFile(mFileSize, handle)) { unused << Send__delete__(this); } } @@ -788,11 +1134,11 @@ private: 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(); @@ -825,10 +1171,12 @@ public: // 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) { @@ -846,7 +1194,24 @@ public: 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); @@ -869,8 +1234,7 @@ private: MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mState == eOpening); - mState = eFinished; - File::OnFailure(); + Fail(); return true; } @@ -891,9 +1255,20 @@ private: } 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 { @@ -918,7 +1293,7 @@ ChildProcessRunnable::Run() 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 @@ -926,8 +1301,7 @@ ChildProcessRunnable::Run() // 'this' alive until returning to the event loop. Release(); - mState = eFinished; - File::OnFailure(); + Fail(); return NS_OK; } @@ -976,10 +1350,12 @@ 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 @@ -1002,9 +1378,11 @@ OpenFile(nsIPrincipal* aPrincipal, // child can then map the file into its address space to perform I/O. nsRefPtr 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)) { @@ -1019,10 +1397,6 @@ OpenFile(nsIPrincipal* aPrincipal, 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, @@ -1035,8 +1409,13 @@ OpenEntryForRead(nsIPrincipal* aPrincipal, 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; } @@ -1092,8 +1471,17 @@ OpenEntryForWrite(nsIPrincipal* aPrincipal, // 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; } @@ -1128,7 +1516,7 @@ CloseEntryForWrite(JS::Handle global, } bool -GetBuildId(js::Vector* aBuildID) +GetBuildId(JS::BuildIdCharVector* aBuildID) { nsCOMPtr info = do_GetService("@mozilla.org/xre/app-info;1"); if (!info) { @@ -1192,19 +1580,24 @@ public: rv = directory->Append(NS_LITERAL_STRING(ASMJSCACHE_DIRECTORY_NAME)); NS_ENSURE_SUCCESS(rv, rv); - bool exists; + DebugOnly exists; MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)) && exists); - nsIFile* path = directory; - rv = path->Append(NS_LITERAL_STRING(ASMJSCACHE_FILE_NAME)); + nsCOMPtr 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 entry; + rv = entries->GetNext(getter_AddRefs(entry)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr file = do_QueryInterface(entry); + NS_ENSURE_TRUE(file, NS_NOINTERFACE); - if (exists) { int64_t fileSize; - rv = path->GetFileSize(&fileSize); + rv = file->GetFileSize(&fileSize); NS_ENSURE_SUCCESS(rv, rv); MOZ_ASSERT(fileSize >= 0, "Negative size?!"); @@ -1279,3 +1672,79 @@ CreateClient() } // namespace asmjscache } // namespace dom } // namespace mozilla + +namespace IPC { + +using mozilla::dom::asmjscache::Metadata; +using mozilla::dom::asmjscache::WriteParams; + +void +ParamTraits::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::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::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::Write(Message* aMsg, const paramType& aParam) +{ + WriteParam(aMsg, aParam.mSize); + WriteParam(aMsg, aParam.mFastHash); + WriteParam(aMsg, aParam.mNumChars); + WriteParam(aMsg, aParam.mFullHash); +} + +bool +ParamTraits::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::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 diff --git a/dom/asmjscache/AsmJSCache.h b/dom/asmjscache/AsmJSCache.h index 676c9794cd4..846f1252f44 100644 --- a/dom/asmjscache/AsmJSCache.h +++ b/dom/asmjscache/AsmJSCache.h @@ -10,6 +10,7 @@ #include "ipc/IPCMessageUtils.h" #include "js/TypeDecls.h" #include "js/Vector.h" +#include "jsapi.h" class nsIPrincipal; @@ -32,6 +33,54 @@ enum OpenMode 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. // @@ -68,8 +117,9 @@ CloseEntryForWrite(JS::Handle aGlobal, size_t aSize, uint8_t* aMemory, intptr_t aHandle); + bool -GetBuildId(js::Vector* aBuildId); +GetBuildId(JS::BuildIdCharVector* aBuildId); // Called from QuotaManager.cpp: @@ -79,7 +129,7 @@ CreateClient(); // Called from ipc/ContentParent.cpp: PAsmJSCacheEntryParent* -AllocEntryParent(OpenMode aOpenMode, uint32_t aSizeToWrite, +AllocEntryParent(OpenMode aOpenMode, WriteParams aWriteParams, nsIPrincipal* aPrincipal); void @@ -103,6 +153,24 @@ struct ParamTraits : mozilla::dom::asmjscache::NUM_OPEN_MODES> { }; +template <> +struct ParamTraits +{ + 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 +{ + 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 diff --git a/dom/asmjscache/PAsmJSCacheEntry.ipdl b/dom/asmjscache/PAsmJSCacheEntry.ipdl index 9961c8784bd..60ff03fb6e8 100644 --- a/dom/asmjscache/PAsmJSCacheEntry.ipdl +++ b/dom/asmjscache/PAsmJSCacheEntry.ipdl @@ -4,6 +4,8 @@ include protocol PContent; +using mozilla::dom::asmjscache::Metadata from "mozilla/dom/asmjscache/AsmJSCache.h"; + namespace mozilla { namespace dom { namespace asmjscache { @@ -12,8 +14,18 @@ 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__(); diff --git a/dom/asmjscache/moz.build b/dom/asmjscache/moz.build index 6840ec81150..7a9293b0530 100644 --- a/dom/asmjscache/moz.build +++ b/dom/asmjscache/moz.build @@ -4,6 +4,8 @@ # 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' ] diff --git a/js/xpconnect/tests/mochitest/file_asmjs.js b/dom/asmjscache/test/file_slow.js similarity index 100% rename from js/xpconnect/tests/mochitest/file_asmjs.js rename to dom/asmjscache/test/file_slow.js diff --git a/dom/asmjscache/test/mochitest.ini b/dom/asmjscache/test/mochitest.ini new file mode 100644 index 00000000000..a4f8dba7e2e --- /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' diff --git a/dom/asmjscache/test/moz.build b/dom/asmjscache/test/moz.build new file mode 100644 index 00000000000..3cf16eb0cfe --- /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'] + diff --git a/js/xpconnect/tests/mochitest/test_asmjs2.html b/dom/asmjscache/test/test_cachingBasic.html similarity index 100% rename from js/xpconnect/tests/mochitest/test_asmjs2.html rename to dom/asmjscache/test/test_cachingBasic.html diff --git a/dom/asmjscache/test/test_cachingMulti.html b/dom/asmjscache/test/test_cachingMulti.html new file mode 100644 index 00000000000..358d3740788 --- /dev/null +++ b/dom/asmjscache/test/test_cachingMulti.html @@ -0,0 +1,77 @@ + + + + + + asm.js browser tests + + + + + asm.js browser tests +

+ +

+
+  
+
+
+
+
diff --git a/js/xpconnect/tests/mochitest/test_asmjs.html b/dom/asmjscache/test/test_slow.html
similarity index 81%
rename from js/xpconnect/tests/mochitest/test_asmjs.html
rename to dom/asmjscache/test/test_slow.html
index de0a4ce4e3e..0fa6fd658df 100644
--- a/js/xpconnect/tests/mochitest/test_asmjs.html
+++ b/dom/asmjscache/test/test_slow.html
@@ -20,10 +20,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=854209
   ok(jsFuns.isAsmJSCompilationAvailable(), "asm.js compilation is available");
   
 
-  
+  
 
   
+
+
+
+
diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini
index ab46010005f..61a915c49f9 100644
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -4,6 +4,7 @@ support-files =
   iframe_messageChannel_pingpong.html
   iframe_messageChannel_post.html
   file_empty.html
+  iframe_postMessage_solidus.html
 
 [test_Image_constructor.html]
 [test_appname_override.html]
@@ -45,4 +46,5 @@ support-files =
 [test_openDialogChromeOnly.html]
 [test_messagemanager_targetchain.html]
 [test_url_empty_port.html]
+[test_postMessage_solidus.html]
 [test_urlSearchParams.html]
diff --git a/dom/base/test/test_postMessage_solidus.html b/dom/base/test/test_postMessage_solidus.html
new file mode 100644
index 00000000000..ab3e4ecec46
--- /dev/null
+++ b/dom/base/test/test_postMessage_solidus.html
@@ -0,0 +1,93 @@
+
+
+
+
+
+  
+  Test for Bug 949488 - basic support
+  
+  
+
+
+  Mozilla Bug 949488
+  
+ + + diff --git a/dom/bindings/CallbackObject.cpp b/dom/bindings/CallbackObject.cpp index 4035c06b0cb..edefe248b30 100644 --- a/dom/bindings/CallbackObject.cpp +++ b/dom/bindings/CallbackObject.cpp @@ -70,60 +70,63 @@ CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback, 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. diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 2fa566d9948..4e19a00d53f 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -863,9 +863,10 @@ ContentChild::DeallocPIndexedDBChild(PIndexedDBChild* aActor) } 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; diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index a47156e2faa..16670e0d0b8 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -167,7 +167,7 @@ public: 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; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 5cbbf75a23c..55ff075ade6 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -2506,11 +2506,11 @@ ContentParent::DeallocPFMRadioParent(PFMRadioParent* aActor) 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 diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 4ab9a8f9558..f2fab9cf3c6 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -384,7 +384,7 @@ private: 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; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 42406512914..a5ca1aec153 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -42,6 +42,7 @@ 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"; @@ -372,8 +373,7 @@ parent: PFMRadio(); - PAsmJSCacheEntry(OpenMode openMode, int64_t sizeToWrite, - Principal principal); + PAsmJSCacheEntry(OpenMode openMode, WriteParams write, Principal principal); // Services remoting diff --git a/dom/tests/mochitest/localstorage/test_clear_browser_data.html b/dom/tests/mochitest/localstorage/test_clear_browser_data.html index 26a48a06872..3444b0dc8d2 100644 --- a/dom/tests/mochitest/localstorage/test_clear_browser_data.html +++ b/dom/tests/mochitest/localstorage/test_clear_browser_data.html @@ -191,7 +191,7 @@ function browserLoadEvent() { setupStorage(gBrowserStorage.localStorage); setupStorage(gBrowserStorage.sessionStorage); - frames[1].postMessage("clear", "http://www.example.com"); + frames[1].postMessage("clear", "*"); waitForClearBrowserData(); }; diff --git a/dom/tests/mochitest/storageevent/interOriginTest2.js b/dom/tests/mochitest/storageevent/interOriginTest2.js index 73b04b0fdd3..939462a4c69 100644 --- a/dom/tests/mochitest/storageevent/interOriginTest2.js +++ b/dom/tests/mochitest/storageevent/interOriginTest2.js @@ -28,11 +28,11 @@ function onMessageReceived(event) // 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; diff --git a/js/src/devtools/rootAnalysis/expect.browser.json b/js/src/devtools/rootAnalysis/expect.browser.json index 61c385a03ba..06f2beb36f7 100644 --- 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 } diff --git a/js/src/jit/AsmJSModule.cpp b/js/src/jit/AsmJSModule.cpp index 712ede098fc..0d2dee0619a 100644 --- a/js/src/jit/AsmJSModule.cpp +++ b/js/src/jit/AsmJSModule.cpp @@ -751,11 +751,9 @@ GetCPUID(uint32_t *cpuId) class MachineId { uint32_t cpuId_; - js::Vector buildId_; + JS::BuildIdCharVector buildId_; public: - MachineId(ExclusiveContext *cx) : buildId_(cx) {} - bool extractCurrentState(ExclusiveContext *cx) { if (!cx->asmJSCacheOps().buildId) return false; @@ -940,7 +938,7 @@ js::StoreAsmJSModuleInCache(AsmJSParser &parser, const AsmJSStaticLinkData &linkData, ExclusiveContext *cx) { - MachineId machineId(cx); + MachineId machineId; if (!machineId.extractCurrentState(cx)) return false; @@ -999,7 +997,7 @@ js::LookupAsmJSModuleInCache(ExclusiveContext *cx, { int64_t usecBefore = PRMJ_Now(); - MachineId machineId(cx); + MachineId machineId; if (!machineId.extractCurrentState(cx)) return true; @@ -1016,7 +1014,7 @@ js::LookupAsmJSModuleInCache(ExclusiveContext *cx, const uint8_t *cursor = entry.memory; - MachineId cachedMachineId(cx); + MachineId cachedMachineId; cursor = cachedMachineId.deserialize(cx, cursor); if (!cursor) return false; diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 844bb380583..e9d058517fc 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -4683,13 +4683,16 @@ typedef void (* CloseAsmJSCacheEntryForWriteOp)(HandleObject global, size_t size, uint8_t *memory, intptr_t handle); +typedef js::Vector 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 *buildId); +(* BuildIdOp)(BuildIdCharVector *buildId); struct AsmJSCacheOps { diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index b2195c4274b..52a644a1864 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -5236,7 +5236,7 @@ ShellCloseAsmJSCacheEntryForWrite(HandleObject global, size_t serializedSize, ui } static bool -ShellBuildId(js::Vector *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. diff --git a/js/xpconnect/tests/mochitest/mochitest.ini b/js/xpconnect/tests/mochitest/mochitest.ini index 3561526282f..a721f46b25f 100644 --- a/js/xpconnect/tests/mochitest/mochitest.ini +++ b/js/xpconnect/tests/mochitest/mochitest.ini @@ -8,7 +8,6 @@ support-files = chrome_wrappers_helper.html file1_bug629227.html file2_bug629227.html - file_asmjs.js file_bug505915.html file_bug650273.html file_bug658560.html @@ -36,11 +35,6 @@ support-files = 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] diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index af550246c11..3c6097c2bee 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -1025,15 +1025,20 @@ nsDocumentViewer::LoadComplete(nsresult aStatus) } } - // Now that the document has loaded, we can tell the presshell - // to unsuppress painting. - if (mPresShell && !mStopped) { - nsCOMPtr 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 shellDeathGrip(mPresShell); + mPresShell->UnsuppressPainting(); + // mPresShell could have been removed now, see bug 378682/421432 + if (mPresShell) { + mPresShell->LoadComplete(); + } } } diff --git a/layout/reftests/scrolling/deferred-anchor-ref.html b/layout/reftests/scrolling/deferred-anchor-ref.html new file mode 100644 index 00000000000..0e3c5ab149f --- /dev/null +++ b/layout/reftests/scrolling/deferred-anchor-ref.html @@ -0,0 +1,14 @@ + + + + + + +
+
+
+
+
+
+ + diff --git a/layout/reftests/scrolling/deferred-anchor.html b/layout/reftests/scrolling/deferred-anchor.html new file mode 100644 index 00000000000..8d44e51dd6d --- /dev/null +++ b/layout/reftests/scrolling/deferred-anchor.html @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/layout/reftests/scrolling/reftest.list b/layout/reftests/scrolling/reftest.list index bc15907d4b7..30dc5d38c0f 100644 --- a/layout/reftests/scrolling/reftest.list +++ b/layout/reftests/scrolling/reftest.list @@ -1,3 +1,4 @@ +== 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 diff --git a/netwerk/protocol/http/nsHttpConnectionInfo.cpp b/netwerk/protocol/http/nsHttpConnectionInfo.cpp index 42d1a793469..dc21ece25bf 100644 --- a/netwerk/protocol/http/nsHttpConnectionInfo.cpp +++ b/netwerk/protocol/http/nsHttpConnectionInfo.cpp @@ -16,8 +16,7 @@ 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) { diff --git a/netwerk/protocol/http/nsHttpConnectionInfo.h b/netwerk/protocol/http/nsHttpConnectionInfo.h index 128f94b2911..3f8032fbe59 100644 --- a/netwerk/protocol/http/nsHttpConnectionInfo.h +++ b/netwerk/protocol/http/nsHttpConnectionInfo.h @@ -25,27 +25,11 @@ public: 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); @@ -96,7 +80,6 @@ public: bool HostIsLocalIPLiteral() const; private: - mozilla::ThreadSafeAutoRefCnt mRef; nsCString mHashKey; nsCString mHost; int32_t mPort; @@ -104,6 +87,9 @@ private: bool mUsingHttpProxy; bool mUsingSSL; bool mUsingConnect; // if will use CONNECT with http proxy + +// for nsRefPtr + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsHttpConnectionInfo) }; #endif // nsHttpConnectionInfo_h__