Files
UXP-Jihad/dom/ipc/ContentChild.cpp
T
athenian200 b787af5702 Issue #1638 - Part 1: Put NPAPI code behind MOZ_ENABLE_NPAPI.
Adds --disable-npapi as an additional build option. The default is still to build in NPAPI compatibility, but with this flag, it's finally possible to build without pulling in GTK2 on systems that don't have the package.

This essentially ifdefs everything associated with NPAPI, following various Mozilla removal bugs.

Ref: BZ 1682030, BZ 1251202, BZ 1217665, BZ 1687239
2024-01-10 04:03:35 -06:00

2883 lines
82 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifdef MOZ_WIDGET_GTK
#include <gtk/gtk.h>
#endif
#include "ContentChild.h"
#include "BlobChild.h"
#include "GeckoProfiler.h"
#include "TabChild.h"
#include "HandlerServiceChild.h"
#include "mozilla/Attributes.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/Preferences.h"
#include "mozilla/ProcessHangMonitorIPC.h"
#include "mozilla/Unused.h"
#include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
#include "mozilla/docshell/OfflineCacheUpdateChild.h"
#include "mozilla/dom/ContentBridgeChild.h"
#include "mozilla/dom/ContentBridgeParent.h"
#include "mozilla/dom/VideoDecoderManagerChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/DataTransfer.h"
#include "mozilla/dom/DOMStorageIPC.h"
#include "mozilla/dom/ExternalHelperAppChild.h"
#include "mozilla/dom/GetFilesHelper.h"
#include "mozilla/dom/ProcessGlobal.h"
#include "mozilla/dom/PushNotifier.h"
#include "mozilla/dom/workers/ServiceWorkerManager.h"
#include "mozilla/dom/nsIContentChild.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/psm/PSMContentListener.h"
#include "mozilla/hal_sandbox/PHalChild.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/FileDescriptorSetChild.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/ipc/ProcessChild.h"
#include "mozilla/ipc/PSendStreamChild.h"
#include "mozilla/ipc/TestShellChild.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "mozilla/layers/APZChild.h"
#include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/layers/ContentProcessController.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "mozilla/layout/RenderFrameChild.h"
#include "mozilla/net/NeckoChild.h"
#include "mozilla/net/CaptivePortalService.h"
#ifdef MOZ_ENABLE_NPAPI
#include "mozilla/plugins/PluginInstanceParent.h"
#include "mozilla/plugins/PluginModuleParent.h"
#endif
#include "mozilla/widget/WidgetMessageUtils.h"
#include "nsBaseDragService.h"
#include "mozilla/media/MediaChild.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/WebBrowserPersistDocumentChild.h"
#include "imgLoader.h"
#ifdef MOZ_GMP
#include "GMPServiceChild.h"
#endif
#include "mozilla/Unused.h"
#include "mozInlineSpellChecker.h"
#include "nsDocShell.h"
#include "nsIConsoleListener.h"
#include "nsIContentViewer.h"
#include "nsICycleCollectorListener.h"
#include "nsIIdlePeriod.h"
#include "nsIDragService.h"
#include "nsIIPCBackgroundChildCreateCallback.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIMemoryReporter.h"
#include "nsIMemoryInfoDumper.h"
#include "nsIMutable.h"
#include "nsIObserverService.h"
#include "nsIScriptSecurityManager.h"
#include "nsScreenManagerProxy.h"
#include "nsMemoryInfoDumper.h"
#include "nsServiceManagerUtils.h"
#include "nsStyleSheetService.h"
#include "nsVariant.h"
#include "nsXULAppAPI.h"
#include "nsIScriptError.h"
#include "nsIConsoleService.h"
#include "nsJSEnvironment.h"
#include "SandboxHal.h"
#include "nsDebugImpl.h"
#include "nsHashPropertyBag.h"
#include "nsLayoutStylesheetCache.h"
#include "nsThreadManager.h"
#include "nsAnonymousTemporaryFile.h"
#include "nsISpellChecker.h"
#include "nsClipboardProxy.h"
#include "nsDirectoryService.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsContentPermissionHelper.h"
#ifdef NS_PRINTING
#include "nsPrintingProxy.h"
#endif
#include "IHistory.h"
#include "nsNetUtil.h"
#include "base/message_loop.h"
#include "base/process_util.h"
#include "base/task.h"
#include "nsChromeRegistryContent.h"
#include "nsFrameMessageManager.h"
#include "nsIGeolocationProvider.h"
#include "mozilla/dom/PMemoryReportRequestChild.h"
#include "mozilla/dom/PCycleCollectWithLogsChild.h"
#include "nsIScriptSecurityManager.h"
#include "nsHostObjectProtocolHandler.h"
#ifdef MOZ_WEBRTC
#include "signaling/src/peerconnection/WebrtcGlobalChild.h"
#endif
#ifdef MOZ_PERMISSIONS
#include "nsPermission.h"
#include "nsPermissionManager.h"
#endif
#include "PermissionMessageUtils.h"
#ifdef XP_WIN
#include <process.h>
#define getpid _getpid
#include "mozilla/widget/AudioSession.h"
#endif
#ifdef MOZ_X11
#include "mozilla/X11Util.h"
#endif
#ifdef ACCESSIBILITY
#include "nsAccessibilityService.h"
#endif
#include "mozilla/dom/File.h"
#include "mozilla/ipc/InputStreamUtils.h"
#ifdef MOZ_WEBSPEECH
#include "mozilla/dom/PSpeechSynthesisChild.h"
#endif
#include "ProcessUtils.h"
#include "URIUtils.h"
#include "nsContentUtils.h"
#include "nsIPrincipal.h"
#include "DomainPolicy.h"
#include "mozilla/dom/ipc/StructuredCloneData.h"
#include "mozilla/dom/time/DateCacheCleaner.h"
#include "mozilla/net/NeckoMessageUtils.h"
#include "mozilla/widget/PuppetBidiKeyboard.h"
#include "mozilla/mozSpellChecker.h"
#ifdef MOZ_GMP
#include "GMPServiceChild.h"
#endif
#include "gfxPlatform.h"
#include "nscore.h" // for NS_FREE_PERMANENT_DATA
using namespace mozilla;
using namespace mozilla::docshell;
using namespace mozilla::dom::ipc;
using namespace mozilla::dom::workers;
using namespace mozilla::media;
using namespace mozilla::embedding;
#ifdef MOZ_GMP
using namespace mozilla::gmp;
#endif
using namespace mozilla::hal_sandbox;
using namespace mozilla::ipc;
using namespace mozilla::layers;
using namespace mozilla::layout;
using namespace mozilla::net;
using namespace mozilla::jsipc;
using namespace mozilla::psm;
using namespace mozilla::widget;
using namespace mozilla::widget;
namespace mozilla {
namespace dom {
class MemoryReportRequestChild : public PMemoryReportRequestChild,
public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
MemoryReportRequestChild(bool aAnonymize,
const MaybeFileDesc& aDMDFile);
NS_IMETHOD Run() override;
private:
virtual ~MemoryReportRequestChild();
bool mAnonymize;
FileDescriptor mDMDFile;
};
NS_IMPL_ISUPPORTS(MemoryReportRequestChild, nsIRunnable)
MemoryReportRequestChild::MemoryReportRequestChild(
bool aAnonymize, const MaybeFileDesc& aDMDFile)
: mAnonymize(aAnonymize)
{
MOZ_COUNT_CTOR(MemoryReportRequestChild);
if (aDMDFile.type() == MaybeFileDesc::TFileDescriptor) {
mDMDFile = aDMDFile.get_FileDescriptor();
}
}
MemoryReportRequestChild::~MemoryReportRequestChild()
{
MOZ_COUNT_DTOR(MemoryReportRequestChild);
}
// IPC sender for remote GC/CC logging.
class CycleCollectWithLogsChild final
: public PCycleCollectWithLogsChild
, public nsICycleCollectorLogSink
{
public:
NS_DECL_ISUPPORTS
CycleCollectWithLogsChild(const FileDescriptor& aGCLog,
const FileDescriptor& aCCLog)
{
mGCLog = FileDescriptorToFILE(aGCLog, "w");
mCCLog = FileDescriptorToFILE(aCCLog, "w");
}
NS_IMETHOD Open(FILE** aGCLog, FILE** aCCLog) override
{
if (NS_WARN_IF(!mGCLog) || NS_WARN_IF(!mCCLog)) {
return NS_ERROR_FAILURE;
}
*aGCLog = mGCLog;
*aCCLog = mCCLog;
return NS_OK;
}
NS_IMETHOD CloseGCLog() override
{
MOZ_ASSERT(mGCLog);
fclose(mGCLog);
mGCLog = nullptr;
SendCloseGCLog();
return NS_OK;
}
NS_IMETHOD CloseCCLog() override
{
MOZ_ASSERT(mCCLog);
fclose(mCCLog);
mCCLog = nullptr;
SendCloseCCLog();
return NS_OK;
}
NS_IMETHOD GetFilenameIdentifier(nsAString& aIdentifier) override
{
return UnimplementedProperty();
}
NS_IMETHOD SetFilenameIdentifier(const nsAString& aIdentifier) override
{
return UnimplementedProperty();
}
NS_IMETHOD GetProcessIdentifier(int32_t *aIdentifier) override
{
return UnimplementedProperty();
}
NS_IMETHOD SetProcessIdentifier(int32_t aIdentifier) override
{
return UnimplementedProperty();
}
NS_IMETHOD GetGcLog(nsIFile** aPath) override
{
return UnimplementedProperty();
}
NS_IMETHOD GetCcLog(nsIFile** aPath) override
{
return UnimplementedProperty();
}
private:
~CycleCollectWithLogsChild()
{
if (mGCLog) {
fclose(mGCLog);
mGCLog = nullptr;
}
if (mCCLog) {
fclose(mCCLog);
mCCLog = nullptr;
}
// The XPCOM refcount drives the IPC lifecycle; see also
// DeallocPCycleCollectWithLogsChild.
Unused << Send__delete__(this);
}
nsresult UnimplementedProperty()
{
MOZ_ASSERT(false, "This object is a remote GC/CC logger;"
" this property isn't meaningful.");
return NS_ERROR_UNEXPECTED;
}
FILE* mGCLog;
FILE* mCCLog;
};
NS_IMPL_ISUPPORTS(CycleCollectWithLogsChild, nsICycleCollectorLogSink);
class AlertObserver
{
public:
AlertObserver(nsIObserver *aObserver, const nsString& aData)
: mObserver(aObserver)
, mData(aData)
{
}
~AlertObserver() {}
bool ShouldRemoveFrom(nsIObserver* aObserver,
const nsString& aData) const
{
return (mObserver == aObserver && mData == aData);
}
bool Observes(const nsString& aData) const
{
return mData.Equals(aData);
}
bool Notify(const nsCString& aType) const
{
mObserver->Observe(nullptr, aType.get(), mData.get());
return true;
}
private:
nsCOMPtr<nsIObserver> mObserver;
nsString mData;
};
class ConsoleListener final : public nsIConsoleListener
{
public:
explicit ConsoleListener(ContentChild* aChild)
: mChild(aChild) {}
NS_DECL_ISUPPORTS
NS_DECL_NSICONSOLELISTENER
private:
~ConsoleListener() {}
ContentChild* mChild;
friend class ContentChild;
};
NS_IMPL_ISUPPORTS(ConsoleListener, nsIConsoleListener)
// Before we send the error to the parent process (which
// involves copying the memory), truncate any long lines. CSS
// errors in particular share the memory for long lines with
// repeated errors, but the IPC communication we're about to do
// will break that sharing, so we better truncate now.
static void
TruncateString(nsAString& aString)
{
if (aString.Length() > 1000) {
aString.Truncate(1000);
}
}
NS_IMETHODIMP
ConsoleListener::Observe(nsIConsoleMessage* aMessage)
{
if (!mChild) {
return NS_OK;
}
nsCOMPtr<nsIScriptError> scriptError = do_QueryInterface(aMessage);
if (scriptError) {
nsAutoString msg, sourceName, sourceLine;
nsXPIDLCString category;
uint32_t lineNum, colNum, flags;
nsresult rv = scriptError->GetErrorMessage(msg);
NS_ENSURE_SUCCESS(rv, rv);
TruncateString(msg);
rv = scriptError->GetSourceName(sourceName);
NS_ENSURE_SUCCESS(rv, rv);
TruncateString(sourceName);
rv = scriptError->GetSourceLine(sourceLine);
NS_ENSURE_SUCCESS(rv, rv);
TruncateString(sourceLine);
rv = scriptError->GetCategory(getter_Copies(category));
NS_ENSURE_SUCCESS(rv, rv);
rv = scriptError->GetLineNumber(&lineNum);
NS_ENSURE_SUCCESS(rv, rv);
rv = scriptError->GetColumnNumber(&colNum);
NS_ENSURE_SUCCESS(rv, rv);
rv = scriptError->GetFlags(&flags);
NS_ENSURE_SUCCESS(rv, rv);
mChild->SendScriptError(msg, sourceName, sourceLine,
lineNum, colNum, flags, category);
return NS_OK;
}
nsXPIDLString msg;
nsresult rv = aMessage->GetMessageMoz(getter_Copies(msg));
NS_ENSURE_SUCCESS(rv, rv);
mChild->SendConsoleMessage(msg);
return NS_OK;
}
class BackgroundChildPrimer final :
public nsIIPCBackgroundChildCreateCallback
{
public:
BackgroundChildPrimer()
{ }
NS_DECL_ISUPPORTS
private:
~BackgroundChildPrimer()
{ }
virtual void
ActorCreated(PBackgroundChild* aActor) override
{
MOZ_ASSERT(aActor, "Failed to create a PBackgroundChild actor!");
}
virtual void
ActorFailed() override
{
MOZ_CRASH("Failed to create a PBackgroundChild actor!");
}
};
NS_IMPL_ISUPPORTS(BackgroundChildPrimer, nsIIPCBackgroundChildCreateCallback)
ContentChild* ContentChild::sSingleton;
ContentChild::ContentChild()
: mID(uint64_t(-1))
#if defined(XP_WIN) && defined(ACCESSIBILITY)
, mMsaaID(0)
#endif
, mCanOverrideProcessName(true)
, mIsAlive(true)
, mShuttingDown(false)
{
// This process is a content process, so it's clearly running in
// multiprocess mode!
nsDebugImpl::SetMultiprocessMode("Child");
}
ContentChild::~ContentChild()
{
#ifndef NS_FREE_PERMANENT_DATA
NS_RUNTIMEABORT("Content Child shouldn't be destroyed.");
#endif
}
NS_INTERFACE_MAP_BEGIN(ContentChild)
NS_INTERFACE_MAP_ENTRY(nsIContentChild)
NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentChild)
NS_INTERFACE_MAP_END
bool
ContentChild::Init(MessageLoop* aIOLoop,
base::ProcessId aParentPid,
IPC::Channel* aChannel)
{
#ifdef MOZ_WIDGET_GTK
// We need to pass a display down to gtk_init because it's not going to
// use the one from the environment on its own when deciding which backend
// to use, and when starting under XWayland, it may choose to start with
// the wayland backend instead of the x11 backend.
// The DISPLAY environment variable is normally set by the parent process.
char* display_name = PR_GetEnv("DISPLAY");
if (display_name) {
int argc = 3;
char option_name[] = "--display";
char* argv[] = {
// argv0 is unused because g_set_prgname() was called in
// XRE_InitChildProcess().
nullptr,
option_name,
display_name,
nullptr
};
char** argvp = argv;
gtk_init(&argc, &argvp);
} else {
gtk_init(nullptr, nullptr);
}
#endif
#ifdef MOZ_X11
// Do this after initializing GDK, or GDK will install its own handler.
XRE_InstallX11ErrorHandler();
#endif
NS_ASSERTION(!sSingleton, "only one ContentChild per child");
// Once we start sending IPC messages, we need the thread manager to be
// initialized so we can deal with the responses. Do that here.
nsresult rv = nsThreadManager::get().Init();
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
if (!Open(aChannel, aParentPid, aIOLoop)) {
return false;
}
sSingleton = this;
// If communications with the parent have broken down, take the process
// down so it's not hanging around.
GetIPCChannel()->SetAbortOnError(true);
#if defined(XP_WIN) && defined(ACCESSIBILITY)
GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_A11Y_REENTRY);
#endif
#ifdef MOZ_X11
// Send the parent our X socket to act as a proxy reference for our X
// resources.
int xSocketFd = ConnectionNumber(DefaultXDisplay());
SendBackUpXResources(FileDescriptor(xSocketFd));
#endif
SendGetProcessAttributes(&mID, &mIsForApp, &mIsForBrowser);
InitProcessAttributes();
#ifdef NS_PRINTING
// Force the creation of the nsPrintingProxy so that it's IPC counterpart,
// PrintingParent, is always available for printing initiated from the parent.
RefPtr<nsPrintingProxy> printingProxy = nsPrintingProxy::GetInstance();
#endif
return true;
}
void
ContentChild::InitProcessAttributes()
{
SetProcessName(NS_LITERAL_STRING("Web Content"), true);
}
void
ContentChild::SetProcessName(const nsAString& aName, bool aDontOverride)
{
if (!mCanOverrideProcessName) {
return;
}
char* name;
if ((name = PR_GetEnv("MOZ_DEBUG_APP_PROCESS")) &&
aName.EqualsASCII(name)) {
#ifdef OS_POSIX
printf_stderr("\n\nCHILDCHILDCHILDCHILD\n [%s] debug me @%d\n\n", name,
getpid());
sleep(30);
#elif defined(OS_WIN)
// Windows has a decent JIT debugging story, so NS_DebugBreak does the
// right thing.
NS_DebugBreak(NS_DEBUG_BREAK,
"Invoking NS_DebugBreak() to debug child process",
nullptr, __FILE__, __LINE__);
#endif
}
mProcessName = aName;
mozilla::ipc::SetThisProcessName(NS_LossyConvertUTF16toASCII(aName).get());
if (aDontOverride) {
mCanOverrideProcessName = false;
}
}
NS_IMETHODIMP
ContentChild::ProvideWindow(mozIDOMWindowProxy* aParent,
uint32_t aChromeFlags,
bool aCalledFromJS,
bool aPositionSpecified,
bool aSizeSpecified,
nsIURI* aURI,
const nsAString& aName,
const nsACString& aFeatures,
bool aForceNoOpener,
bool* aWindowIsNew,
mozIDOMWindowProxy** aReturn)
{
return ProvideWindowCommon(nullptr, aParent, false, aChromeFlags,
aCalledFromJS, aPositionSpecified,
aSizeSpecified, aURI, aName, aFeatures,
aForceNoOpener, aWindowIsNew, aReturn);
}
nsresult
ContentChild::ProvideWindowCommon(TabChild* aTabOpener,
mozIDOMWindowProxy* aParent,
bool aIframeMoz,
uint32_t aChromeFlags,
bool aCalledFromJS,
bool aPositionSpecified,
bool aSizeSpecified,
nsIURI* aURI,
const nsAString& aName,
const nsACString& aFeatures,
bool aForceNoOpener,
bool* aWindowIsNew,
mozIDOMWindowProxy** aReturn)
{
*aReturn = nullptr;
nsAutoPtr<IPCTabContext> ipcContext;
TabId openerTabId = TabId(0);
if (aTabOpener) {
PopupIPCTabContext context;
openerTabId = aTabOpener->GetTabId();
context.opener() = openerTabId;
context.isMozBrowserElement() = aTabOpener->IsMozBrowserElement();
ipcContext = new IPCTabContext(context);
} else {
// It's possible to not have a TabChild opener in the case
// of ServiceWorker::OpenWindow.
UnsafeIPCTabContext unsafeTabContext;
ipcContext = new IPCTabContext(unsafeTabContext);
}
MOZ_ASSERT(ipcContext);
TabId tabId;
SendAllocateTabId(openerTabId,
*ipcContext,
GetID(),
&tabId);
TabContext newTabContext = aTabOpener ? *aTabOpener : TabContext();
RefPtr<TabChild> newChild = new TabChild(this, tabId,
newTabContext, aChromeFlags);
if (NS_FAILED(newChild->Init())) {
return NS_ERROR_ABORT;
}
if (aTabOpener) {
MOZ_ASSERT(ipcContext->type() == IPCTabContext::TPopupIPCTabContext);
ipcContext->get_PopupIPCTabContext().opener() = aTabOpener;
}
Unused << SendPBrowserConstructor(
// We release this ref in DeallocPBrowserChild
RefPtr<TabChild>(newChild).forget().take(),
tabId, *ipcContext, aChromeFlags,
GetID(), IsForApp(), IsForBrowser());
nsString name(aName);
nsAutoCString features(aFeatures);
nsTArray<FrameScriptInfo> frameScripts;
nsCString urlToLoad;
PRenderFrameChild* renderFrame = newChild->SendPRenderFrameConstructor();
TextureFactoryIdentifier textureFactoryIdentifier;
uint64_t layersId = 0;
if (aIframeMoz) {
MOZ_ASSERT(aTabOpener);
nsAutoCString url;
if (aURI) {
aURI->GetSpec(url);
} else {
// We can't actually send a nullptr up as the URI, since IPDL doesn't let us
// send nullptr's for primitives. We indicate that the nsString for the URI
// should be converted to a nullptr by voiding the string.
url.SetIsVoid(true);
}
newChild->SendBrowserFrameOpenWindow(aTabOpener, renderFrame, NS_ConvertUTF8toUTF16(url),
name, NS_ConvertUTF8toUTF16(features),
aWindowIsNew, &textureFactoryIdentifier,
&layersId);
} else {
nsAutoCString baseURIString;
if (aTabOpener) {
auto* opener = nsPIDOMWindowOuter::From(aParent);
nsCOMPtr<nsIDocument> doc = opener->GetDoc();
nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
if (!baseURI) {
NS_ERROR("nsIDocument didn't return a base URI");
return NS_ERROR_FAILURE;
}
baseURI->GetSpec(baseURIString);
}
auto* opener = nsPIDOMWindowOuter::From(aParent);
nsIDocShell* openerShell;
RefPtr<nsDocShell> openerDocShell;
float fullZoom = 1.0f;
if (opener && (openerShell = opener->GetDocShell())) {
openerDocShell = static_cast<nsDocShell*>(openerShell);
nsCOMPtr<nsIContentViewer> cv;
openerDocShell->GetContentViewer(getter_AddRefs(cv));
if (cv) {
cv->GetFullZoom(&fullZoom);
}
}
nsresult rv;
if (!SendCreateWindow(aTabOpener, newChild, renderFrame,
aChromeFlags, aCalledFromJS, aPositionSpecified,
aSizeSpecified,
features,
baseURIString,
openerDocShell
? openerDocShell->GetOriginAttributes()
: DocShellOriginAttributes(),
fullZoom,
&rv,
aWindowIsNew,
&frameScripts,
&urlToLoad,
&textureFactoryIdentifier,
&layersId)) {
PRenderFrameChild::Send__delete__(renderFrame);
return NS_ERROR_NOT_AVAILABLE;
}
if (NS_FAILED(rv)) {
PRenderFrameChild::Send__delete__(renderFrame);
return rv;
}
}
if (!*aWindowIsNew) {
PRenderFrameChild::Send__delete__(renderFrame);
return NS_ERROR_ABORT;
}
if (layersId == 0) { // if renderFrame is invalid.
PRenderFrameChild::Send__delete__(renderFrame);
renderFrame = nullptr;
}
ShowInfo showInfo(EmptyString(), false, false, true, false, 0, 0, 0);
auto* opener = nsPIDOMWindowOuter::From(aParent);
nsIDocShell* openerShell;
if (opener && (openerShell = opener->GetDocShell())) {
nsCOMPtr<nsILoadContext> context = do_QueryInterface(openerShell);
showInfo = ShowInfo(EmptyString(), false,
context->UsePrivateBrowsing(), true, false,
aTabOpener->mDPI, aTabOpener->mRounding,
aTabOpener->mDefaultScale);
}
// Set the opener window for this window before we start loading the document
// inside of it. We have to do this before loading the remote scripts, because
// they can poke at the document and cause the nsDocument to be created before
// the openerwindow
nsCOMPtr<mozIDOMWindowProxy> windowProxy = do_GetInterface(newChild->WebNavigation());
if (!aForceNoOpener && windowProxy && aParent) {
nsPIDOMWindowOuter* outer = nsPIDOMWindowOuter::From(windowProxy);
nsPIDOMWindowOuter* parent = nsPIDOMWindowOuter::From(aParent);
outer->SetOpenerWindow(parent, *aWindowIsNew);
}
// Unfortunately we don't get a window unless we've shown the frame. That's
// pretty bogus; see bug 763602.
newChild->DoFakeShow(textureFactoryIdentifier, layersId, renderFrame,
showInfo);
for (size_t i = 0; i < frameScripts.Length(); i++) {
FrameScriptInfo& info = frameScripts[i];
if (!newChild->RecvLoadRemoteScript(info.url(), info.runInGlobalScope())) {
MOZ_CRASH();
}
}
if (!urlToLoad.IsEmpty()) {
newChild->RecvLoadURL(urlToLoad, showInfo);
}
nsCOMPtr<mozIDOMWindowProxy> win = do_GetInterface(newChild->WebNavigation());
win.forget(aReturn);
return NS_OK;
}
void
ContentChild::GetProcessName(nsAString& aName) const
{
aName.Assign(mProcessName);
}
bool
ContentChild::IsAlive() const
{
return mIsAlive;
}
bool
ContentChild::IsShuttingDown() const
{
return mShuttingDown;
}
void
ContentChild::GetProcessName(nsACString& aName) const
{
aName.Assign(NS_ConvertUTF16toUTF8(mProcessName));
}
/* static */ void
ContentChild::AppendProcessId(nsACString& aName)
{
if (!aName.IsEmpty()) {
aName.Append(' ');
}
unsigned pid = getpid();
aName.Append(nsPrintfCString("(pid %u)", pid));
}
void
ContentChild::InitGraphicsDeviceData()
{
// Initialize the graphics platform. This may contact the parent process
// to read device preferences.
gfxPlatform::GetPlatform();
}
void
ContentChild::InitXPCOM()
{
// Do this as early as possible to get the parent process to initialize the
// background thread since we'll likely need database information very soon.
BackgroundChild::Startup();
nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
new BackgroundChildPrimer();
if (!BackgroundChild::GetOrCreateForCurrentThread(callback)) {
MOZ_CRASH("Failed to create PBackgroundChild!");
}
BlobChild::Startup(BlobChild::FriendKey());
nsCOMPtr<nsIConsoleService> svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
if (!svc) {
NS_WARNING("Couldn't acquire console service");
return;
}
mConsoleListener = new ConsoleListener(this);
if (NS_FAILED(svc->RegisterListener(mConsoleListener)))
NS_WARNING("Couldn't register console listener for child process");
bool isOffline, isLangRTL, haveBidiKeyboards;
bool isConnected;
ClipboardCapabilities clipboardCaps;
DomainPolicyClone domainPolicy;
StructuredCloneData initialData;
OptionalURIParams userContentSheetURL;
int32_t captivePortalState;
SendGetXPCOMProcessAttributes(&isOffline, &isConnected, &captivePortalState,
&isLangRTL, &haveBidiKeyboards,
&mAvailableDictionaries,
&clipboardCaps, &domainPolicy, &initialData,
&userContentSheetURL);
RecvSetOffline(isOffline);
RecvSetConnectivity(isConnected);
RecvSetCaptivePortalState(captivePortalState);
RecvBidiKeyboardNotify(isLangRTL, haveBidiKeyboards);
// Create the CPOW manager as soon as possible.
SendPJavaScriptConstructor();
if (domainPolicy.active()) {
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
MOZ_ASSERT(ssm);
ssm->ActivateDomainPolicyInternal(getter_AddRefs(mPolicy));
if (!mPolicy) {
MOZ_CRASH("Failed to activate domain policy.");
}
mPolicy->ApplyClone(&domainPolicy);
}
nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1"));
if (nsCOMPtr<nsIClipboardProxy> clipboardProxy = do_QueryInterface(clipboard)) {
clipboardProxy->SetCapabilities(clipboardCaps);
}
{
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
MOZ_CRASH();
}
ErrorResult rv;
JS::RootedValue data(jsapi.cx());
initialData.Read(jsapi.cx(), &data, rv);
if (NS_WARN_IF(rv.Failed())) {
MOZ_CRASH();
}
ProcessGlobal* global = ProcessGlobal::Get();
global->SetInitialProcessData(data);
}
// The stylesheet cache is not ready yet. Store this URL for future use.
nsCOMPtr<nsIURI> ucsURL = DeserializeURI(userContentSheetURL);
nsLayoutStylesheetCache::SetUserContentCSSURL(ucsURL);
// This will register cross-process observer.
mozilla::dom::time::InitializeDateCacheCleaner();
}
PMemoryReportRequestChild*
ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
const bool &aAnonymize,
const bool &aMinimizeMemoryUsage,
const MaybeFileDesc& aDMDFile)
{
MemoryReportRequestChild *actor =
new MemoryReportRequestChild(aAnonymize, aDMDFile);
actor->AddRef();
return actor;
}
class HandleReportCallback final : public nsIHandleReportCallback
{
public:
NS_DECL_ISUPPORTS
explicit HandleReportCallback(MemoryReportRequestChild* aActor,
const nsACString& aProcess)
: mActor(aActor)
, mProcess(aProcess)
{ }
NS_IMETHOD Callback(const nsACString& aProcess, const nsACString &aPath,
int32_t aKind, int32_t aUnits, int64_t aAmount,
const nsACString& aDescription,
nsISupports* aUnused) override
{
MemoryReport memreport(mProcess, nsCString(aPath), aKind, aUnits,
aAmount, nsCString(aDescription));
mActor->SendReport(memreport);
return NS_OK;
}
private:
~HandleReportCallback() {}
RefPtr<MemoryReportRequestChild> mActor;
const nsCString mProcess;
};
NS_IMPL_ISUPPORTS(
HandleReportCallback
, nsIHandleReportCallback
)
class FinishReportingCallback final : public nsIFinishReportingCallback
{
public:
NS_DECL_ISUPPORTS
explicit FinishReportingCallback(MemoryReportRequestChild* aActor)
: mActor(aActor)
{
}
NS_IMETHOD Callback(nsISupports* aUnused) override
{
bool sent = PMemoryReportRequestChild::Send__delete__(mActor);
return sent ? NS_OK : NS_ERROR_FAILURE;
}
private:
~FinishReportingCallback() {}
RefPtr<MemoryReportRequestChild> mActor;
};
NS_IMPL_ISUPPORTS(
FinishReportingCallback
, nsIFinishReportingCallback
)
bool
ContentChild::RecvPMemoryReportRequestConstructor(
PMemoryReportRequestChild* aChild,
const uint32_t& aGeneration,
const bool& aAnonymize,
const bool& aMinimizeMemoryUsage,
const MaybeFileDesc& aDMDFile)
{
MemoryReportRequestChild *actor =
static_cast<MemoryReportRequestChild*>(aChild);
DebugOnly<nsresult> rv;
if (aMinimizeMemoryUsage) {
nsCOMPtr<nsIMemoryReporterManager> mgr =
do_GetService("@mozilla.org/memory-reporter-manager;1");
rv = mgr->MinimizeMemoryUsage(actor);
// mgr will eventually call actor->Run()
} else {
rv = actor->Run();
}
// Bug 1295622: don't kill the process just because this failed.
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "actor operation failed");
return true;
}
NS_IMETHODIMP MemoryReportRequestChild::Run()
{
ContentChild *child = static_cast<ContentChild*>(Manager());
nsCOMPtr<nsIMemoryReporterManager> mgr =
do_GetService("@mozilla.org/memory-reporter-manager;1");
nsCString process;
child->GetProcessName(process);
child->AppendProcessId(process);
// Run the reporters. The callback will turn each measurement into a
// MemoryReport.
RefPtr<HandleReportCallback> handleReport =
new HandleReportCallback(this, process);
RefPtr<FinishReportingCallback> finishReporting =
new FinishReportingCallback(this);
nsresult rv =
mgr->GetReportsForThisProcessExtended(handleReport, nullptr, mAnonymize,
FileDescriptorToFILE(mDMDFile, "wb"),
finishReporting, nullptr);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"GetReportsForThisProcessExtended failed");
return rv;
}
bool
ContentChild::DeallocPMemoryReportRequestChild(PMemoryReportRequestChild* actor)
{
static_cast<MemoryReportRequestChild*>(actor)->Release();
return true;
}
PCycleCollectWithLogsChild*
ContentChild::AllocPCycleCollectWithLogsChild(const bool& aDumpAllTraces,
const FileDescriptor& aGCLog,
const FileDescriptor& aCCLog)
{
CycleCollectWithLogsChild* actor = new CycleCollectWithLogsChild(aGCLog, aCCLog);
// Return actor with refcount 0, which is safe because it has a non-XPCOM type.
return actor;
}
bool
ContentChild::RecvPCycleCollectWithLogsConstructor(PCycleCollectWithLogsChild* aActor,
const bool& aDumpAllTraces,
const FileDescriptor& aGCLog,
const FileDescriptor& aCCLog)
{
// Take a reference here, where the XPCOM type is regained.
RefPtr<CycleCollectWithLogsChild> sink = static_cast<CycleCollectWithLogsChild*>(aActor);
nsCOMPtr<nsIMemoryInfoDumper> dumper = do_GetService("@mozilla.org/memory-info-dumper;1");
dumper->DumpGCAndCCLogsToSink(aDumpAllTraces, sink);
// The actor's destructor is called when the last reference goes away...
return true;
}
bool
ContentChild::DeallocPCycleCollectWithLogsChild(PCycleCollectWithLogsChild* /* aActor */)
{
// ...so when we get here, there's nothing for us to do.
//
// Also, we're already in ~CycleCollectWithLogsChild (q.v.) at
// this point, so we shouldn't touch the actor in any case.
return true;
}
#ifdef MOZ_ENABLE_NPAPI
mozilla::plugins::PPluginModuleParent*
ContentChild::AllocPPluginModuleParent(mozilla::ipc::Transport* aTransport,
base::ProcessId aOtherProcess)
{
return plugins::PluginModuleContentParent::Initialize(aTransport, aOtherProcess);
}
#endif
PContentBridgeChild*
ContentChild::AllocPContentBridgeChild(mozilla::ipc::Transport* aTransport,
base::ProcessId aOtherProcess)
{
return ContentBridgeChild::Create(aTransport, aOtherProcess);
}
PContentBridgeParent*
ContentChild::AllocPContentBridgeParent(mozilla::ipc::Transport* aTransport,
base::ProcessId aOtherProcess)
{
MOZ_ASSERT(!mLastBridge);
mLastBridge = static_cast<ContentBridgeParent*>(
ContentBridgeParent::Create(aTransport, aOtherProcess));
return mLastBridge;
}
#ifdef MOZ_GMP
PGMPServiceChild*
ContentChild::AllocPGMPServiceChild(mozilla::ipc::Transport* aTransport,
base::ProcessId aOtherProcess)
{
return GMPServiceChild::Create(aTransport, aOtherProcess);
}
bool
ContentChild::RecvGMPsChanged(nsTArray<GMPCapabilityData>&& capabilities)
{
GeckoMediaPluginServiceChild::UpdateGMPCapabilities(Move(capabilities));
return true;
}
#endif
bool
ContentChild::RecvInitRendering(Endpoint<PCompositorBridgeChild>&& aCompositor,
Endpoint<PImageBridgeChild>&& aImageBridge,
Endpoint<PVideoDecoderManagerChild>&& aVideoManager)
{
if (!CompositorBridgeChild::InitForContent(Move(aCompositor))) {
return false;
}
if (!ImageBridgeChild::InitForContent(Move(aImageBridge))) {
return false;
}
VideoDecoderManagerChild::InitForContent(Move(aVideoManager));
return true;
}
bool
ContentChild::RecvReinitRendering(Endpoint<PCompositorBridgeChild>&& aCompositor,
Endpoint<PImageBridgeChild>&& aImageBridge,
Endpoint<PVideoDecoderManagerChild>&& aVideoManager)
{
nsTArray<RefPtr<TabChild>> tabs = TabChild::GetAll();
// Zap all the old layer managers we have lying around.
for (const auto& tabChild : tabs) {
if (tabChild->LayersId()) {
tabChild->InvalidateLayers();
}
}
// Re-establish singleton bridges to the compositor.
if (!CompositorBridgeChild::ReinitForContent(Move(aCompositor))) {
return false;
}
if (!ImageBridgeChild::ReinitForContent(Move(aImageBridge))) {
return false;
}
// Establish new PLayerTransactions.
for (const auto& tabChild : tabs) {
if (tabChild->LayersId()) {
tabChild->ReinitRendering();
}
}
VideoDecoderManagerChild::InitForContent(Move(aVideoManager));
return true;
}
PBackgroundChild*
ContentChild::AllocPBackgroundChild(Transport* aTransport,
ProcessId aOtherProcess)
{
return BackgroundChild::Alloc(aTransport, aOtherProcess);
}
PProcessHangMonitorChild*
ContentChild::AllocPProcessHangMonitorChild(Transport* aTransport,
ProcessId aOtherProcess)
{
return CreateHangMonitorChild(aTransport, aOtherProcess);
}
bool
ContentChild::RecvSetProcessSandbox(const MaybeFileDesc& aBroker)
{
// We may want to move the sandbox initialization somewhere else
// at some point; see bug 880808.
return true;
}
bool
ContentChild::RecvNotifyLayerAllocated(const dom::TabId& aTabId, const uint64_t& aLayersId)
{
if (!CompositorBridgeChild::Get()->IPCOpen()) {
return true;
}
APZChild* apz = ContentProcessController::Create(aTabId);
return CompositorBridgeChild::Get()->SendPAPZConstructor(apz, aLayersId);
}
bool
ContentChild::RecvSpeakerManagerNotify()
{
return false;
}
bool
ContentChild::RecvBidiKeyboardNotify(const bool& aIsLangRTL,
const bool& aHaveBidiKeyboards)
{
// bidi is always of type PuppetBidiKeyboard* (because in the child, the only
// possible implementation of nsIBidiKeyboard is PuppetBidiKeyboard).
PuppetBidiKeyboard* bidi = static_cast<PuppetBidiKeyboard*>(nsContentUtils::GetBidiKeyboard());
if (bidi) {
bidi->SetBidiKeyboardInfo(aIsLangRTL, aHaveBidiKeyboards);
}
return true;
}
static CancelableRunnable* sFirstIdleTask;
static void FirstIdle(void)
{
MOZ_ASSERT(sFirstIdleTask);
sFirstIdleTask = nullptr;
ContentChild::GetSingleton()->SendFirstIdle();
}
mozilla::jsipc::PJavaScriptChild *
ContentChild::AllocPJavaScriptChild()
{
MOZ_ASSERT(ManagedPJavaScriptChild().IsEmpty());
return nsIContentChild::AllocPJavaScriptChild();
}
bool
ContentChild::DeallocPJavaScriptChild(PJavaScriptChild *aChild)
{
return nsIContentChild::DeallocPJavaScriptChild(aChild);
}
PBrowserChild*
ContentChild::AllocPBrowserChild(const TabId& aTabId,
const IPCTabContext& aContext,
const uint32_t& aChromeFlags,
const ContentParentId& aCpID,
const bool& aIsForApp,
const bool& aIsForBrowser)
{
return nsIContentChild::AllocPBrowserChild(aTabId,
aContext,
aChromeFlags,
aCpID,
aIsForApp,
aIsForBrowser);
}
bool
ContentChild::SendPBrowserConstructor(PBrowserChild* aActor,
const TabId& aTabId,
const IPCTabContext& aContext,
const uint32_t& aChromeFlags,
const ContentParentId& aCpID,
const bool& aIsForApp,
const bool& aIsForBrowser)
{
if (IsShuttingDown()) {
return false;
}
return PContentChild::SendPBrowserConstructor(aActor,
aTabId,
aContext,
aChromeFlags,
aCpID,
aIsForApp,
aIsForBrowser);
}
bool
ContentChild::RecvPBrowserConstructor(PBrowserChild* aActor,
const TabId& aTabId,
const IPCTabContext& aContext,
const uint32_t& aChromeFlags,
const ContentParentId& aCpID,
const bool& aIsForApp,
const bool& aIsForBrowser)
{
MOZ_ASSERT(!IsShuttingDown());
// This runs after AllocPBrowserChild() returns and the IPC machinery for this
// PBrowserChild has been set up.
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
nsITabChild* tc =
static_cast<nsITabChild*>(static_cast<TabChild*>(aActor));
os->NotifyObservers(tc, "tab-child-created", nullptr);
}
static bool hasRunOnce = false;
if (!hasRunOnce) {
hasRunOnce = true;
MOZ_ASSERT(!sFirstIdleTask);
RefPtr<CancelableRunnable> firstIdleTask = NewCancelableRunnableFunction(FirstIdle);
sFirstIdleTask = firstIdleTask;
MessageLoop::current()->PostIdleTask(firstIdleTask.forget());
// Redo InitProcessAttributes() when the app or browser is really
// launching so the attributes will be correct.
mID = aCpID;
mIsForApp = aIsForApp;
mIsForBrowser = aIsForBrowser;
InitProcessAttributes();
}
return true;
}
void
ContentChild::GetAvailableDictionaries(InfallibleTArray<nsString>& aDictionaries)
{
aDictionaries = mAvailableDictionaries;
}
PFileDescriptorSetChild*
ContentChild::SendPFileDescriptorSetConstructor(const FileDescriptor& aFD)
{
if (IsShuttingDown()) {
return nullptr;
}
return PContentChild::SendPFileDescriptorSetConstructor(aFD);
}
PFileDescriptorSetChild*
ContentChild::AllocPFileDescriptorSetChild(const FileDescriptor& aFD)
{
return nsIContentChild::AllocPFileDescriptorSetChild(aFD);
}
bool
ContentChild::DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor)
{
return nsIContentChild::DeallocPFileDescriptorSetChild(aActor);
}
bool
ContentChild::DeallocPBrowserChild(PBrowserChild* aIframe)
{
return nsIContentChild::DeallocPBrowserChild(aIframe);
}
PBlobChild*
ContentChild::AllocPBlobChild(const BlobConstructorParams& aParams)
{
return nsIContentChild::AllocPBlobChild(aParams);
}
mozilla::PRemoteSpellcheckEngineChild *
ContentChild::AllocPRemoteSpellcheckEngineChild()
{
MOZ_CRASH("Default Constructor for PRemoteSpellcheckEngineChild should never be called");
return nullptr;
}
bool
ContentChild::DeallocPRemoteSpellcheckEngineChild(PRemoteSpellcheckEngineChild *child)
{
delete child;
return true;
}
bool
ContentChild::DeallocPBlobChild(PBlobChild* aActor)
{
return nsIContentChild::DeallocPBlobChild(aActor);
}
PBlobChild*
ContentChild::SendPBlobConstructor(PBlobChild* aActor,
const BlobConstructorParams& aParams)
{
if (IsShuttingDown()) {
return nullptr;
}
return PContentChild::SendPBlobConstructor(aActor, aParams);
}
bool
ContentChild::RecvNotifyEmptyHTTPCache()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->NotifyObservers(nullptr, "cacheservice:empty-cache", nullptr);
return true;
}
PHalChild*
ContentChild::AllocPHalChild()
{
return CreateHalChild();
}
bool
ContentChild::DeallocPHalChild(PHalChild* aHal)
{
delete aHal;
return true;
}
devtools::PHeapSnapshotTempFileHelperChild*
ContentChild::AllocPHeapSnapshotTempFileHelperChild()
{
return devtools::HeapSnapshotTempFileHelperChild::Create();
}
bool
ContentChild::DeallocPHeapSnapshotTempFileHelperChild(
devtools::PHeapSnapshotTempFileHelperChild* aHeapSnapshotHelper)
{
delete aHeapSnapshotHelper;
return true;
}
PTestShellChild*
ContentChild::AllocPTestShellChild()
{
return new TestShellChild();
}
bool
ContentChild::DeallocPTestShellChild(PTestShellChild* shell)
{
delete shell;
return true;
}
jsipc::CPOWManager*
ContentChild::GetCPOWManager()
{
if (PJavaScriptChild* c = LoneManagedOrNullAsserts(ManagedPJavaScriptChild())) {
return CPOWManagerFor(c);
}
return CPOWManagerFor(SendPJavaScriptConstructor());
}
bool
ContentChild::RecvPTestShellConstructor(PTestShellChild* actor)
{
return true;
}
PNeckoChild*
ContentChild::AllocPNeckoChild()
{
return new NeckoChild();
}
bool
ContentChild::DeallocPNeckoChild(PNeckoChild* necko)
{
delete necko;
return true;
}
PPrintingChild*
ContentChild::AllocPPrintingChild()
{
// The ContentParent should never attempt to allocate the nsPrintingProxy,
// which implements PPrintingChild. Instead, the nsPrintingProxy service is
// requested and instantiated via XPCOM, and the constructor of
// nsPrintingProxy sets up the IPC connection.
MOZ_CRASH("Should never get here!");
return nullptr;
}
bool
ContentChild::DeallocPPrintingChild(PPrintingChild* printing)
{
return true;
}
PSendStreamChild*
ContentChild::SendPSendStreamConstructor(PSendStreamChild* aActor)
{
if (IsShuttingDown()) {
return nullptr;
}
return PContentChild::SendPSendStreamConstructor(aActor);
}
PSendStreamChild*
ContentChild::AllocPSendStreamChild()
{
return nsIContentChild::AllocPSendStreamChild();
}
bool
ContentChild::DeallocPSendStreamChild(PSendStreamChild* aActor)
{
return nsIContentChild::DeallocPSendStreamChild(aActor);
}
PScreenManagerChild*
ContentChild::AllocPScreenManagerChild(uint32_t* aNumberOfScreens,
float* aSystemDefaultScale,
bool* aSuccess)
{
// The ContentParent should never attempt to allocate the
// nsScreenManagerProxy. Instead, the nsScreenManagerProxy
// service is requested and instantiated via XPCOM, and the
// constructor of nsScreenManagerProxy sets up the IPC connection.
MOZ_CRASH("Should never get here!");
return nullptr;
}
bool
ContentChild::DeallocPScreenManagerChild(PScreenManagerChild* aService)
{
// nsScreenManagerProxy is AddRef'd in its constructor.
nsScreenManagerProxy *child = static_cast<nsScreenManagerProxy*>(aService);
child->Release();
return true;
}
PPSMContentDownloaderChild*
ContentChild::AllocPPSMContentDownloaderChild(const uint32_t& aCertType)
{
// NB: We don't need aCertType in the child actor.
RefPtr<PSMContentDownloaderChild> child = new PSMContentDownloaderChild();
return child.forget().take();
}
bool
ContentChild::DeallocPPSMContentDownloaderChild(PPSMContentDownloaderChild* aListener)
{
auto* listener = static_cast<PSMContentDownloaderChild*>(aListener);
RefPtr<PSMContentDownloaderChild> child = dont_AddRef(listener);
return true;
}
PExternalHelperAppChild*
ContentChild::AllocPExternalHelperAppChild(const OptionalURIParams& uri,
const nsCString& aMimeContentType,
const nsCString& aContentDisposition,
const uint32_t& aContentDispositionHint,
const nsString& aContentDispositionFilename,
const bool& aForceSave,
const int64_t& aContentLength,
const bool& aWasFileChannel,
const OptionalURIParams& aReferrer,
PBrowserChild* aBrowser)
{
ExternalHelperAppChild *child = new ExternalHelperAppChild();
child->AddRef();
return child;
}
bool
ContentChild::DeallocPExternalHelperAppChild(PExternalHelperAppChild* aService)
{
ExternalHelperAppChild *child = static_cast<ExternalHelperAppChild*>(aService);
child->Release();
return true;
}
PHandlerServiceChild*
ContentChild::AllocPHandlerServiceChild()
{
HandlerServiceChild* actor = new HandlerServiceChild();
actor->AddRef();
return actor;
}
bool ContentChild::DeallocPHandlerServiceChild(PHandlerServiceChild* aHandlerServiceChild)
{
static_cast<HandlerServiceChild*>(aHandlerServiceChild)->Release();
return true;
}
media::PMediaChild*
ContentChild::AllocPMediaChild()
{
return media::AllocPMediaChild();
}
bool
ContentChild::DeallocPMediaChild(media::PMediaChild *aActor)
{
return media::DeallocPMediaChild(aActor);
}
PStorageChild*
ContentChild::AllocPStorageChild()
{
MOZ_CRASH("We should never be manually allocating PStorageChild actors");
return nullptr;
}
bool
ContentChild::DeallocPStorageChild(PStorageChild* aActor)
{
DOMStorageDBChild* child = static_cast<DOMStorageDBChild*>(aActor);
child->ReleaseIPDLReference();
return true;
}
PSpeechSynthesisChild*
ContentChild::AllocPSpeechSynthesisChild()
{
#ifdef MOZ_WEBSPEECH
MOZ_CRASH("No one should be allocating PSpeechSynthesisChild actors");
#else
return nullptr;
#endif
}
bool
ContentChild::DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor)
{
#ifdef MOZ_WEBSPEECH
delete aActor;
return true;
#else
return false;
#endif
}
PWebrtcGlobalChild *
ContentChild::AllocPWebrtcGlobalChild()
{
#ifdef MOZ_WEBRTC
WebrtcGlobalChild *child = new WebrtcGlobalChild();
return child;
#else
return nullptr;
#endif
}
bool
ContentChild::DeallocPWebrtcGlobalChild(PWebrtcGlobalChild *aActor)
{
#ifdef MOZ_WEBRTC
delete static_cast<WebrtcGlobalChild*>(aActor);
return true;
#else
return false;
#endif
}
bool
ContentChild::RecvRegisterChrome(InfallibleTArray<ChromePackage>&& packages,
InfallibleTArray<SubstitutionMapping>&& resources,
InfallibleTArray<OverrideMapping>&& overrides,
const nsCString& locale,
const bool& reset)
{
nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
nsChromeRegistryContent* chromeRegistry =
static_cast<nsChromeRegistryContent*>(registrySvc.get());
chromeRegistry->RegisterRemoteChrome(packages, resources, overrides,
locale, reset);
return true;
}
bool
ContentChild::RecvRegisterChromeItem(const ChromeRegistryItem& item)
{
nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
nsChromeRegistryContent* chromeRegistry =
static_cast<nsChromeRegistryContent*>(registrySvc.get());
switch (item.type()) {
case ChromeRegistryItem::TChromePackage:
chromeRegistry->RegisterPackage(item.get_ChromePackage());
break;
case ChromeRegistryItem::TOverrideMapping:
chromeRegistry->RegisterOverride(item.get_OverrideMapping());
break;
case ChromeRegistryItem::TSubstitutionMapping:
chromeRegistry->RegisterSubstitution(item.get_SubstitutionMapping());
break;
default:
MOZ_ASSERT(false, "bad chrome item");
return false;
}
return true;
}
bool
ContentChild::RecvClearImageCache(const bool& privateLoader, const bool& chrome)
{
imgLoader* loader = privateLoader ? imgLoader::PrivateBrowsingLoader() :
imgLoader::NormalLoader();
loader->ClearCache(chrome);
return true;
}
bool
ContentChild::RecvSetOffline(const bool& offline)
{
nsCOMPtr<nsIIOService> io (do_GetIOService());
NS_ASSERTION(io, "IO Service can not be null");
io->SetOffline(offline);
return true;
}
bool
ContentChild::RecvSetConnectivity(const bool& connectivity)
{
nsCOMPtr<nsIIOService> io(do_GetIOService());
nsCOMPtr<nsIIOServiceInternal> ioInternal(do_QueryInterface(io));
NS_ASSERTION(ioInternal, "IO Service can not be null");
ioInternal->SetConnectivity(connectivity);
return true;
}
bool
ContentChild::RecvSetCaptivePortalState(const int32_t& aState)
{
nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID);
if (!cps) {
return true;
}
mozilla::net::CaptivePortalService *portal =
static_cast<mozilla::net::CaptivePortalService*>(cps.get());
portal->SetStateInChild(aState);
return true;
}
void
ContentChild::ActorDestroy(ActorDestroyReason why)
{
if (mForceKillTimer) {
mForceKillTimer->Cancel();
mForceKillTimer = nullptr;
}
if (AbnormalShutdown == why) {
NS_WARNING("shutting down early because of crash!");
ProcessChild::QuickExit();
}
#ifndef NS_FREE_PERMANENT_DATA
// In release builds, there's no point in the content process
// going through the full XPCOM shutdown path, because it doesn't
// keep persistent state.
ProcessChild::QuickExit();
#else
if (sFirstIdleTask) {
sFirstIdleTask->Cancel();
}
nsHostObjectProtocolHandler::RemoveDataEntries();
mAlertObservers.Clear();
mIdleObservers.Clear();
nsCOMPtr<nsIConsoleService> svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
if (svc) {
svc->UnregisterListener(mConsoleListener);
mConsoleListener->mChild = nullptr;
}
mIsAlive = false;
XRE_ShutdownChildProcess();
#endif // NS_FREE_PERMANENT_DATA
}
void
ContentChild::ProcessingError(Result aCode, const char* aReason)
{
switch (aCode) {
case MsgDropped:
NS_WARNING("MsgDropped in ContentChild");
return;
case MsgNotKnown:
case MsgNotAllowed:
case MsgPayloadError:
case MsgProcessingError:
case MsgRouteError:
case MsgValueError:
break;
default:
NS_RUNTIMEABORT("not reached");
}
NS_RUNTIMEABORT("Content child abort due to IPC error");
}
nsresult
ContentChild::AddRemoteAlertObserver(const nsString& aData,
nsIObserver* aObserver)
{
NS_ASSERTION(aObserver, "Adding a null observer?");
mAlertObservers.AppendElement(new AlertObserver(aObserver, aData));
return NS_OK;
}
bool
ContentChild::RecvPreferenceUpdate(const PrefSetting& aPref)
{
Preferences::SetPreference(aPref);
return true;
}
bool
ContentChild::RecvVarUpdate(const GfxVarUpdate& aVar)
{
gfx::gfxVars::ApplyUpdate(aVar);
return true;
}
bool
ContentChild::RecvDataStoragePut(const nsString& aFilename,
const DataStorageItem& aItem)
{
RefPtr<DataStorage> storage = DataStorage::GetIfExists(aFilename);
if (storage) {
storage->Put(aItem.key(), aItem.value(), aItem.type());
}
return true;
}
bool
ContentChild::RecvDataStorageRemove(const nsString& aFilename,
const nsCString& aKey,
const DataStorageType& aType)
{
RefPtr<DataStorage> storage = DataStorage::GetIfExists(aFilename);
if (storage) {
storage->Remove(aKey, aType);
}
return true;
}
bool
ContentChild::RecvDataStorageClear(const nsString& aFilename)
{
RefPtr<DataStorage> storage = DataStorage::GetIfExists(aFilename);
if (storage) {
storage->Clear();
}
return true;
}
bool
ContentChild::RecvNotifyAlertsObserver(const nsCString& aType, const nsString& aData)
{
for (uint32_t i = 0; i < mAlertObservers.Length();
/*we mutate the array during the loop; ++i iff no mutation*/) {
AlertObserver* observer = mAlertObservers[i];
if (observer->Observes(aData) && observer->Notify(aType)) {
// if aType == alertfinished, this alert is done. we can
// remove the observer.
if (aType.Equals(nsDependentCString("alertfinished"))) {
mAlertObservers.RemoveElementAt(i);
continue;
}
}
++i;
}
return true;
}
bool
ContentChild::RecvNotifyVisited(const URIParams& aURI)
{
nsCOMPtr<nsIURI> newURI = DeserializeURI(aURI);
if (!newURI) {
return false;
}
nsCOMPtr<IHistory> history = services::GetHistoryService();
if (history) {
history->NotifyVisited(newURI);
}
return true;
}
bool
ContentChild::RecvLoadProcessScript(const nsString& aURL)
{
ProcessGlobal* global = ProcessGlobal::Get();
global->LoadScript(aURL);
return true;
}
bool
ContentChild::RecvAsyncMessage(const nsString& aMsg,
InfallibleTArray<CpowEntry>&& aCpows,
const IPC::Principal& aPrincipal,
const ClonedMessageData& aData)
{
RefPtr<nsFrameMessageManager> cpm =
nsFrameMessageManager::GetChildProcessManager();
if (cpm) {
StructuredCloneData data;
ipc::UnpackClonedMessageDataForChild(aData, data);
CrossProcessCpowHolder cpows(this, aCpows);
cpm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(cpm.get()),
nullptr, aMsg, false, &data, &cpows, aPrincipal,
nullptr);
}
return true;
}
bool
ContentChild::RecvGeolocationUpdate(const GeoPosition& somewhere)
{
nsCOMPtr<nsIGeolocationUpdate> gs =
do_GetService("@mozilla.org/geolocation/service;1");
if (!gs) {
return true;
}
nsCOMPtr<nsIDOMGeoPosition> position = somewhere;
gs->Update(position);
return true;
}
bool
ContentChild::RecvGeolocationError(const uint16_t& errorCode)
{
nsCOMPtr<nsIGeolocationUpdate> gs =
do_GetService("@mozilla.org/geolocation/service;1");
if (!gs) {
return true;
}
gs->NotifyError(errorCode);
return true;
}
bool
ContentChild::RecvUpdateDictionaryList(InfallibleTArray<nsString>&& aDictionaries)
{
mAvailableDictionaries = aDictionaries;
mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking();
return true;
}
bool
ContentChild::RecvAddPermission(const IPC::Permission& permission)
{
#if MOZ_PERMISSIONS
nsCOMPtr<nsIPermissionManager> permissionManagerIface =
services::GetPermissionManager();
nsPermissionManager* permissionManager =
static_cast<nsPermissionManager*>(permissionManagerIface.get());
MOZ_ASSERT(permissionManager,
"We have no permissionManager in the Content process !");
// note we do not need to force mUserContextId to the default here because
// the permission manager does that internally.
nsAutoCString originNoSuffix;
PrincipalOriginAttributes attrs;
bool success = attrs.PopulateFromOrigin(permission.origin, originNoSuffix);
NS_ENSURE_TRUE(success, false);
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
NS_ENSURE_SUCCESS(rv, true);
nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs);
// child processes don't care about modification time.
int64_t modificationTime = 0;
permissionManager->AddInternal(principal,
nsCString(permission.type),
permission.capability,
0,
permission.expireType,
permission.expireTime,
modificationTime,
nsPermissionManager::eNotify,
nsPermissionManager::eNoDBOperation);
#endif
return true;
}
bool
ContentChild::RecvFlushMemory(const nsString& reason)
{
nsCOMPtr<nsIObserverService> os =
mozilla::services::GetObserverService();
if (os) {
os->NotifyObservers(nullptr, "memory-pressure", reason.get());
}
return true;
}
bool
ContentChild::RecvActivateA11y(const uint32_t& aMsaaID)
{
#ifdef ACCESSIBILITY
#ifdef XP_WIN
MOZ_ASSERT(aMsaaID != 0);
mMsaaID = aMsaaID;
#endif // XP_WIN
// Start accessibility in content process if it's running in chrome
// process.
GetOrCreateAccService(nsAccessibilityService::eMainProcess);
#endif // ACCESSIBILITY
return true;
}
bool
ContentChild::RecvShutdownA11y()
{
#ifdef ACCESSIBILITY
// Try to shutdown accessibility in content process if it's shutting down in
// chrome process.
MaybeShutdownAccService(nsAccessibilityService::eMainProcess);
#endif
return true;
}
bool
ContentChild::RecvGarbageCollect()
{
// Rebroadcast the "child-gc-request" so that workers will GC.
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->NotifyObservers(nullptr, "child-gc-request", nullptr);
}
nsJSContext::GarbageCollectNow(JS::gcreason::DOM_IPC);
return true;
}
bool
ContentChild::RecvCycleCollect()
{
// Rebroadcast the "child-cc-request" so that workers will CC.
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->NotifyObservers(nullptr, "child-cc-request", nullptr);
}
nsJSContext::CycleCollectNow();
return true;
}
static void
PreloadSlowThings()
{
// This fetches and creates all the built-in stylesheets.
//
// XXXheycam In the future we might want to preload the Servo-flavoured
// UA sheets too, but for now that will be a waste of time.
nsLayoutStylesheetCache::For(StyleBackendType::Gecko)->UserContentSheet();
TabChild::PreloadSlowThings();
}
bool
ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID,
const nsCString& name, const nsCString& UAName,
const nsCString& ID, const nsCString& vendor)
{
mAppInfo.version.Assign(version);
mAppInfo.buildID.Assign(buildID);
mAppInfo.name.Assign(name);
mAppInfo.UAName.Assign(UAName);
mAppInfo.ID.Assign(ID);
mAppInfo.vendor.Assign(vendor);
return true;
}
bool
ContentChild::RecvAppInit()
{
if (!Preferences::GetBool("dom.ipc.processPrelaunch.enabled", false)) {
return true;
}
// If we're part of the mozbrowser machinery, go ahead and start
// preloading things. We can only do this for mozbrowser because
// 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) {
PreloadSlowThings();
}
return true;
}
bool
ContentChild::RecvInitServiceWorkers(const ServiceWorkerConfiguration& aConfig)
{
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
if (!swm) {
// browser shutdown began
return true;
}
swm->LoadRegistrations(aConfig.serviceWorkerRegistrations());
return true;
}
bool
ContentChild::RecvInitBlobURLs(nsTArray<BlobURLRegistrationData>&& aRegistrations)
{
for (uint32_t i = 0; i < aRegistrations.Length(); ++i) {
BlobURLRegistrationData& registration = aRegistrations[i];
RefPtr<BlobImpl> blobImpl =
static_cast<BlobChild*>(registration.blobChild())->GetBlobImpl();
MOZ_ASSERT(blobImpl);
nsHostObjectProtocolHandler::AddDataEntry(registration.url(),
registration.principal(),
blobImpl);
}
return true;
}
bool
ContentChild::RecvLastPrivateDocShellDestroyed()
{
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
return true;
}
bool
ContentChild::RecvVolumes(nsTArray<VolumeInfo>&& aVolumes)
{
return true;
}
bool
ContentChild::RecvFileSystemUpdate(const nsString& aFsName,
const nsString& aVolumeName,
const int32_t& aState,
const int32_t& aMountGeneration,
const bool& aIsMediaPresent,
const bool& aIsSharing,
const bool& aIsFormatting,
const bool& aIsFake,
const bool& aIsUnmounting,
const bool& aIsRemovable,
const bool& aIsHotSwappable)
{
// Remove warnings about unused arguments
Unused << aFsName;
Unused << aVolumeName;
Unused << aState;
Unused << aMountGeneration;
Unused << aIsMediaPresent;
Unused << aIsSharing;
Unused << aIsFormatting;
Unused << aIsFake;
Unused << aIsUnmounting;
Unused << aIsRemovable;
Unused << aIsHotSwappable;
return true;
}
bool
ContentChild::RecvVolumeRemoved(const nsString& aFsName)
{
// Remove warnings about unused arguments
Unused << aFsName;
return true;
}
bool
ContentChild::RecvNotifyProcessPriorityChanged(
const hal::ProcessPriority& aPriority)
{
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
NS_ENSURE_TRUE(os, true);
RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
props->SetPropertyAsInt32(NS_LITERAL_STRING("priority"),
static_cast<int32_t>(aPriority));
os->NotifyObservers(static_cast<nsIPropertyBag2*>(props),
"ipc:process-priority-changed", nullptr);
return true;
}
bool
ContentChild::RecvMinimizeMemoryUsage()
{
nsCOMPtr<nsIMemoryReporterManager> mgr =
do_GetService("@mozilla.org/memory-reporter-manager;1");
NS_ENSURE_TRUE(mgr, true);
Unused << mgr->MinimizeMemoryUsage(/* callback = */ nullptr);
return true;
}
bool
ContentChild::RecvNotifyPhoneStateChange(const nsString& aState)
{
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
os->NotifyObservers(nullptr, "phone-state-changed", aState.get());
}
return true;
}
void
ContentChild::AddIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS)
{
MOZ_ASSERT(aObserver, "null idle observer");
// Make sure aObserver isn't released while we wait for the parent
aObserver->AddRef();
SendAddIdleObserver(reinterpret_cast<uint64_t>(aObserver), aIdleTimeInS);
mIdleObservers.PutEntry(aObserver);
}
void
ContentChild::RemoveIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS)
{
MOZ_ASSERT(aObserver, "null idle observer");
SendRemoveIdleObserver(reinterpret_cast<uint64_t>(aObserver), aIdleTimeInS);
aObserver->Release();
mIdleObservers.RemoveEntry(aObserver);
}
bool
ContentChild::RecvNotifyIdleObserver(const uint64_t& aObserver,
const nsCString& aTopic,
const nsString& aTimeStr)
{
nsIObserver* observer = reinterpret_cast<nsIObserver*>(aObserver);
if (mIdleObservers.Contains(observer)) {
observer->Observe(nullptr, aTopic.get(), aTimeStr.get());
} else {
NS_WARNING("Received notification for an idle observer that was removed.");
}
return true;
}
bool
ContentChild::RecvLoadAndRegisterSheet(const URIParams& aURI, const uint32_t& aType)
{
nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
if (!uri) {
return true;
}
nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
if (sheetService) {
sheetService->LoadAndRegisterSheet(uri, aType);
}
return true;
}
bool
ContentChild::RecvUnregisterSheet(const URIParams& aURI, const uint32_t& aType)
{
nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
if (!uri) {
return true;
}
nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
if (sheetService) {
sheetService->UnregisterSheet(uri, aType);
}
return true;
}
POfflineCacheUpdateChild*
ContentChild::AllocPOfflineCacheUpdateChild(const URIParams& manifestURI,
const URIParams& documentURI,
const PrincipalInfo& aLoadingPrincipalInfo,
const bool& stickDocument)
{
NS_RUNTIMEABORT("unused");
return nullptr;
}
bool
ContentChild::DeallocPOfflineCacheUpdateChild(POfflineCacheUpdateChild* actor)
{
OfflineCacheUpdateChild* offlineCacheUpdate =
static_cast<OfflineCacheUpdateChild*>(actor);
NS_RELEASE(offlineCacheUpdate);
return true;
}
#ifdef MOZ_ENABLE_NPAPI
bool
ContentChild::RecvLoadPluginResult(const uint32_t& aPluginId,
const bool& aResult)
{
nsresult rv;
bool finalResult = aResult && SendConnectPluginBridge(aPluginId, &rv) &&
NS_SUCCEEDED(rv);
plugins::PluginModuleContentParent::OnLoadPluginResult(aPluginId,
finalResult);
return true;
}
bool
ContentChild::RecvAssociatePluginId(const uint32_t& aPluginId,
const base::ProcessId& aProcessId)
{
plugins::PluginModuleContentParent::AssociatePluginId(aPluginId, aProcessId);
return true;
}
#endif
bool
ContentChild::RecvDomainSetChanged(const uint32_t& aSetType,
const uint32_t& aChangeType,
const OptionalURIParams& aDomain)
{
if (aChangeType == ACTIVATE_POLICY) {
if (mPolicy) {
return true;
}
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
MOZ_ASSERT(ssm);
ssm->ActivateDomainPolicyInternal(getter_AddRefs(mPolicy));
return !!mPolicy;
} else if (!mPolicy) {
MOZ_ASSERT_UNREACHABLE("If the domain policy is not active yet,"
" the first message should be ACTIVATE_POLICY");
return false;
}
NS_ENSURE_TRUE(mPolicy, false);
if (aChangeType == DEACTIVATE_POLICY) {
mPolicy->Deactivate();
mPolicy = nullptr;
return true;
}
nsCOMPtr<nsIDomainSet> set;
switch(aSetType) {
case BLACKLIST:
mPolicy->GetBlacklist(getter_AddRefs(set));
break;
case SUPER_BLACKLIST:
mPolicy->GetSuperBlacklist(getter_AddRefs(set));
break;
case WHITELIST:
mPolicy->GetWhitelist(getter_AddRefs(set));
break;
case SUPER_WHITELIST:
mPolicy->GetSuperWhitelist(getter_AddRefs(set));
break;
default:
NS_NOTREACHED("Unexpected setType");
return false;
}
MOZ_ASSERT(set);
nsCOMPtr<nsIURI> uri = DeserializeURI(aDomain);
switch(aChangeType) {
case ADD_DOMAIN:
NS_ENSURE_TRUE(uri, false);
set->Add(uri);
break;
case REMOVE_DOMAIN:
NS_ENSURE_TRUE(uri, false);
set->Remove(uri);
break;
case CLEAR_DOMAINS:
set->Clear();
break;
default:
NS_NOTREACHED("Unexpected changeType");
return false;
}
return true;
}
void
ContentChild::StartForceKillTimer()
{
if (mForceKillTimer) {
return;
}
int32_t timeoutSecs = Preferences::GetInt("dom.ipc.tabs.shutdownTimeoutSecs", 5);
if (timeoutSecs > 0) {
mForceKillTimer = do_CreateInstance("@mozilla.org/timer;1");
MOZ_ASSERT(mForceKillTimer);
mForceKillTimer->InitWithFuncCallback(ContentChild::ForceKillTimerCallback,
this,
timeoutSecs * 1000,
nsITimer::TYPE_ONE_SHOT);
}
}
/* static */ void
ContentChild::ForceKillTimerCallback(nsITimer* aTimer, void* aClosure)
{
ProcessChild::QuickExit();
}
bool
ContentChild::RecvShutdown()
{
// If we receive the shutdown message from within a nested event loop, we want
// to wait for that event loop to finish. Otherwise we could prematurely
// terminate an "unload" or "pagehide" event handler (which might be doing a
// sync XHR, for example).
nsCOMPtr<nsIThread> thread;
nsresult rv = NS_GetMainThread(getter_AddRefs(thread));
if (NS_SUCCEEDED(rv) && thread) {
RefPtr<nsThread> mainThread(thread.forget().downcast<nsThread>());
if (mainThread->RecursionDepth() > 1) {
// We're in a nested event loop. Let's delay for an arbitrary period of
// time (100ms) in the hopes that the event loop will have finished by
// then.
MessageLoop::current()->PostDelayedTask(
NewRunnableMethod(this, &ContentChild::RecvShutdown), 100);
return true;
}
}
mShuttingDown = true;
if (mPolicy) {
mPolicy->Deactivate();
mPolicy = nullptr;
}
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
os->NotifyObservers(static_cast<nsIContentChild*>(this),
"content-child-shutdown", nullptr);
}
#if defined(XP_WIN)
mozilla::widget::StopAudioSession();
#endif
GetIPCChannel()->SetAbortOnError(false);
// Start a timer that will insure we quickly exit after a reasonable
// period of time. Prevents shutdown hangs after our connection to the
// parent closes.
StartForceKillTimer();
// Ignore errors here. If this fails, the parent will kill us after a
// timeout.
Unused << SendFinishShutdown();
return true;
}
PBrowserOrId
ContentChild::GetBrowserOrId(TabChild* aTabChild)
{
if (!aTabChild ||
this == aTabChild->Manager()) {
return PBrowserOrId(aTabChild);
}
else {
return PBrowserOrId(aTabChild->GetTabId());
}
}
bool
ContentChild::RecvUpdateWindow(const uintptr_t& aChildId)
{
#if defined(XP_WIN) && defined(MOZ_ENABLE_NPAPI)
NS_ASSERTION(aChildId, "Expected child hwnd value for remote plugin instance.");
mozilla::plugins::PluginInstanceParent* parentInstance =
mozilla::plugins::PluginInstanceParent::LookupPluginInstanceByID(aChildId);
if (parentInstance) {
// sync! update call to the plugin instance that forces the
// plugin to paint its child window.
parentInstance->CallUpdateWindow();
}
return true;
#else
MOZ_ASSERT(false, "ContentChild::RecvUpdateWindow calls unexpected on this platform.");
return false;
#endif
}
PContentPermissionRequestChild*
ContentChild::AllocPContentPermissionRequestChild(const InfallibleTArray<PermissionRequest>& aRequests,
const IPC::Principal& aPrincipal,
const TabId& aTabId)
{
NS_RUNTIMEABORT("unused");
return nullptr;
}
bool
ContentChild::DeallocPContentPermissionRequestChild(PContentPermissionRequestChild* actor)
{
nsContentPermissionUtils::NotifyRemoveContentPermissionRequestChild(actor);
auto child = static_cast<RemotePermissionRequest*>(actor);
child->IPDLRelease();
return true;
}
PWebBrowserPersistDocumentChild*
ContentChild::AllocPWebBrowserPersistDocumentChild(PBrowserChild* aBrowser,
const uint64_t& aOuterWindowID)
{
return new WebBrowserPersistDocumentChild();
}
bool
ContentChild::RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor,
PBrowserChild* aBrowser,
const uint64_t& aOuterWindowID)
{
if (NS_WARN_IF(!aBrowser)) {
return false;
}
nsCOMPtr<nsIDocument> rootDoc =
static_cast<TabChild*>(aBrowser)->GetDocument();
nsCOMPtr<nsIDocument> foundDoc;
if (aOuterWindowID) {
foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc, aOuterWindowID);
} else {
foundDoc = rootDoc;
}
if (!foundDoc) {
aActor->SendInitFailure(NS_ERROR_NO_CONTENT);
} else {
static_cast<WebBrowserPersistDocumentChild*>(aActor)->Start(foundDoc);
}
return true;
}
bool
ContentChild::DeallocPWebBrowserPersistDocumentChild(PWebBrowserPersistDocumentChild* aActor)
{
delete aActor;
return true;
}
bool
ContentChild::RecvSetAudioSessionData(const nsID& aId,
const nsString& aDisplayName,
const nsString& aIconPath)
{
#if defined(XP_WIN)
if (NS_FAILED(mozilla::widget::RecvAudioSessionData(aId, aDisplayName,
aIconPath))) {
return true;
}
// Ignore failures here; we can't really do anything about them
mozilla::widget::StartAudioSession();
return true;
#else
NS_RUNTIMEABORT("Not Reached!");
return false;
#endif
}
// This code goes here rather than nsGlobalWindow.cpp because nsGlobalWindow.cpp
// can't include ContentChild.h since it includes windows.h.
static uint64_t gNextWindowID = 0;
// We use only 53 bits for the window ID so that it can be converted to and from
// a JS value without loss of precision. The upper bits of the window ID hold the
// process ID. The lower bits identify the window.
static const uint64_t kWindowIDTotalBits = 53;
static const uint64_t kWindowIDProcessBits = 22;
static const uint64_t kWindowIDWindowBits = kWindowIDTotalBits - kWindowIDProcessBits;
// Try to return a window ID that is unique across processes and that will never
// be recycled.
uint64_t
NextWindowID()
{
uint64_t processID = 0;
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
processID = cc->GetID();
}
MOZ_RELEASE_ASSERT(processID < (uint64_t(1) << kWindowIDProcessBits));
uint64_t processBits = processID & ((uint64_t(1) << kWindowIDProcessBits) - 1);
// Make sure no actual window ends up with mWindowID == 0.
uint64_t windowID = ++gNextWindowID;
MOZ_RELEASE_ASSERT(windowID < (uint64_t(1) << kWindowIDWindowBits));
uint64_t windowBits = windowID & ((uint64_t(1) << kWindowIDWindowBits) - 1);
return (processBits << kWindowIDWindowBits) | windowBits;
}
bool
ContentChild::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
const uint32_t& aAction)
{
nsCOMPtr<nsIDragService> dragService =
do_GetService("@mozilla.org/widget/dragservice;1");
if (dragService) {
dragService->StartDragSession();
nsCOMPtr<nsIDragSession> session;
dragService->GetCurrentSession(getter_AddRefs(session));
if (session) {
session->SetDragAction(aAction);
// Check if we are receiving any file objects. If we are we will want
// to hide any of the other objects coming in from content.
bool hasFiles = false;
for (uint32_t i = 0; i < aTransfers.Length() && !hasFiles; ++i) {
auto& items = aTransfers[i].items();
for (uint32_t j = 0; j < items.Length() && !hasFiles; ++j) {
if (items[j].data().type() == IPCDataTransferData::TPBlobChild) {
hasFiles = true;
}
}
}
// Add the entries from the IPC to the new DataTransfer
nsCOMPtr<DataTransfer> dataTransfer =
new DataTransfer(nullptr, eDragStart, false, -1);
for (uint32_t i = 0; i < aTransfers.Length(); ++i) {
auto& items = aTransfers[i].items();
for (uint32_t j = 0; j < items.Length(); ++j) {
const IPCDataTransferItem& item = items[j];
RefPtr<nsVariantCC> variant = new nsVariantCC();
if (item.data().type() == IPCDataTransferData::TnsString) {
const nsString& data = item.data().get_nsString();
variant->SetAsAString(data);
} else if (item.data().type() == IPCDataTransferData::TShmem) {
Shmem data = item.data().get_Shmem();
variant->SetAsACString(nsDependentCString(data.get<char>(), data.Size<char>()));
Unused << DeallocShmem(data);
} else if (item.data().type() == IPCDataTransferData::TPBlobChild) {
BlobChild* blob = static_cast<BlobChild*>(item.data().get_PBlobChild());
RefPtr<BlobImpl> blobImpl = blob->GetBlobImpl();
variant->SetAsISupports(blobImpl);
} else {
continue;
}
// We should hide this data from content if we have a file, and we aren't a file.
bool hidden = hasFiles && item.data().type() != IPCDataTransferData::TPBlobChild;
dataTransfer->SetDataWithPrincipalFromOtherProcess(
NS_ConvertUTF8toUTF16(item.flavor()), variant, i,
nsContentUtils::GetSystemPrincipal(), hidden);
}
}
session->SetDataTransfer(dataTransfer);
}
}
return true;
}
bool
ContentChild::RecvEndDragSession(const bool& aDoneDrag,
const bool& aUserCancelled,
const LayoutDeviceIntPoint& aDragEndPoint)
{
nsCOMPtr<nsIDragService> dragService =
do_GetService("@mozilla.org/widget/dragservice;1");
if (dragService) {
if (aUserCancelled) {
nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
if (dragSession) {
dragSession->UserCancelled();
}
}
static_cast<nsBaseDragService*>(dragService.get())->SetDragEndPoint(aDragEndPoint);
dragService->EndDragSession(aDoneDrag);
}
return true;
}
bool
ContentChild::RecvPush(const nsCString& aScope,
const IPC::Principal& aPrincipal,
const nsString& aMessageId)
{
PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
return true;
}
bool
ContentChild::RecvPushWithData(const nsCString& aScope,
const IPC::Principal& aPrincipal,
const nsString& aMessageId,
InfallibleTArray<uint8_t>&& aData)
{
PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Some(aData));
Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
return true;
}
bool
ContentChild::RecvPushSubscriptionChange(const nsCString& aScope,
const IPC::Principal& aPrincipal)
{
PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
return true;
}
bool
ContentChild::RecvPushError(const nsCString& aScope, const IPC::Principal& aPrincipal,
const nsString& aMessage, const uint32_t& aFlags)
{
PushErrorDispatcher dispatcher(aScope, aPrincipal, aMessage, aFlags);
Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
return true;
}
bool
ContentChild::RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aScope,
const IPC::Principal& aPrincipal)
{
PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
return true;
}
bool
ContentChild::RecvBlobURLRegistration(const nsCString& aURI, PBlobChild* aBlobChild,
const IPC::Principal& aPrincipal)
{
RefPtr<BlobImpl> blobImpl = static_cast<BlobChild*>(aBlobChild)->GetBlobImpl();
MOZ_ASSERT(blobImpl);
nsHostObjectProtocolHandler::AddDataEntry(aURI, aPrincipal, blobImpl);
return true;
}
bool
ContentChild::RecvBlobURLUnregistration(const nsCString& aURI)
{
nsHostObjectProtocolHandler::RemoveDataEntry(aURI);
return true;
}
#if defined(XP_WIN) && defined(ACCESSIBILITY)
bool
ContentChild::SendGetA11yContentId()
{
return PContentChild::SendGetA11yContentId(&mMsaaID);
}
#endif // defined(XP_WIN) && defined(ACCESSIBILITY)
void
ContentChild::CreateGetFilesRequest(const nsAString& aDirectoryPath,
bool aRecursiveFlag,
nsID& aUUID,
GetFilesHelperChild* aChild)
{
MOZ_ASSERT(aChild);
MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aUUID));
Unused << SendGetFilesRequest(aUUID, nsString(aDirectoryPath),
aRecursiveFlag);
mGetFilesPendingRequests.Put(aUUID, aChild);
}
void
ContentChild::DeleteGetFilesRequest(nsID& aUUID, GetFilesHelperChild* aChild)
{
MOZ_ASSERT(aChild);
MOZ_ASSERT(mGetFilesPendingRequests.GetWeak(aUUID));
Unused << SendDeleteGetFilesRequest(aUUID);
mGetFilesPendingRequests.Remove(aUUID);
}
bool
ContentChild::RecvGetFilesResponse(const nsID& aUUID,
const GetFilesResponseResult& aResult)
{
RefPtr<GetFilesHelperChild> child;
// This object can already been deleted in case DeleteGetFilesRequest has
// been called when the response was sending by the parent.
if (!mGetFilesPendingRequests.Remove(aUUID, getter_AddRefs(child))) {
return true;
}
if (aResult.type() == GetFilesResponseResult::TGetFilesResponseFailure) {
child->Finished(aResult.get_GetFilesResponseFailure().errorCode());
} else {
MOZ_ASSERT(aResult.type() == GetFilesResponseResult::TGetFilesResponseSuccess);
const nsTArray<PBlobChild*>& blobs =
aResult.get_GetFilesResponseSuccess().blobsChild();
bool succeeded = true;
for (uint32_t i = 0; succeeded && i < blobs.Length(); ++i) {
RefPtr<BlobImpl> impl = static_cast<BlobChild*>(blobs[i])->GetBlobImpl();
succeeded = child->AppendBlobImpl(impl);
}
child->Finished(succeeded ? NS_OK : NS_ERROR_OUT_OF_MEMORY);
}
return true;
}
/* static */ void
ContentChild::FatalErrorIfNotUsingGPUProcess(const char* const aProtocolName,
const char* const aErrorMsg,
base::ProcessId aOtherPid)
{
// If we're communicating with the same process or the UI process then we
// want to crash normally. Otherwise we want to just warn as the other end
// must be the GPU process and it crashing shouldn't be fatal for us.
if (aOtherPid == base::GetCurrentProcId() ||
(GetSingleton() && GetSingleton()->OtherPid() == aOtherPid)) {
mozilla::ipc::FatalError(aProtocolName, aErrorMsg, false);
} else {
nsAutoCString formattedMessage("IPDL error [");
formattedMessage.AppendASCII(aProtocolName);
formattedMessage.AppendLiteral("]: \"");
formattedMessage.AppendASCII(aErrorMsg);
formattedMessage.AppendLiteral("\".");
NS_WARNING(formattedMessage.get());
}
}
} // namespace dom
} // namespace mozilla