/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifdef MOZ_WIDGET_GTK #include #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/FlyWebPublishedServerIPC.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/plugins/PluginInstanceParent.h" #include "mozilla/plugins/PluginModuleParent.h" #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 THE_GMP #include "GMPServiceChild.h" #endif #include "mozilla/Unused.h" #include "mozInlineSpellChecker.h" #include "nsDocShell.h" #include "nsIConsoleListener.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" #if defined(MOZ_WIDGET_ANDROID) #include "APKOpen.h" #endif #ifdef XP_WIN #include #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/RemoteSpellCheckEngineChild.h" #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 THE_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 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 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 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 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 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(newChild).forget().take(), tabId, *ipcContext, aChromeFlags, GetID(), IsForApp(), IsForBrowser()); nsString name(aName); nsAutoCString features(aFeatures); nsTArray 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 doc = opener->GetDoc(); nsCOMPtr 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 openerDocShell; float fullZoom = 1.0f; if (opener && (openerShell = opener->GetDocShell())) { openerDocShell = static_cast(openerShell); nsCOMPtr 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 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 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 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 callback = new BackgroundChildPrimer(); if (!BackgroundChild::GetOrCreateForCurrentThread(callback)) { MOZ_CRASH("Failed to create PBackgroundChild!"); } BlobChild::Startup(BlobChild::FriendKey()); nsCOMPtr 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; SendGetXPCOMProcessAttributes(&isOffline, &isConnected, &isLangRTL, &haveBidiKeyboards, &mAvailableDictionaries, &clipboardCaps, &domainPolicy, &initialData, &userContentSheetURL); RecvSetOffline(isOffline); RecvSetConnectivity(isConnected); 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 clipboard(do_GetService("@mozilla.org/widget/clipboard;1")); if (nsCOMPtr 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 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 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 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(aChild); DebugOnly rv; if (aMinimizeMemoryUsage) { nsCOMPtr 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(Manager()); nsCOMPtr 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 handleReport = new HandleReportCallback(this, process); RefPtr 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(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 sink = static_cast(aActor); nsCOMPtr 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; } mozilla::plugins::PPluginModuleParent* ContentChild::AllocPPluginModuleParent(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) { return plugins::PluginModuleContentParent::Initialize(aTransport, aOtherProcess); } 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::Create(aTransport, aOtherProcess)); return mLastBridge; } #ifdef THE_GMP PGMPServiceChild* ContentChild::AllocPGMPServiceChild(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) { return GMPServiceChild::Create(aTransport, aOtherProcess); } bool ContentChild::RecvGMPsChanged(nsTArray&& capabilities) { GeckoMediaPluginServiceChild::UpdateGMPCapabilities(Move(capabilities)); return true; } #endif bool ContentChild::RecvInitRendering(Endpoint&& aCompositor, Endpoint&& aImageBridge, Endpoint&& 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&& aCompositor, Endpoint&& aImageBridge, Endpoint&& aVideoManager) { nsTArray> 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(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 os = services::GetObserverService(); if (os) { nsITabChild* tc = static_cast(static_cast(aActor)); os->NotifyObservers(tc, "tab-child-created", nullptr); } static bool hasRunOnce = false; if (!hasRunOnce) { hasRunOnce = true; MOZ_ASSERT(!sFirstIdleTask); RefPtr 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& 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); } PFlyWebPublishedServerChild* ContentChild::AllocPFlyWebPublishedServerChild(const nsString& name, const FlyWebPublishOptions& params) { MOZ_CRASH("We should never be manually allocating PFlyWebPublishedServerChild actors"); return nullptr; } bool ContentChild::DeallocPFlyWebPublishedServerChild(PFlyWebPublishedServerChild* aActor) { RefPtr actor = dont_AddRef(static_cast(aActor)); return true; } bool ContentChild::RecvNotifyEmptyHTTPCache() { MOZ_ASSERT(NS_IsMainThread()); nsCOMPtr 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(aService); child->Release(); return true; } PPSMContentDownloaderChild* ContentChild::AllocPPSMContentDownloaderChild(const uint32_t& aCertType) { // NB: We don't need aCertType in the child actor. RefPtr child = new PSMContentDownloaderChild(); return child.forget().take(); } bool ContentChild::DeallocPPSMContentDownloaderChild(PPSMContentDownloaderChild* aListener) { auto* listener = static_cast(aListener); RefPtr 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(aService); child->Release(); return true; } PHandlerServiceChild* ContentChild::AllocPHandlerServiceChild() { HandlerServiceChild* actor = new HandlerServiceChild(); actor->AddRef(); return actor; } bool ContentChild::DeallocPHandlerServiceChild(PHandlerServiceChild* aHandlerServiceChild) { static_cast(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(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(aActor); return true; #else return false; #endif } bool ContentChild::RecvRegisterChrome(InfallibleTArray&& packages, InfallibleTArray&& resources, InfallibleTArray&& overrides, const nsCString& locale, const bool& reset) { nsCOMPtr registrySvc = nsChromeRegistry::GetService(); nsChromeRegistryContent* chromeRegistry = static_cast(registrySvc.get()); chromeRegistry->RegisterRemoteChrome(packages, resources, overrides, locale, reset); return true; } bool ContentChild::RecvRegisterChromeItem(const ChromeRegistryItem& item) { nsCOMPtr registrySvc = nsChromeRegistry::GetService(); nsChromeRegistryContent* chromeRegistry = static_cast(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 io (do_GetIOService()); NS_ASSERTION(io, "IO Service can not be null"); io->SetOffline(offline); return true; } bool ContentChild::RecvSetConnectivity(const bool& connectivity) { nsCOMPtr io(do_GetIOService()); nsCOMPtr ioInternal(do_QueryInterface(io)); NS_ASSERTION(ioInternal, "IO Service can not be null"); ioInternal->SetConnectivity(connectivity); 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 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 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 storage = DataStorage::GetIfExists(aFilename); if (storage) { storage->Remove(aKey, aType); } return true; } bool ContentChild::RecvDataStorageClear(const nsString& aFilename) { RefPtr 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 newURI = DeserializeURI(aURI); if (!newURI) { return false; } nsCOMPtr 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&& aCpows, const IPC::Principal& aPrincipal, const ClonedMessageData& aData) { RefPtr cpm = nsFrameMessageManager::GetChildProcessManager(); if (cpm) { StructuredCloneData data; ipc::UnpackClonedMessageDataForChild(aData, data); CrossProcessCpowHolder cpows(this, aCpows); cpm->ReceiveMessage(static_cast(cpm.get()), nullptr, aMsg, false, &data, &cpows, aPrincipal, nullptr); } return true; } bool ContentChild::RecvGeolocationUpdate(const GeoPosition& somewhere) { nsCOMPtr gs = do_GetService("@mozilla.org/geolocation/service;1"); if (!gs) { return true; } nsCOMPtr position = somewhere; gs->Update(position); return true; } bool ContentChild::RecvGeolocationError(const uint16_t& errorCode) { nsCOMPtr gs = do_GetService("@mozilla.org/geolocation/service;1"); if (!gs) { return true; } gs->NotifyError(errorCode); return true; } bool ContentChild::RecvUpdateDictionaryList(InfallibleTArray&& aDictionaries) { mAvailableDictionaries = aDictionaries; mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking(); return true; } bool ContentChild::RecvAddPermission(const IPC::Permission& permission) { #if MOZ_PERMISSIONS nsCOMPtr permissionManagerIface = services::GetPermissionManager(); nsPermissionManager* permissionManager = static_cast(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 uri; nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix); NS_ENSURE_SUCCESS(rv, true); nsCOMPtr 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 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 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 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 swm = ServiceWorkerManager::GetInstance(); if (!swm) { // browser shutdown began return true; } swm->LoadRegistrations(aConfig.serviceWorkerRegistrations()); return true; } bool ContentChild::RecvInitBlobURLs(nsTArray&& aRegistrations) { for (uint32_t i = 0; i < aRegistrations.Length(); ++i) { BlobURLRegistrationData& registration = aRegistrations[i]; RefPtr blobImpl = static_cast(registration.blobChild())->GetBlobImpl(); MOZ_ASSERT(blobImpl); nsHostObjectProtocolHandler::AddDataEntry(registration.url(), registration.principal(), blobImpl); } return true; } bool ContentChild::RecvLastPrivateDocShellDestroyed() { nsCOMPtr obs = mozilla::services::GetObserverService(); obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr); return true; } bool ContentChild::RecvVolumes(nsTArray&& 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 os = services::GetObserverService(); NS_ENSURE_TRUE(os, true); RefPtr props = new nsHashPropertyBag(); props->SetPropertyAsInt32(NS_LITERAL_STRING("priority"), static_cast(aPriority)); os->NotifyObservers(static_cast(props), "ipc:process-priority-changed", nullptr); return true; } bool ContentChild::RecvMinimizeMemoryUsage() { nsCOMPtr 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 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(aObserver), aIdleTimeInS); mIdleObservers.PutEntry(aObserver); } void ContentChild::RemoveIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS) { MOZ_ASSERT(aObserver, "null idle observer"); SendRemoveIdleObserver(reinterpret_cast(aObserver), aIdleTimeInS); aObserver->Release(); mIdleObservers.RemoveEntry(aObserver); } bool ContentChild::RecvNotifyIdleObserver(const uint64_t& aObserver, const nsCString& aTopic, const nsString& aTimeStr) { nsIObserver* observer = reinterpret_cast(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 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 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(actor); NS_RELEASE(offlineCacheUpdate); return true; } 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; } 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 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 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 thread; nsresult rv = NS_GetMainThread(getter_AddRefs(thread)); if (NS_SUCCEEDED(rv) && thread) { RefPtr mainThread(thread.forget().downcast()); 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 os = services::GetObserverService(); if (os) { os->NotifyObservers(static_cast(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) 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& 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(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 rootDoc = static_cast(aBrowser)->GetDocument(); nsCOMPtr foundDoc; if (aOuterWindowID) { foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc, aOuterWindowID); } else { foundDoc = rootDoc; } if (!foundDoc) { aActor->SendInitFailure(NS_ERROR_NO_CONTENT); } else { static_cast(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&& aTransfers, const uint32_t& aAction) { nsCOMPtr dragService = do_GetService("@mozilla.org/widget/dragservice;1"); if (dragService) { dragService->StartDragSession(); nsCOMPtr 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 = 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 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(), data.Size())); Unused << DeallocShmem(data); } else if (item.data().type() == IPCDataTransferData::TPBlobChild) { BlobChild* blob = static_cast(item.data().get_PBlobChild()); RefPtr 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 dragService = do_GetService("@mozilla.org/widget/dragservice;1"); if (dragService) { if (aUserCancelled) { nsCOMPtr dragSession = nsContentUtils::GetDragSession(); if (dragSession) { dragSession->UserCancelled(); } } static_cast(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&& 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 = static_cast(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) { GetFilesHelperChild* child = mGetFilesPendingRequests.GetWeak(aUUID); // This object can already been deleted in case DeleteGetFilesRequest has // been called when the response was sending by the parent. if (!child) { return true; } if (aResult.type() == GetFilesResponseResult::TGetFilesResponseFailure) { child->Finished(aResult.get_GetFilesResponseFailure().errorCode()); } else { MOZ_ASSERT(aResult.type() == GetFilesResponseResult::TGetFilesResponseSuccess); const nsTArray& blobs = aResult.get_GetFilesResponseSuccess().blobsChild(); bool succeeded = true; for (uint32_t i = 0; succeeded && i < blobs.Length(); ++i) { RefPtr impl = static_cast(blobs[i])->GetBlobImpl(); succeeded = child->AppendBlobImpl(impl); } child->Finished(succeeded ? NS_OK : NS_ERROR_OUT_OF_MEMORY); } mGetFilesPendingRequests.Remove(aUUID); 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