Bug 1166207 - Load preload.js in the Nuwa process. r=khuey

This commit is contained in:
Cervantes Yu 2015-08-28 17:57:44 +08:00
parent 4a75075663
commit aa469abcee
11 changed files with 231 additions and 15 deletions

View File

@ -1757,4 +1757,13 @@ BrowserElementChild.prototype = {
}
};
var api = new BrowserElementChild();
var api = null;
if ('DoPreloadPostfork' in this && typeof this.DoPreloadPostfork === 'function') {
// If we are preloaded, instantiate BrowserElementChild after a content
// process is forked.
this.DoPreloadPostfork(function() {
api = new BrowserElementChild();
});
} else {
api = new BrowserElementChild();
}

View File

@ -2291,11 +2291,7 @@ ContentChild::RecvAppInit()
// PreloadSlowThings() may set the docshell of the first TabChild
// inactive, and we can only safely restore it to active from
// BrowserElementChild.js.
if ((mIsForApp || mIsForBrowser)
#ifdef MOZ_NUWA_PROCESS
&& !IsNuwaProcess()
#endif
) {
if (mIsForApp || mIsForBrowser) {
PreloadSlowThings();
}

View File

@ -23,6 +23,9 @@
#include "mozilla/plugins/PluginWidgetChild.h"
#include "mozilla/IMEStateManager.h"
#include "mozilla/ipc/DocumentRendererChild.h"
#ifdef MOZ_NUWA_PROCESS
#include "ipc/Nuwa.h"
#endif
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/layers/APZCCallbackHelper.h"
#include "mozilla/layers/APZCTreeManager.h"
@ -450,10 +453,67 @@ TabChild::FindTabChild(const TabId& aTabId)
return tabChild.forget();
}
static void
PreloadSlowThingsPostFork(void* aUnused)
{
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
observerService->NotifyObservers(nullptr, "preload-postfork", nullptr);
}
#ifdef MOZ_NUWA_PROCESS
class MessageChannelAutoBlock MOZ_STACK_CLASS
{
public:
MessageChannelAutoBlock()
{
SetMessageChannelBlocked(true);
}
~MessageChannelAutoBlock()
{
SetMessageChannelBlocked(false);
}
private:
void SetMessageChannelBlocked(bool aBlock)
{
if (!IsNuwaProcess()) {
return;
}
mozilla::dom::ContentChild* content =
mozilla::dom::ContentChild::GetSingleton();
if (aBlock) {
content->GetIPCChannel()->Block();
} else {
content->GetIPCChannel()->Unblock();
}
nsTArray<IToplevelProtocol*> actors;
content->GetOpenedActors(actors);
for (size_t j = 0; j < actors.Length(); j++) {
IToplevelProtocol* actor = actors[j];
if (aBlock) {
actor->GetIPCChannel()->Block();
} else {
actor->GetIPCChannel()->Unblock();
}
}
}
};
#endif
static bool sPreloaded = false;
/*static*/ void
TabChild::PreloadSlowThings()
{
MOZ_ASSERT(!sPreallocatedTab);
if (sPreloaded) {
// If we are alredy initialized in Nuwa, don't redo preloading.
return;
}
sPreloaded = true;
// Pass nullptr to aManager since at this point the TabChild is
// not connected to any manager. Any attempt to use the TabChild
@ -465,6 +525,13 @@ TabChild::PreloadSlowThings()
!tab->InitTabChildGlobal(DONT_LOAD_SCRIPTS)) {
return;
}
#ifdef MOZ_NUWA_PROCESS
// Temporarily block the IPC channels to the chrome process when we are
// preloading.
MessageChannelAutoBlock autoblock;
#endif
// Just load and compile these scripts, but don't run them.
tab->TryCacheLoadAndCompileScript(BROWSER_ELEMENT_CHILD_SCRIPT, true);
// Load, compile, and run these scripts.
@ -472,6 +539,16 @@ TabChild::PreloadSlowThings()
NS_LITERAL_STRING("chrome://global/content/preload.js"),
true);
#ifdef MOZ_NUWA_PROCESS
if (IsNuwaProcess()) {
NuwaAddFinalConstructor(PreloadSlowThingsPostFork, nullptr);
} else {
PreloadSlowThingsPostFork(nullptr);
}
#else
PreloadSlowThingsPostFork(nullptr);
#endif
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(tab->WebNavigation());
if (nsIPresShell* presShell = docShell->GetPresShell()) {
// Initialize and do an initial reflow of the about:blank

View File

@ -9,6 +9,17 @@
const BrowserElementIsPreloaded = true;
const DoPreloadPostfork = function(aCallback) {
Services.obs.addObserver({
_callback: aCallback,
observe: function() {
this._callback();
Services.obs.removeObserver(this, "preload-postfork");
}
}, "preload-postfork", false);
};
(function (global) {
"use strict";
@ -16,7 +27,6 @@ const BrowserElementIsPreloaded = true;
let Cc = Components.classes;
let Ci = Components.interfaces;
Cu.import("resource://gre/modules/AppsServiceChild.jsm");
Cu.import("resource://gre/modules/AppsUtils.jsm");
Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
@ -30,12 +40,10 @@ const BrowserElementIsPreloaded = true;
Cc["@mozilla.org/appshell/appShellService;1"].getService(Ci["nsIAppShellService"]);
Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci["nsIWindowMediator"]);
Cc["@mozilla.org/AppsService;1"].getService(Ci["nsIAppsService"]);
Cc["@mozilla.org/base/telemetry;1"].getService(Ci["nsITelemetry"]);
Cc["@mozilla.org/categorymanager;1"].getService(Ci["nsICategoryManager"]);
Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci["nsIMessageSender"]);
Cc["@mozilla.org/consoleservice;1"].getService(Ci["nsIConsoleService"]);
Cc["@mozilla.org/cookieService;1"].getService(Ci["nsICookieService"]);
Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci["nsIURIFixup"]);
Cc["@mozilla.org/dom/dom-request-service;1"].getService(Ci["nsIDOMRequestService"]);
Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci["nsIPromptService"]);
@ -53,14 +61,12 @@ const BrowserElementIsPreloaded = true;
Cc["@mozilla.org/network/idn-service;1"].getService(Ci["nsIIDNService"]);
Cc["@mozilla.org/network/io-service;1"].getService(Ci["nsIIOService2"]);
Cc["@mozilla.org/network/mime-hdrparam;1"].getService(Ci["nsIMIMEHeaderParam"]);
Cc["@mozilla.org/network/protocol-proxy-service;1"].getService(Ci["nsIProtocolProxyService"]);
Cc["@mozilla.org/network/socket-transport-service;1"].getService(Ci["nsISocketTransportService"]);
Cc["@mozilla.org/network/stream-transport-service;1"].getService(Ci["nsIStreamTransportService"]);
Cc["@mozilla.org/network/url-parser;1?auth=maybe"].getService(Ci["nsIURLParser"]);
Cc["@mozilla.org/network/url-parser;1?auth=no"].getService(Ci["nsIURLParser"]);
Cc["@mozilla.org/network/url-parser;1?auth=yes"].getService(Ci["nsIURLParser"]);
Cc["@mozilla.org/observer-service;1"].getService(Ci["nsIObserverService"]);
Cc["@mozilla.org/permissionmanager;1"].getService(Ci["nsIPermissionManager"]);
Cc["@mozilla.org/preferences-service;1"].getService(Ci["nsIPrefBranch"]);
Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci["nsIScriptSecurityManager"]);
Cc["@mozilla.org/storage/service;1"].getService(Ci["mozIStorageService"]);
@ -70,7 +76,6 @@ const BrowserElementIsPreloaded = true;
Cc["@mozilla.org/uriloader;1"].getService(Ci["nsIURILoader"]);
Cc["@mozilla.org/cspcontext;1"].createInstance(Ci["nsIContentSecurityPolicy"]);
Cc["@mozilla.org/settingsManager;1"].createInstance(Ci["nsISupports"]);
Cc["@mozilla.org/webapps;1"].createInstance(Ci["nsISupports"]);
/* Applications Specific Helper */
try {
@ -104,7 +109,44 @@ const BrowserElementIsPreloaded = true;
Services.io.getProtocolHandler("app");
Services.io.getProtocolHandler("default");
docShell.isActive = false;
docShell.createAboutBlankContentViewer(null);
// Register an observer for topic "preload_postfork" after we fork a content
// process.
DoPreloadPostfork(function () {
// Load AppsServiceChild.jsm after fork since it sends an async message to
// the chrome process in its init() function.
Cu.import("resource://gre/modules/AppsServiceChild.jsm");
// Load UserCustomizations.jsm after fork since it sends an async message to
// the chrome process in its init() function.
try {
if (Services.prefs.getBoolPref("dom.apps.customization.enabled")) {
Cu.import("resource://gre/modules/UserCustomizations.jsm");
}
} catch(e) {}
// Load nsIAppsService after fork since its implementation loads
// AppsServiceChild.jsm
Cc["@mozilla.org/AppsService;1"].getService(Ci["nsIAppsService"]);
// Load nsICookieService after fork since it sends an IPC constructor
// message to the chrome process.
Cc["@mozilla.org/cookieService;1"].getService(Ci["nsICookieService"]);
// Load nsIPermissionManager after fork since it sends a message to the
// chrome process to read permissions.
Cc["@mozilla.org/permissionmanager;1"].getService(Ci["nsIPermissionManager"]);
// Create this instance after fork since it loads AppsServiceChild.jsm
Cc["@mozilla.org/webapps;1"].createInstance(Ci["nsISupports"]);
// Load nsIProtocolProxyService after fork since it asynchronously accesses
// the "Proxy Resolution" thread after it's frozen.
Cc["@mozilla.org/network/protocol-proxy-service;1"].getService(Ci["nsIProtocolProxyService"]);
// Call docShell.createAboutBlankContentViewer() after fork since it has IPC
// activity in the PCompositor protocol.
docShell.createAboutBlankContentViewer(null);
docShell.isActive = false;
});
})(this);

View File

@ -175,6 +175,16 @@ class MessageChannel : HasResultCodes
sIsPumpingMessages = aIsPumping;
}
#ifdef MOZ_NUWA_PROCESS
void Block() {
mLink->Block();
}
void Unblock() {
mLink->Unblock();
}
#endif
#ifdef OS_WIN
struct MOZ_STACK_CLASS SyncStackFrame
{

View File

@ -16,6 +16,10 @@
#include "mozilla/dom/PContent.h"
#include "mozilla/dom/PNuwa.h"
#include "mozilla/hal_sandbox/PHal.h"
#if defined(DEBUG) || defined(ENABLE_TESTS)
#include "jsprf.h"
extern "C" char* PrintJSStack();
#endif
#endif
#include "mozilla/Assertions.h"
@ -75,6 +79,7 @@ ProcessLink::ProcessLink(MessageChannel *aChan)
, mExistingListener(nullptr)
#ifdef MOZ_NUWA_PROCESS
, mIsToNuwaProcess(false)
, mIsBlocked(false)
#endif
{
}
@ -175,6 +180,8 @@ ProcessLink::SendMessage(Message *msg)
mChan->mMonitor->AssertCurrentThreadOwns();
#ifdef MOZ_NUWA_PROCESS
// Parent to child: check whether we are sending some unexpected message to
// the Nuwa process.
if (mIsToNuwaProcess && mozilla::dom::ContentParent::IsNuwaReady()) {
switch (msg->type()) {
case mozilla::dom::PNuwa::Msg_Fork__ID:
@ -193,6 +200,19 @@ ProcessLink::SendMessage(Message *msg)
#endif
}
}
// Nuwa to parent: check whether we are currently blocked.
if (IsNuwaProcess() && mIsBlocked) {
#if defined(ENABLE_TESTS) || defined(DEBUG)
char* jsstack = PrintJSStack();
printf_stderr("Fatal error: sending a message to the chrome process"
"with a blocked IPC channel from \n%s",
jsstack ? jsstack : "<no JS stack>");
JS_smprintf_free(jsstack);
MOZ_CRASH();
#endif
}
#endif
mIOLoop->PostTask(

View File

@ -128,6 +128,12 @@ class MessageLink
virtual bool Unsound_IsClosed() const = 0;
virtual uint32_t Unsound_NumQueuedMessages() const = 0;
#ifdef MOZ_NUWA_PROCESS
// To be overridden by ProcessLink.
virtual void Block() {}
virtual void Unblock() {}
#endif
protected:
MessageChannel *mChan;
};
@ -175,12 +181,22 @@ class ProcessLink
virtual bool Unsound_IsClosed() const override;
virtual uint32_t Unsound_NumQueuedMessages() const override;
#ifdef MOZ_NUWA_PROCESS
void Block() override {
mIsBlocked = true;
}
void Unblock() override {
mIsBlocked = false;
}
#endif
protected:
Transport* mTransport;
MessageLoop* mIOLoop; // thread where IO happens
Transport::Listener* mExistingListener; // channel's previous listener
#ifdef MOZ_NUWA_PROCESS
bool mIsToNuwaProcess;
bool mIsBlocked;
#endif
};

View File

@ -235,6 +235,8 @@ public:
void GetOpenedActors(nsTArray<IToplevelProtocol*>& aActors);
virtual MessageChannel* GetIPCChannel() = 0;
// This Unsafe version should only be used when all other threads are
// frozen, since it performs no locking. It also takes a stack-allocated
// array and its size (number of elements) rather than an nsTArray. The Nuwa

View File

@ -2077,4 +2077,28 @@ IsNuwaReady() {
return sNuwaReady;
}
#if defined(DEBUG) || defined(ENABLE_TESTS)
MFBT_API void
NuwaAssertNotFrozen(unsigned int aThread, const char* aThreadName) {
if (!sIsNuwaProcess || !sIsFreezing) {
return;
}
thread_info_t *tinfo = GetThreadInfo(static_cast<pthread_t>(aThread));
if (!tinfo) {
return;
}
if ((tinfo->flags & TINFO_FLAG_NUWA_SUPPORT) &&
!(tinfo->flags & TINFO_FLAG_NUWA_EXPLICIT_CHECKPOINT)) {
__android_log_print(ANDROID_LOG_FATAL, "Nuwa",
"Fatal error: the Nuwa process is about to deadlock in "
"accessing a frozen thread (%s, tid=%d).",
aThreadName ? aThreadName : "(unnamed)",
tinfo->origNativeThreadID);
abort();
}
}
#endif
} // extern "C"

View File

@ -186,6 +186,16 @@ MFBT_API bool IsNuwaProcess();
* @return If the Nuwa process is ready for spawning new processes.
*/
MFBT_API bool IsNuwaReady();
#if defined(DEBUG) || defined(ENABLE_TESTS)
/**
* Asserts that aThread is not frozen.
*/
MFBT_API void NuwaAssertNotFrozen(unsigned int aThread,
const char* aThreadName);
#else
#define NuwaAssertNotFrozen(aThread, aThreadName) do {} while(0);
#endif
};
#endif /* __NUWA_H_ */

View File

@ -41,6 +41,10 @@
#include "nsICrashReporter.h"
#endif
#ifdef MOZ_NUWA_PROCESS
#include "private/pprthred.h"
#endif
#ifdef XP_LINUX
#include <sys/time.h>
#include <sys/resource.h>
@ -511,6 +515,12 @@ nsThread::PutEvent(already_AddRefed<nsIRunnable>&& aEvent, nsNestedEventTarget*
{
nsCOMPtr<nsIThreadObserver> obs;
#ifdef MOZ_NUWA_PROCESS
// On debug build or when tests are enabled, assert that we are not about to
// create a deadlock in the Nuwa process.
NuwaAssertNotFrozen(PR_GetThreadID(mThread), PR_GetThreadName(mThread));
#endif
{
MutexAutoLock lock(mLock);
nsChainedEventQueue* queue = aTarget ? aTarget->mQueue : &mEventsRoot;