mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
Bug 1361900: Part 6 - Add content process support for the script preloader. r=erahm,gabor
MozReview-Commit-ID: 6hDQAI52bKC --HG-- extra : rebase_source : 46e8d5ed5965e483fa4b0bd8f6f4403e0bb771dc extra : source : 4227bcda00babd2d5980dca3b42cb4b467da38c4
This commit is contained in:
parent
e3992a28fd
commit
589a82d7ba
@ -59,6 +59,7 @@
|
||||
#include "mozilla/layers/ContentProcessController.h"
|
||||
#include "mozilla/layers/ImageBridgeChild.h"
|
||||
#include "mozilla/layout/RenderFrameChild.h"
|
||||
#include "mozilla/loader/ScriptCacheActors.h"
|
||||
#include "mozilla/net/NeckoChild.h"
|
||||
#include "mozilla/net/CaptivePortalService.h"
|
||||
#include "mozilla/Omnijar.h"
|
||||
@ -235,6 +236,7 @@ using namespace mozilla::widget;
|
||||
using namespace mozilla::system;
|
||||
#endif
|
||||
using namespace mozilla::widget;
|
||||
using mozilla::loader::PScriptCacheChild;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -1820,6 +1822,31 @@ ContentChild::RecvPTestShellConstructor(PTestShellChild* actor)
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
PScriptCacheChild*
|
||||
ContentChild::AllocPScriptCacheChild(const FileDescOrError& cacheFile, const bool& wantCacheData)
|
||||
{
|
||||
return new loader::ScriptCacheChild();
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::DeallocPScriptCacheChild(PScriptCacheChild* cache)
|
||||
{
|
||||
delete static_cast<loader::ScriptCacheChild*>(cache);
|
||||
return true;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ContentChild::RecvPScriptCacheConstructor(PScriptCacheChild* actor, const FileDescOrError& cacheFile, const bool& wantCacheData)
|
||||
{
|
||||
Maybe<FileDescriptor> fd;
|
||||
if (cacheFile.type() == cacheFile.TFileDescriptor) {
|
||||
fd.emplace(cacheFile.get_FileDescriptor());
|
||||
}
|
||||
|
||||
static_cast<loader::ScriptCacheChild*>(actor)->Init(fd, wantCacheData);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
PNeckoChild*
|
||||
ContentChild::AllocPNeckoChild()
|
||||
{
|
||||
|
@ -36,6 +36,8 @@ struct LookAndFeelInt;
|
||||
namespace mozilla {
|
||||
class RemoteSpellcheckEngineChild;
|
||||
|
||||
using mozilla::loader::PScriptCacheChild;
|
||||
|
||||
namespace ipc {
|
||||
class OptionalURIParams;
|
||||
class URIParams;
|
||||
@ -241,6 +243,17 @@ public:
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvPTestShellConstructor(PTestShellChild*) override;
|
||||
|
||||
virtual PScriptCacheChild*
|
||||
AllocPScriptCacheChild(const FileDescOrError& cacheFile,
|
||||
const bool& wantCacheData) override;
|
||||
|
||||
virtual bool DeallocPScriptCacheChild(PScriptCacheChild*) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult
|
||||
RecvPScriptCacheConstructor(PScriptCacheChild*,
|
||||
const FileDescOrError& cacheFile,
|
||||
const bool& wantCacheData) override;
|
||||
|
||||
jsipc::CPOWManager* GetCPOWManager() override;
|
||||
|
||||
virtual PNeckoChild* AllocPNeckoChild() override;
|
||||
|
@ -85,6 +85,7 @@
|
||||
#include "mozilla/layers/ImageBridgeParent.h"
|
||||
#include "mozilla/layers/LayerTreeOwnerTracker.h"
|
||||
#include "mozilla/layout/RenderFrameParent.h"
|
||||
#include "mozilla/loader/ScriptCacheActors.h"
|
||||
#include "mozilla/LookAndFeel.h"
|
||||
#include "mozilla/media/MediaParent.h"
|
||||
#include "mozilla/Move.h"
|
||||
@ -94,6 +95,7 @@
|
||||
#include "mozilla/ProcessHangMonitor.h"
|
||||
#include "mozilla/ProcessHangMonitorIPC.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/ScriptPreloader.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
@ -284,6 +286,7 @@ using namespace mozilla::net;
|
||||
using namespace mozilla::jsipc;
|
||||
using namespace mozilla::psm;
|
||||
using namespace mozilla::widget;
|
||||
using mozilla::loader::PScriptCacheParent;
|
||||
|
||||
// XXX Workaround for bug 986973 to maintain the existing broken semantics
|
||||
template<>
|
||||
@ -2265,15 +2268,17 @@ ContentParent::InitInternal(ProcessPriority aInitialPriority,
|
||||
Unused << SendAppInfo(version, buildID, name, UAName, ID, vendor);
|
||||
}
|
||||
|
||||
// Initialize the message manager (and load delayed scripts) now that we
|
||||
// have established communications with the child.
|
||||
mMessageManager->InitWithCallback(this);
|
||||
|
||||
// Send the child its remote type. On Mac, this needs to be sent prior
|
||||
// to the message we send to enable the Sandbox (SendStartProcessSandbox)
|
||||
// because different remote types require different sandbox privileges.
|
||||
Unused << SendRemoteType(mRemoteType);
|
||||
|
||||
ScriptPreloader::InitContentChild(*this);
|
||||
|
||||
// Initialize the message manager (and load delayed scripts) now that we
|
||||
// have established communications with the child.
|
||||
mMessageManager->InitWithCallback(this);
|
||||
|
||||
// Set the subprocess's priority. We do this early on because we're likely
|
||||
// /lowering/ the process's CPU and memory priority, which it has inherited
|
||||
// from this process.
|
||||
@ -3121,6 +3126,19 @@ ContentParent::DeallocPTestShellParent(PTestShellParent* shell)
|
||||
return true;
|
||||
}
|
||||
|
||||
PScriptCacheParent*
|
||||
ContentParent::AllocPScriptCacheParent(const FileDescOrError& cacheFile, const bool& wantCacheData)
|
||||
{
|
||||
return new loader::ScriptCacheParent(wantCacheData);
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::DeallocPScriptCacheParent(PScriptCacheParent* cache)
|
||||
{
|
||||
delete static_cast<loader::ScriptCacheParent*>(cache);
|
||||
return true;
|
||||
}
|
||||
|
||||
PNeckoParent*
|
||||
ContentParent::AllocPNeckoParent()
|
||||
{
|
||||
|
@ -67,6 +67,8 @@ class SandboxBrokerPolicyFactory;
|
||||
|
||||
class PreallocatedProcessManagerImpl;
|
||||
|
||||
using mozilla::loader::PScriptCacheParent;
|
||||
|
||||
namespace embedding {
|
||||
class PrintingParent;
|
||||
}
|
||||
@ -911,6 +913,12 @@ private:
|
||||
|
||||
virtual bool DeallocPTestShellParent(PTestShellParent* shell) override;
|
||||
|
||||
virtual PScriptCacheParent*
|
||||
AllocPScriptCacheParent(const FileDescOrError& cacheFile,
|
||||
const bool& wantCacheData) override;
|
||||
|
||||
virtual bool DeallocPScriptCacheParent(PScriptCacheParent* shell) override;
|
||||
|
||||
virtual bool DeallocPNeckoParent(PNeckoParent* necko) override;
|
||||
|
||||
virtual PPSMContentDownloaderParent*
|
||||
|
@ -45,6 +45,7 @@ include protocol PURLClassifierLocal;
|
||||
include protocol PVRManager;
|
||||
include protocol PVideoDecoderManager;
|
||||
include protocol PFlyWebPublishedServer;
|
||||
include protocol PScriptCache;
|
||||
include DOMTypes;
|
||||
include JavaScriptTypes;
|
||||
include IPCBlob;
|
||||
@ -311,6 +312,7 @@ nested(upto inside_cpow) sync protocol PContent
|
||||
manages PFlyWebPublishedServer;
|
||||
manages PURLClassifier;
|
||||
manages PURLClassifierLocal;
|
||||
manages PScriptCache;
|
||||
|
||||
both:
|
||||
// Depending on exactly how the new browser is being created, it might be
|
||||
@ -406,6 +408,8 @@ child:
|
||||
|
||||
async PTestShell();
|
||||
|
||||
async PScriptCache(FileDescOrError cacheFile, bool wantCacheData);
|
||||
|
||||
async RegisterChrome(ChromePackage[] packages, SubstitutionMapping[] substitutions,
|
||||
OverrideMapping[] overrides, nsCString locale, bool reset);
|
||||
async RegisterChromeItem(ChromeRegistryItem item);
|
||||
|
33
js/xpconnect/loader/PScriptCache.ipdl
Normal file
33
js/xpconnect/loader/PScriptCache.ipdl
Normal file
@ -0,0 +1,33 @@
|
||||
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
|
||||
/* 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 class mozilla::TimeStamp from "mozilla/TimeStamp.h";
|
||||
using mozilla::void_t from "ipc/IPCMessageUtils.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace loader {
|
||||
|
||||
struct ScriptData {
|
||||
nsCString url;
|
||||
nsCString cachePath;
|
||||
TimeStamp loadTime;
|
||||
// This will be an empty array if script data is present in the previous
|
||||
// session's cache.
|
||||
uint8_t[] xdrData;
|
||||
};
|
||||
|
||||
protocol PScriptCache
|
||||
{
|
||||
manager PContent;
|
||||
|
||||
parent:
|
||||
async __delete__(ScriptData[] scripts);
|
||||
};
|
||||
|
||||
} // namespace loader
|
||||
} // namespace mozilla
|
97
js/xpconnect/loader/ScriptCacheActors.cpp
Normal file
97
js/xpconnect/loader/ScriptCacheActors.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
|
||||
/* 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 "mozilla/ScriptPreloader.h"
|
||||
#include "ScriptPreloader-inl.h"
|
||||
#include "mozilla/loader/ScriptCacheActors.h"
|
||||
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace loader {
|
||||
|
||||
void
|
||||
ScriptCacheChild::Init(const Maybe<FileDescriptor>& cacheFile, bool wantCacheData)
|
||||
{
|
||||
mWantCacheData = wantCacheData;
|
||||
|
||||
auto& cache = ScriptPreloader::GetChildSingleton();
|
||||
Unused << cache.InitCache(cacheFile, this);
|
||||
|
||||
if (!wantCacheData) {
|
||||
// If the parent process isn't expecting any cache data from us, we're
|
||||
// done.
|
||||
Send__delete__(this, AutoTArray<ScriptData, 0>());
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize the script cache for the content process, and send back data about
|
||||
// any scripts executed up to this point.
|
||||
void
|
||||
ScriptCacheChild::Finalize(LinkedList<ScriptPreloader::CachedScript>& scripts)
|
||||
{
|
||||
MOZ_ASSERT(mWantCacheData);
|
||||
|
||||
AutoSafeJSAPI jsapi;
|
||||
|
||||
nsTArray<ScriptData> dataArray;
|
||||
for (auto script : scripts) {
|
||||
if (!script->mSize && !script->XDREncode(jsapi.cx())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto data = dataArray.AppendElement();
|
||||
|
||||
data->url() = script->mURL;
|
||||
data->cachePath() = script->mCachePath;
|
||||
|
||||
if (script->HasBuffer()) {
|
||||
auto& xdrData = script->Buffer();
|
||||
data->xdrData().AppendElements(xdrData.begin(), xdrData.length());
|
||||
script->FreeData();
|
||||
}
|
||||
}
|
||||
|
||||
Send__delete__(this, dataArray);
|
||||
}
|
||||
|
||||
void
|
||||
ScriptCacheChild::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
auto& cache = ScriptPreloader::GetChildSingleton();
|
||||
cache.mChildActor = nullptr;
|
||||
}
|
||||
|
||||
|
||||
IPCResult
|
||||
ScriptCacheParent::Recv__delete__(nsTArray<ScriptData>&& scripts)
|
||||
{
|
||||
if (!mWantCacheData && scripts.Length()) {
|
||||
return IPC_FAIL(this, "UnexpectedScriptData");
|
||||
}
|
||||
|
||||
// We don't want any more data from the process at this point.
|
||||
mWantCacheData = false;
|
||||
|
||||
// Merge the child's script data with the parent's.
|
||||
auto parent = static_cast<dom::ContentParent*>(Manager());
|
||||
auto processType = ScriptPreloader::GetChildProcessType(parent->GetRemoteType());
|
||||
|
||||
auto& cache = ScriptPreloader::GetChildSingleton();
|
||||
for (auto& script : scripts) {
|
||||
cache.NoteScript(script.url(), script.cachePath(), processType,
|
||||
Move(script.xdrData()));
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void
|
||||
ScriptCacheParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{}
|
||||
|
||||
} // namespace loader
|
||||
} // namespace mozilla
|
62
js/xpconnect/loader/ScriptCacheActors.h
Normal file
62
js/xpconnect/loader/ScriptCacheActors.h
Normal file
@ -0,0 +1,62 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef ScriptCache_h
|
||||
#define ScriptCache_h
|
||||
|
||||
#include "mozilla/ScriptPreloader.h"
|
||||
#include "mozilla/loader/PScriptCacheChild.h"
|
||||
#include "mozilla/loader/PScriptCacheParent.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
class FileDescriptor;
|
||||
}
|
||||
|
||||
|
||||
namespace loader {
|
||||
|
||||
using mozilla::ipc::FileDescriptor;
|
||||
using mozilla::ipc::IPCResult;
|
||||
|
||||
class ScriptCacheParent final : public PScriptCacheParent
|
||||
{
|
||||
public:
|
||||
explicit ScriptCacheParent(bool wantCacheData)
|
||||
: mWantCacheData(wantCacheData)
|
||||
{}
|
||||
|
||||
protected:
|
||||
virtual IPCResult Recv__delete__(nsTArray<ScriptData>&& scripts) override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
private:
|
||||
bool mWantCacheData;
|
||||
};
|
||||
|
||||
class ScriptCacheChild final : public PScriptCacheChild
|
||||
{
|
||||
friend class mozilla::ScriptPreloader;
|
||||
|
||||
public:
|
||||
ScriptCacheChild() = default;
|
||||
|
||||
void Init(const Maybe<FileDescriptor>& cacheFile, bool wantCacheData);
|
||||
|
||||
protected:
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
void Finalize(LinkedList<ScriptPreloader::CachedScript>& scripts);
|
||||
|
||||
private:
|
||||
bool mWantCacheData = false;
|
||||
};
|
||||
|
||||
|
||||
} // namespace loader
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ScriptCache_h
|
@ -13,6 +13,7 @@
|
||||
#include "mozilla/Range.h"
|
||||
#include "mozilla/Result.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
@ -21,6 +22,13 @@
|
||||
namespace mozilla {
|
||||
namespace loader {
|
||||
|
||||
using mozilla::dom::AutoJSAPI;
|
||||
|
||||
struct MOZ_RAII AutoSafeJSAPI : public AutoJSAPI
|
||||
{
|
||||
AutoSafeJSAPI() { Init(); }
|
||||
};
|
||||
|
||||
static inline Result<Ok, nsresult>
|
||||
WrapNSResult(PRStatus aRv)
|
||||
{
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "mozilla/ScriptPreloader.h"
|
||||
#include "ScriptPreloader-inl.h"
|
||||
#include "mozilla/loader/ScriptCacheActors.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
@ -15,7 +16,6 @@
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
|
||||
#include "MainThreadUtils.h"
|
||||
#include "nsDebug.h"
|
||||
@ -29,6 +29,7 @@
|
||||
#include "xpcpublic.h"
|
||||
|
||||
#define DELAYED_STARTUP_TOPIC "browser-delayed-startup-finished"
|
||||
#define DOC_ELEM_INSERTED_TOPIC "document-element-inserted"
|
||||
#define CLEANUP_TOPIC "xpcom-shutdown"
|
||||
#define SHUTDOWN_TOPIC "quit-application-granted"
|
||||
#define CACHE_FLUSH_TOPIC "startupcache-invalidate"
|
||||
@ -41,6 +42,8 @@ static LazyLogModule gLog("ScriptPreloader");
|
||||
}
|
||||
|
||||
using mozilla::dom::AutoJSAPI;
|
||||
using mozilla::dom::ContentChild;
|
||||
using mozilla::dom::ContentParent;
|
||||
using namespace mozilla::loader;
|
||||
|
||||
ProcessType ScriptPreloader::sProcessType;
|
||||
@ -135,6 +138,36 @@ ScriptPreloader::GetChildSingleton()
|
||||
return *singleton;
|
||||
}
|
||||
|
||||
void
|
||||
ScriptPreloader::InitContentChild(ContentParent& parent)
|
||||
{
|
||||
auto& cache = GetChildSingleton();
|
||||
|
||||
// We want startup script data from the first process of a given type.
|
||||
// That process sends back its script data before it executes any
|
||||
// untrusted code, and then we never accept further script data for that
|
||||
// type of process for the rest of the session.
|
||||
//
|
||||
// The script data from each process type is merged with the data from the
|
||||
// parent process's frame and process scripts, and shared between all
|
||||
// content process types in the next session.
|
||||
//
|
||||
// Note that if the first process of a given type crashes or shuts down
|
||||
// before sending us its script data, we silently ignore it, and data for
|
||||
// that process type is not included in the next session's cache. This
|
||||
// should be a sufficiently rare occurrence that it's not worth trying to
|
||||
// handle specially.
|
||||
auto processType = GetChildProcessType(parent.GetRemoteType());
|
||||
bool wantScriptData = !cache.mInitializedProcesses.contains(processType);
|
||||
cache.mInitializedProcesses += processType;
|
||||
|
||||
auto fd = cache.mCacheData.cloneFileDescriptor();
|
||||
if (fd.IsValid()) {
|
||||
Unused << parent.SendPScriptCacheConstructor(fd, wantScriptData);
|
||||
} else {
|
||||
Unused << parent.SendPScriptCacheConstructor(NS_ERROR_FILE_NOT_FOUND, wantScriptData);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessType
|
||||
ScriptPreloader::GetChildProcessType(const nsAString& remoteType)
|
||||
@ -145,14 +178,9 @@ ScriptPreloader::GetChildProcessType(const nsAString& remoteType)
|
||||
return ProcessType::Web;
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
struct MOZ_RAII AutoSafeJSAPI : public AutoJSAPI
|
||||
{
|
||||
AutoSafeJSAPI() { Init(); }
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
TraceOp(JSTracer* trc, void* data)
|
||||
{
|
||||
@ -188,7 +216,18 @@ ScriptPreloader::ScriptPreloader()
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
MOZ_RELEASE_ASSERT(obs);
|
||||
obs->AddObserver(this, DELAYED_STARTUP_TOPIC, false);
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
// In the parent process, we want to freeze the script cache as soon
|
||||
// as delayed startup for the first browser window has completed.
|
||||
obs->AddObserver(this, DELAYED_STARTUP_TOPIC, false);
|
||||
} else {
|
||||
// In the child process, we need to freeze the script cache before any
|
||||
// untrusted code has been executed. The insertion of the first DOM
|
||||
// document element may sometimes be earlier than is ideal, but at
|
||||
// least it should always be safe.
|
||||
obs->AddObserver(this, DOC_ELEM_INSERTED_TOPIC, false);
|
||||
}
|
||||
obs->AddObserver(this, SHUTDOWN_TOPIC, false);
|
||||
obs->AddObserver(this, CLEANUP_TOPIC, false);
|
||||
obs->AddObserver(this, CACHE_FLUSH_TOPIC, false);
|
||||
@ -271,17 +310,28 @@ ScriptPreloader::FlushCache()
|
||||
nsresult
|
||||
ScriptPreloader::Observe(nsISupports* subject, const char* topic, const char16_t* data)
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (!strcmp(topic, DELAYED_STARTUP_TOPIC)) {
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
obs->RemoveObserver(this, DELAYED_STARTUP_TOPIC);
|
||||
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
mStartupFinished = true;
|
||||
|
||||
|
||||
if (XRE_IsParentProcess() && mChildCache) {
|
||||
if (mChildCache) {
|
||||
Unused << NS_NewNamedThread("SaveScripts",
|
||||
getter_AddRefs(mSaveThread), this);
|
||||
}
|
||||
} else if (!strcmp(topic, DOC_ELEM_INSERTED_TOPIC)) {
|
||||
obs->RemoveObserver(this, DOC_ELEM_INSERTED_TOPIC);
|
||||
|
||||
MOZ_ASSERT(XRE_IsContentProcess());
|
||||
|
||||
mStartupFinished = true;
|
||||
|
||||
if (mChildActor) {
|
||||
mChildActor->Finalize(mSavedScripts);
|
||||
}
|
||||
} else if (!strcmp(topic, SHUTDOWN_TOPIC)) {
|
||||
ForceWriteCacheFile();
|
||||
} else if (!strcmp(topic, CLEANUP_TOPIC)) {
|
||||
@ -351,6 +401,31 @@ ScriptPreloader::InitCache(const nsAString& basePath)
|
||||
|
||||
MOZ_TRY(OpenCache());
|
||||
|
||||
return InitCacheInternal();
|
||||
}
|
||||
|
||||
Result<Ok, nsresult>
|
||||
ScriptPreloader::InitCache(const Maybe<ipc::FileDescriptor>& cacheFile, ScriptCacheChild* cacheChild)
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsContentProcess());
|
||||
|
||||
mCacheInitialized = true;
|
||||
mChildActor = cacheChild;
|
||||
|
||||
RegisterWeakMemoryReporter(this);
|
||||
|
||||
if (cacheFile.isNothing()){
|
||||
return Ok();
|
||||
}
|
||||
|
||||
MOZ_TRY(mCacheData.init(cacheFile.ref()));
|
||||
|
||||
return InitCacheInternal();
|
||||
}
|
||||
|
||||
Result<Ok, nsresult>
|
||||
ScriptPreloader::InitCacheInternal()
|
||||
{
|
||||
auto size = mCacheData.size();
|
||||
|
||||
uint32_t headerSize;
|
||||
@ -467,7 +542,7 @@ ScriptPreloader::PrepareCacheWrite()
|
||||
// don't bother writing out a new cache file.
|
||||
bool found = false;
|
||||
for (auto script : mSavedScripts) {
|
||||
if (script->mXDRRange.isNothing()) {
|
||||
if (!script->HasRange() || script->HasArray()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@ -584,7 +659,10 @@ ScriptPreloader::WriteCache()
|
||||
|
||||
for (auto script : mSavedScripts) {
|
||||
MOZ_TRY(Write(fd, script->Range().begin().get(), script->mSize));
|
||||
script->mXDRData.reset();
|
||||
|
||||
if (script->mScript) {
|
||||
script->FreeData();
|
||||
}
|
||||
}
|
||||
|
||||
NS_TRY(cacheFile->MoveTo(nullptr, mBaseName + NS_LITERAL_STRING(".bin")));
|
||||
@ -629,14 +707,11 @@ ScriptPreloader::FindScript(LinkedList<CachedScript>& scripts, const nsCString&
|
||||
|
||||
void
|
||||
ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath,
|
||||
JS::HandleScript script)
|
||||
JS::HandleScript jsscript)
|
||||
{
|
||||
if (mStartupFinished || !mCacheInitialized) {
|
||||
return;
|
||||
}
|
||||
// Don't bother trying to cache any URLs with cache-busting query
|
||||
// parameters.
|
||||
if (cachePath.FindChar('?') >= 0) {
|
||||
if (mStartupFinished || !mCacheInitialized || cachePath.FindChar('?') >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -646,28 +721,58 @@ ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath,
|
||||
return;
|
||||
}
|
||||
|
||||
bool exists = mScripts.Get(cachePath);
|
||||
|
||||
CachedScript* restored = nullptr;
|
||||
if (exists) {
|
||||
restored = FindScript(mRestoredScripts, cachePath);
|
||||
}
|
||||
CachedScript* script = mScripts.Get(cachePath);
|
||||
bool restored = script && FindScript(mRestoredScripts, cachePath);
|
||||
|
||||
if (restored) {
|
||||
restored->remove();
|
||||
mSavedScripts.insertBack(restored);
|
||||
script->remove();
|
||||
mSavedScripts.insertBack(script);
|
||||
|
||||
MOZ_ASSERT(script);
|
||||
restored->mProcesses += CurrentProcessType();
|
||||
restored->mScript = script;
|
||||
restored->mReadyToExecute = true;
|
||||
} else if (!exists) {
|
||||
auto cachedScript = new CachedScript(*this, url, cachePath, script);
|
||||
cachedScript->mProcesses += CurrentProcessType();
|
||||
|
||||
mSavedScripts.insertBack(cachedScript);
|
||||
mScripts.Put(cachePath, cachedScript);
|
||||
MOZ_ASSERT(jsscript);
|
||||
script->mScript = jsscript;
|
||||
script->mReadyToExecute = true;
|
||||
} else if (!script) {
|
||||
script = new CachedScript(*this, url, cachePath, jsscript);
|
||||
mSavedScripts.insertBack(script);
|
||||
mScripts.Put(cachePath, script);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
script->mProcessTypes += CurrentProcessType();
|
||||
}
|
||||
|
||||
void
|
||||
ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath,
|
||||
ProcessType processType, nsTArray<uint8_t>&& xdrData)
|
||||
{
|
||||
CachedScript* script = mScripts.Get(cachePath);
|
||||
bool restored = script && FindScript(mRestoredScripts, cachePath);
|
||||
|
||||
if (restored) {
|
||||
script->remove();
|
||||
mSavedScripts.insertBack(script);
|
||||
|
||||
script->mReadyToExecute = true;
|
||||
} else {
|
||||
if (!script) {
|
||||
script = new CachedScript(this, url, cachePath, nullptr);
|
||||
mSavedScripts.insertBack(script);
|
||||
mScripts.Put(cachePath, script);
|
||||
}
|
||||
|
||||
if (!script->HasRange()) {
|
||||
MOZ_ASSERT(!script->HasArray());
|
||||
|
||||
script->mSize = xdrData.Length();
|
||||
script->mXDRData.construct<nsTArray<uint8_t>>(Forward<nsTArray<uint8_t>>(xdrData));
|
||||
|
||||
auto& data = script->Array();
|
||||
script->mXDRRange.emplace(data.Elements(), data.Length());
|
||||
}
|
||||
}
|
||||
|
||||
script->mProcessTypes += processType;
|
||||
}
|
||||
|
||||
JSScript*
|
||||
@ -775,11 +880,11 @@ ScriptPreloader::CachedScript::XDREncode(JSContext* cx)
|
||||
JSAutoCompartment ac(cx, mScript);
|
||||
JS::RootedScript jsscript(cx, mScript);
|
||||
|
||||
mXDRData.emplace();
|
||||
mXDRData.construct<JS::TranscodeBuffer>();
|
||||
|
||||
JS::TranscodeResult code = JS::EncodeScript(cx, Data(), jsscript);
|
||||
JS::TranscodeResult code = JS::EncodeScript(cx, Buffer(), jsscript);
|
||||
if (code == JS::TranscodeResult_Ok) {
|
||||
mXDRRange.emplace(Data().begin(), Data().length());
|
||||
mXDRRange.emplace(Buffer().begin(), Buffer().length());
|
||||
return true;
|
||||
}
|
||||
JS_ClearPendingException(cx);
|
||||
@ -814,11 +919,15 @@ ScriptPreloader::CachedScript::GetJSScript(JSContext* cx)
|
||||
// wait for the off-thread decoding to finish. In either case, we decode
|
||||
// it synchronously the first time it's needed.
|
||||
if (!mToken) {
|
||||
MOZ_ASSERT(mXDRRange.isSome());
|
||||
MOZ_ASSERT(HasRange());
|
||||
|
||||
JS::RootedScript script(cx);
|
||||
if (JS::DecodeScript(cx, Range(), &script)) {
|
||||
mScript = script;
|
||||
|
||||
if (mCache.mSaveComplete) {
|
||||
FreeData();
|
||||
}
|
||||
}
|
||||
|
||||
return mScript;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/MaybeOneOf.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/Range.h"
|
||||
#include "mozilla/Vector.h"
|
||||
@ -27,8 +28,15 @@
|
||||
#include <prio.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class ContentParent;
|
||||
}
|
||||
namespace ipc {
|
||||
class FileDescriptor;
|
||||
}
|
||||
namespace loader {
|
||||
class InputBuffer;
|
||||
class ScriptCacheChild;
|
||||
|
||||
enum class ProcessType : uint8_t {
|
||||
Parent,
|
||||
@ -45,6 +53,8 @@ class ScriptPreloader : public nsIObserver
|
||||
{
|
||||
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
|
||||
|
||||
friend class mozilla::loader::ScriptCacheChild;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
@ -65,9 +75,18 @@ public:
|
||||
// stored to the startup script cache.
|
||||
void NoteScript(const nsCString& url, const nsCString& cachePath, JS::HandleScript script);
|
||||
|
||||
void NoteScript(const nsCString& url, const nsCString& cachePath,
|
||||
ProcessType processType, nsTArray<uint8_t>&& xdrData);
|
||||
|
||||
// Initializes the script cache from the startup script cache file.
|
||||
Result<Ok, nsresult> InitCache(const nsAString& = NS_LITERAL_STRING("scriptCache"));
|
||||
|
||||
Result<Ok, nsresult> InitCache(const Maybe<ipc::FileDescriptor>& cacheFile, ScriptCacheChild* cacheChild);
|
||||
|
||||
private:
|
||||
Result<Ok, nsresult> InitCacheInternal();
|
||||
|
||||
public:
|
||||
void Trace(JSTracer* trc);
|
||||
|
||||
static ProcessType CurrentProcessType()
|
||||
@ -75,6 +94,8 @@ public:
|
||||
return sProcessType;
|
||||
}
|
||||
|
||||
static void InitContentChild(dom::ContentParent& parent);
|
||||
|
||||
protected:
|
||||
virtual ~ScriptPreloader() = default;
|
||||
|
||||
@ -129,6 +150,16 @@ private:
|
||||
|
||||
void Cancel();
|
||||
|
||||
void FreeData()
|
||||
{
|
||||
// If the script data isn't mmapped, we need to release both it
|
||||
// and the Range that points to it at the same time.
|
||||
if (!mXDRData.empty()) {
|
||||
mXDRRange.reset();
|
||||
mXDRData.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
// Encodes this script into XDR data, and stores the result in mXDRData.
|
||||
// Returns true on success, false on failure.
|
||||
bool XDREncode(JSContext* cx);
|
||||
@ -147,29 +178,48 @@ private:
|
||||
|
||||
// Returns the XDR data generated for this script during this session. See
|
||||
// mXDRData.
|
||||
JS::TranscodeBuffer& Data()
|
||||
JS::TranscodeBuffer& Buffer()
|
||||
{
|
||||
MOZ_ASSERT(mXDRData.isSome());
|
||||
return mXDRData.ref();
|
||||
MOZ_ASSERT(HasBuffer());
|
||||
return mXDRData.ref<JS::TranscodeBuffer>();
|
||||
}
|
||||
|
||||
bool HasBuffer() { return mXDRData.constructed<JS::TranscodeBuffer>(); }
|
||||
|
||||
// Returns the read-only XDR data for this script. See mXDRRange.
|
||||
const JS::TranscodeRange& Range()
|
||||
{
|
||||
MOZ_ASSERT(mXDRRange.isSome());
|
||||
MOZ_ASSERT(HasRange());
|
||||
return mXDRRange.ref();
|
||||
}
|
||||
|
||||
bool HasRange() { return mXDRRange.isSome(); }
|
||||
|
||||
nsTArray<uint8_t>& Array()
|
||||
{
|
||||
MOZ_ASSERT(HasArray());
|
||||
return mXDRData.ref<nsTArray<uint8_t>>();
|
||||
}
|
||||
|
||||
bool HasArray() { return mXDRData.constructed<nsTArray<uint8_t>>(); }
|
||||
|
||||
|
||||
JSScript* GetJSScript(JSContext* cx);
|
||||
|
||||
size_t HeapSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
||||
{
|
||||
auto size = mallocSizeOf(this);
|
||||
if (mXDRData.isSome()) {
|
||||
size += (mXDRData->sizeOfExcludingThis(mallocSizeOf) +
|
||||
mURL.SizeOfExcludingThisEvenIfShared(mallocSizeOf) +
|
||||
mCachePath.SizeOfExcludingThisEvenIfShared(mallocSizeOf));
|
||||
|
||||
if (HasArray()) {
|
||||
size += Array().ShallowSizeOfExcludingThis(mallocSizeOf);
|
||||
} else if (HasBuffer()) {
|
||||
size += Buffer().sizeOfExcludingThis(mallocSizeOf);
|
||||
} else {
|
||||
return size;
|
||||
}
|
||||
|
||||
size += (mURL.SizeOfExcludingThisEvenIfShared(mallocSizeOf) +
|
||||
mCachePath.SizeOfExcludingThisEvenIfShared(mallocSizeOf));
|
||||
return size;
|
||||
}
|
||||
|
||||
@ -209,7 +259,7 @@ private:
|
||||
|
||||
// XDR data which was generated from a script compiled during this
|
||||
// session, and will be written to the cache file.
|
||||
Maybe<JS::TranscodeBuffer> mXDRData;
|
||||
MaybeOneOf<JS::TranscodeBuffer, nsTArray<uint8_t>> mXDRData;
|
||||
};
|
||||
|
||||
// There's a trade-off between the time it takes to setup an off-thread
|
||||
@ -302,7 +352,12 @@ private:
|
||||
// The process type of the current process.
|
||||
static ProcessType sProcessType;
|
||||
|
||||
// The process types for which remote processes have been initialized, and
|
||||
// are expected to send back script data.
|
||||
EnumSet<ProcessType> mInitializedProcesses{};
|
||||
|
||||
RefPtr<ScriptPreloader> mChildCache;
|
||||
ScriptCacheChild* mChildActor = nullptr;
|
||||
|
||||
nsString mBaseName;
|
||||
|
||||
|
@ -9,6 +9,7 @@ UNIFIED_SOURCES += [
|
||||
'ChromeScriptLoader.cpp',
|
||||
'mozJSLoaderUtils.cpp',
|
||||
'mozJSSubScriptLoader.cpp',
|
||||
'ScriptCacheActors.cpp',
|
||||
'ScriptPreloader.cpp',
|
||||
]
|
||||
|
||||
@ -18,6 +19,10 @@ SOURCES += [
|
||||
'mozJSComponentLoader.cpp'
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'PScriptCache.ipdl',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'ScriptPreloader.h',
|
||||
]
|
||||
@ -28,6 +33,7 @@ EXPORTS.mozilla.dom += [
|
||||
|
||||
EXPORTS.mozilla.loader += [
|
||||
'AutoMemMap.h',
|
||||
'ScriptCacheActors.h',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
|
Loading…
Reference in New Issue
Block a user