michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set sw=4 ts=8 et tw=80 : */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_QT michael@0: #include "nsQAppInstance.h" michael@0: #endif michael@0: michael@0: #include "ContentChild.h" michael@0: #include "CrashReporterChild.h" michael@0: #include "FileDescriptorSetChild.h" michael@0: #include "TabChild.h" michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/dom/asmjscache/AsmJSCache.h" michael@0: #include "mozilla/dom/asmjscache/PAsmJSCacheEntryChild.h" michael@0: #include "mozilla/dom/ExternalHelperAppChild.h" michael@0: #include "mozilla/dom/PCrashReporterChild.h" michael@0: #include "mozilla/dom/DOMStorageIPC.h" michael@0: #include "mozilla/hal_sandbox/PHalChild.h" michael@0: #include "mozilla/ipc/BackgroundChild.h" michael@0: #include "mozilla/ipc/GeckoChildProcessHost.h" michael@0: #include "mozilla/ipc/TestShellChild.h" michael@0: #include "mozilla/layers/CompositorChild.h" michael@0: #include "mozilla/layers/ImageBridgeChild.h" michael@0: #include "mozilla/layers/PCompositorChild.h" michael@0: #include "mozilla/net/NeckoChild.h" michael@0: #include "mozilla/Preferences.h" michael@0: michael@0: #if defined(MOZ_CONTENT_SANDBOX) michael@0: #if defined(XP_WIN) michael@0: #define TARGET_SANDBOX_EXPORTS michael@0: #include "mozilla/sandboxTarget.h" michael@0: #elif defined(XP_LINUX) michael@0: #include "mozilla/Sandbox.h" michael@0: #endif michael@0: #endif michael@0: michael@0: #include "mozilla/unused.h" michael@0: michael@0: #include "nsIConsoleListener.h" michael@0: #include "nsIIPCBackgroundChildCreateCallback.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsIMemoryReporter.h" michael@0: #include "nsIMemoryInfoDumper.h" michael@0: #include "nsIMutable.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsStyleSheetService.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "nsIScriptError.h" michael@0: #include "nsIConsoleService.h" michael@0: #include "nsJSEnvironment.h" michael@0: #include "SandboxHal.h" michael@0: #include "nsDebugImpl.h" michael@0: #include "nsHashPropertyBag.h" michael@0: #include "nsLayoutStylesheetCache.h" michael@0: #include "nsIJSRuntimeService.h" michael@0: #include "nsThreadManager.h" michael@0: michael@0: #include "IHistory.h" michael@0: #include "nsNetUtil.h" michael@0: michael@0: #include "base/message_loop.h" michael@0: #include "base/process_util.h" michael@0: #include "base/task.h" michael@0: michael@0: #include "nsChromeRegistryContent.h" michael@0: #include "nsFrameMessageManager.h" michael@0: michael@0: #include "nsIGeolocationProvider.h" michael@0: #include "mozilla/dom/PMemoryReportRequestChild.h" michael@0: michael@0: #ifdef MOZ_PERMISSIONS michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsPermission.h" michael@0: #include "nsPermissionManager.h" michael@0: #endif michael@0: michael@0: #include "PermissionMessageUtils.h" michael@0: michael@0: #if defined(MOZ_WIDGET_ANDROID) michael@0: #include "APKOpen.h" michael@0: #endif michael@0: michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: #include "nsVolume.h" michael@0: #include "nsVolumeService.h" michael@0: #include "SpeakerManagerService.h" michael@0: #endif michael@0: michael@0: #ifdef XP_WIN michael@0: #include michael@0: #define getpid _getpid michael@0: #endif michael@0: michael@0: #ifdef MOZ_X11 michael@0: #include "mozilla/X11Util.h" michael@0: #endif michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: #include "nsIAccessibilityService.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: #include michael@0: #include "ipc/Nuwa.h" michael@0: #endif michael@0: michael@0: #include "mozilla/dom/indexedDB/PIndexedDBChild.h" michael@0: #include "mozilla/dom/mobilemessage/SmsChild.h" michael@0: #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h" michael@0: #include "mozilla/dom/PFileSystemRequestChild.h" michael@0: #include "mozilla/dom/FileSystemTaskBase.h" michael@0: #include "mozilla/dom/bluetooth/PBluetoothChild.h" michael@0: #include "mozilla/dom/PFMRadioChild.h" michael@0: #include "mozilla/ipc/InputStreamUtils.h" michael@0: michael@0: #ifdef MOZ_WEBSPEECH michael@0: #include "mozilla/dom/PSpeechSynthesisChild.h" michael@0: #endif michael@0: michael@0: #include "nsDOMFile.h" michael@0: #include "nsIRemoteBlob.h" michael@0: #include "ProcessUtils.h" michael@0: #include "StructuredCloneUtils.h" michael@0: #include "URIUtils.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsDeviceStorage.h" michael@0: #include "AudioChannelService.h" michael@0: #include "JavaScriptChild.h" michael@0: #include "mozilla/dom/telephony/PTelephonyChild.h" michael@0: #include "mozilla/dom/time/DateCacheCleaner.h" michael@0: #include "mozilla/net/NeckoMessageUtils.h" michael@0: michael@0: using namespace base; michael@0: using namespace mozilla; michael@0: using namespace mozilla::docshell; michael@0: using namespace mozilla::dom::bluetooth; michael@0: using namespace mozilla::dom::devicestorage; michael@0: using namespace mozilla::dom::ipc; michael@0: using namespace mozilla::dom::mobilemessage; michael@0: using namespace mozilla::dom::indexedDB; michael@0: using namespace mozilla::dom::telephony; michael@0: using namespace mozilla::hal_sandbox; michael@0: using namespace mozilla::ipc; michael@0: using namespace mozilla::layers; michael@0: using namespace mozilla::net; michael@0: using namespace mozilla::jsipc; michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: using namespace mozilla::system; michael@0: #endif michael@0: michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: static bool sNuwaForking = false; michael@0: michael@0: // The size of the reserved stack (in unsigned ints). It's used to reserve space michael@0: // to push sigsetjmp() in NuwaCheckpointCurrentThread() to higher in the stack michael@0: // so that after it returns and do other work we don't garble the stack we want michael@0: // to preserve in NuwaCheckpointCurrentThread(). michael@0: #define RESERVED_INT_STACK 128 michael@0: michael@0: // A sentinel value for checking whether RESERVED_INT_STACK is large enough. michael@0: #define STACK_SENTINEL_VALUE 0xdeadbeef michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: class MemoryReportRequestChild : public PMemoryReportRequestChild, michael@0: public nsIRunnable michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: MemoryReportRequestChild(uint32_t aGeneration, const nsAString& aDMDDumpIdent); michael@0: virtual ~MemoryReportRequestChild(); michael@0: NS_IMETHOD Run(); michael@0: private: michael@0: uint32_t mGeneration; michael@0: nsString mDMDDumpIdent; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(MemoryReportRequestChild, nsIRunnable) michael@0: michael@0: MemoryReportRequestChild::MemoryReportRequestChild(uint32_t aGeneration, const nsAString& aDMDDumpIdent) michael@0: : mGeneration(aGeneration), mDMDDumpIdent(aDMDDumpIdent) michael@0: { michael@0: MOZ_COUNT_CTOR(MemoryReportRequestChild); michael@0: } michael@0: michael@0: MemoryReportRequestChild::~MemoryReportRequestChild() michael@0: { michael@0: MOZ_COUNT_DTOR(MemoryReportRequestChild); michael@0: } michael@0: michael@0: class AlertObserver michael@0: { michael@0: public: michael@0: michael@0: AlertObserver(nsIObserver *aObserver, const nsString& aData) michael@0: : mObserver(aObserver) michael@0: , mData(aData) michael@0: { michael@0: } michael@0: michael@0: ~AlertObserver() {} michael@0: michael@0: bool ShouldRemoveFrom(nsIObserver* aObserver, michael@0: const nsString& aData) const michael@0: { michael@0: return (mObserver == aObserver && michael@0: mData == aData); michael@0: } michael@0: michael@0: bool Observes(const nsString& aData) const michael@0: { michael@0: return mData.Equals(aData); michael@0: } michael@0: michael@0: bool Notify(const nsCString& aType) const michael@0: { michael@0: mObserver->Observe(nullptr, aType.get(), mData.get()); michael@0: return true; michael@0: } michael@0: michael@0: private: michael@0: nsCOMPtr mObserver; michael@0: nsString mData; michael@0: }; michael@0: michael@0: class ConsoleListener MOZ_FINAL : public nsIConsoleListener michael@0: { michael@0: public: michael@0: ConsoleListener(ContentChild* aChild) michael@0: : mChild(aChild) {} michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSICONSOLELISTENER michael@0: michael@0: private: michael@0: ContentChild* mChild; michael@0: friend class ContentChild; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(ConsoleListener, nsIConsoleListener) michael@0: michael@0: NS_IMETHODIMP michael@0: ConsoleListener::Observe(nsIConsoleMessage* aMessage) michael@0: { michael@0: if (!mChild) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr scriptError = do_QueryInterface(aMessage); michael@0: if (scriptError) { michael@0: nsString msg, sourceName, sourceLine; michael@0: nsXPIDLCString category; michael@0: uint32_t lineNum, colNum, flags; michael@0: michael@0: nsresult rv = scriptError->GetErrorMessage(msg); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = scriptError->GetSourceName(sourceName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = scriptError->GetSourceLine(sourceLine); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Before we send the error to the parent process (which michael@0: // involves copying the memory), truncate any long lines. CSS michael@0: // errors in particular share the memory for long lines with michael@0: // repeated errors, but the IPC communication we're about to do michael@0: // will break that sharing, so we better truncate now. michael@0: if (sourceLine.Length() > 1000) { michael@0: sourceLine.Truncate(1000); michael@0: } michael@0: michael@0: rv = scriptError->GetCategory(getter_Copies(category)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = scriptError->GetLineNumber(&lineNum); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = scriptError->GetColumnNumber(&colNum); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = scriptError->GetFlags(&flags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: mChild->SendScriptError(msg, sourceName, sourceLine, michael@0: lineNum, colNum, flags, category); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsXPIDLString msg; michael@0: nsresult rv = aMessage->GetMessageMoz(getter_Copies(msg)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: mChild->SendConsoleMessage(msg); michael@0: return NS_OK; michael@0: } michael@0: michael@0: class SystemMessageHandledObserver MOZ_FINAL : public nsIObserver michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: void Init(); michael@0: }; michael@0: michael@0: void SystemMessageHandledObserver::Init() michael@0: { michael@0: nsCOMPtr os = michael@0: mozilla::services::GetObserverService(); michael@0: michael@0: if (os) { michael@0: os->AddObserver(this, "handle-system-messages-done", michael@0: /* ownsWeak */ false); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: SystemMessageHandledObserver::Observe(nsISupports* aSubject, michael@0: const char* aTopic, michael@0: const char16_t* aData) michael@0: { michael@0: if (ContentChild::GetSingleton()) { michael@0: ContentChild::GetSingleton()->SendSystemMessageHandled(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(SystemMessageHandledObserver, nsIObserver) michael@0: michael@0: class BackgroundChildPrimer MOZ_FINAL : michael@0: public nsIIPCBackgroundChildCreateCallback michael@0: { michael@0: public: michael@0: BackgroundChildPrimer() michael@0: { } michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: private: michael@0: ~BackgroundChildPrimer() michael@0: { } michael@0: michael@0: virtual void michael@0: ActorCreated(PBackgroundChild* aActor) MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(aActor, "Failed to create a PBackgroundChild actor!"); michael@0: } michael@0: michael@0: virtual void michael@0: ActorFailed() MOZ_OVERRIDE michael@0: { michael@0: MOZ_CRASH("Failed to create a PBackgroundChild actor!"); michael@0: } michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(BackgroundChildPrimer, nsIIPCBackgroundChildCreateCallback) michael@0: michael@0: ContentChild* ContentChild::sSingleton; michael@0: michael@0: // Performs initialization that is not fork-safe, i.e. that must be done after michael@0: // forking from the Nuwa process. michael@0: static void michael@0: InitOnContentProcessCreated() michael@0: { michael@0: // This will register cross-process observer. michael@0: mozilla::dom::time::InitializeDateCacheCleaner(); michael@0: } michael@0: michael@0: ContentChild::ContentChild() michael@0: : mID(uint64_t(-1)) michael@0: #ifdef ANDROID michael@0: ,mScreenSize(0, 0) michael@0: #endif michael@0: , mCanOverrideProcessName(true) michael@0: { michael@0: // This process is a content process, so it's clearly running in michael@0: // multiprocess mode! michael@0: nsDebugImpl::SetMultiprocessMode("Child"); michael@0: } michael@0: michael@0: ContentChild::~ContentChild() michael@0: { michael@0: } michael@0: michael@0: bool michael@0: ContentChild::Init(MessageLoop* aIOLoop, michael@0: base::ProcessHandle aParentHandle, michael@0: IPC::Channel* aChannel) michael@0: { michael@0: #ifdef MOZ_WIDGET_GTK michael@0: // sigh michael@0: gtk_init(nullptr, nullptr); michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_QT michael@0: // sigh, seriously michael@0: nsQAppInstance::AddRef(); michael@0: #endif michael@0: michael@0: #ifdef MOZ_X11 michael@0: // Do this after initializing GDK, or GDK will install its own handler. michael@0: XRE_InstallX11ErrorHandler(); michael@0: #endif michael@0: michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: SetTransport(aChannel); michael@0: #endif michael@0: michael@0: NS_ASSERTION(!sSingleton, "only one ContentChild per child"); michael@0: michael@0: // Once we start sending IPC messages, we need the thread manager to be michael@0: // initialized so we can deal with the responses. Do that here before we michael@0: // try to construct the crash reporter. michael@0: nsresult rv = nsThreadManager::get()->Init(); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return false; michael@0: } michael@0: michael@0: Open(aChannel, aParentHandle, aIOLoop); michael@0: sSingleton = this; michael@0: michael@0: #ifdef MOZ_X11 michael@0: // Send the parent our X socket to act as a proxy reference for our X michael@0: // resources. michael@0: int xSocketFd = ConnectionNumber(DefaultXDisplay()); michael@0: SendBackUpXResources(FileDescriptor(xSocketFd)); michael@0: #endif michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: SendPCrashReporterConstructor(CrashReporter::CurrentThreadId(), michael@0: XRE_GetProcessType()); michael@0: #endif michael@0: michael@0: GetCPOWManager(); michael@0: michael@0: InitProcessAttributes(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: ContentChild::InitProcessAttributes() michael@0: { michael@0: SendGetProcessAttributes(&mID, &mIsForApp, &mIsForBrowser); michael@0: michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: if (IsNuwaProcess()) { michael@0: SetProcessName(NS_LITERAL_STRING("(Nuwa)"), false); michael@0: return; michael@0: } michael@0: #endif michael@0: if (mIsForApp && !mIsForBrowser) { michael@0: SetProcessName(NS_LITERAL_STRING("(Preallocated app)"), false); michael@0: } else { michael@0: SetProcessName(NS_LITERAL_STRING("Browser"), false); michael@0: } michael@0: michael@0: } michael@0: michael@0: void michael@0: ContentChild::SetProcessName(const nsAString& aName, bool aDontOverride) michael@0: { michael@0: if (!mCanOverrideProcessName) { michael@0: return; michael@0: } michael@0: michael@0: char* name; michael@0: if ((name = PR_GetEnv("MOZ_DEBUG_APP_PROCESS")) && michael@0: aName.EqualsASCII(name)) { michael@0: #ifdef OS_POSIX michael@0: printf_stderr("\n\nCHILDCHILDCHILDCHILD\n [%s] debug me @%d\n\n", name, getpid()); michael@0: sleep(30); michael@0: #elif defined(OS_WIN) michael@0: // Windows has a decent JIT debugging story, so NS_DebugBreak does the michael@0: // right thing. michael@0: NS_DebugBreak(NS_DEBUG_BREAK, michael@0: "Invoking NS_DebugBreak() to debug child process", michael@0: nullptr, __FILE__, __LINE__); michael@0: #endif michael@0: } michael@0: michael@0: mProcessName = aName; michael@0: mozilla::ipc::SetThisProcessName(NS_LossyConvertUTF16toASCII(aName).get()); michael@0: michael@0: if (aDontOverride) { michael@0: mCanOverrideProcessName = false; michael@0: } michael@0: } michael@0: michael@0: void michael@0: ContentChild::GetProcessName(nsAString& aName) michael@0: { michael@0: aName.Assign(mProcessName); michael@0: } michael@0: michael@0: void michael@0: ContentChild::GetProcessName(nsACString& aName) michael@0: { michael@0: aName.Assign(NS_ConvertUTF16toUTF8(mProcessName)); michael@0: } michael@0: michael@0: /* static */ void michael@0: ContentChild::AppendProcessId(nsACString& aName) michael@0: { michael@0: if (!aName.IsEmpty()) { michael@0: aName.AppendLiteral(" "); michael@0: } michael@0: unsigned pid = getpid(); michael@0: aName.Append(nsPrintfCString("(pid %u)", pid)); michael@0: } michael@0: michael@0: void michael@0: ContentChild::InitXPCOM() michael@0: { michael@0: // Do this as early as possible to get the parent process to initialize the michael@0: // background thread since we'll likely need database information very soon. michael@0: BackgroundChild::Startup(); michael@0: michael@0: nsCOMPtr callback = michael@0: new BackgroundChildPrimer(); michael@0: if (!BackgroundChild::GetOrCreateForCurrentThread(callback)) { michael@0: MOZ_CRASH("Failed to create PBackgroundChild!"); michael@0: } michael@0: michael@0: nsCOMPtr svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID)); michael@0: if (!svc) { michael@0: NS_WARNING("Couldn't acquire console service"); michael@0: return; michael@0: } michael@0: michael@0: mConsoleListener = new ConsoleListener(this); michael@0: if (NS_FAILED(svc->RegisterListener(mConsoleListener))) michael@0: NS_WARNING("Couldn't register console listener for child process"); michael@0: michael@0: bool isOffline; michael@0: SendGetXPCOMProcessAttributes(&isOffline); michael@0: RecvSetOffline(isOffline); michael@0: michael@0: DebugOnly observer = FileUpdateDispatcher::GetSingleton(); michael@0: NS_ASSERTION(observer, "FileUpdateDispatcher is null"); michael@0: michael@0: // This object is held alive by the observer service. michael@0: nsRefPtr sysMsgObserver = michael@0: new SystemMessageHandledObserver(); michael@0: sysMsgObserver->Init(); michael@0: michael@0: #ifndef MOZ_NUWA_PROCESS michael@0: InitOnContentProcessCreated(); michael@0: #endif michael@0: } michael@0: michael@0: PMemoryReportRequestChild* michael@0: ContentChild::AllocPMemoryReportRequestChild(const uint32_t& generation, michael@0: const bool &minimizeMemoryUsage, michael@0: const nsString& aDMDDumpIdent) michael@0: { michael@0: MemoryReportRequestChild *actor = new MemoryReportRequestChild(generation, aDMDDumpIdent); michael@0: actor->AddRef(); michael@0: return actor; michael@0: } michael@0: michael@0: // This is just a wrapper for InfallibleTArray that implements michael@0: // nsISupports, so it can be passed to nsIMemoryReporter::CollectReports. michael@0: class MemoryReportsWrapper MOZ_FINAL : public nsISupports { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: MemoryReportsWrapper(InfallibleTArray *r) : mReports(r) { } michael@0: InfallibleTArray *mReports; michael@0: }; michael@0: NS_IMPL_ISUPPORTS0(MemoryReportsWrapper) michael@0: michael@0: class MemoryReportCallback MOZ_FINAL : public nsIMemoryReporterCallback michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: MemoryReportCallback(const nsACString &aProcess) michael@0: : mProcess(aProcess) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath, michael@0: int32_t aKind, int32_t aUnits, int64_t aAmount, michael@0: const nsACString &aDescription, michael@0: nsISupports *aiWrappedReports) michael@0: { michael@0: MemoryReportsWrapper *wrappedReports = michael@0: static_cast(aiWrappedReports); michael@0: michael@0: MemoryReport memreport(mProcess, nsCString(aPath), aKind, aUnits, michael@0: aAmount, nsCString(aDescription)); michael@0: wrappedReports->mReports->AppendElement(memreport); michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: const nsCString mProcess; michael@0: }; michael@0: NS_IMPL_ISUPPORTS( michael@0: MemoryReportCallback michael@0: , nsIMemoryReporterCallback michael@0: ) michael@0: michael@0: bool michael@0: ContentChild::RecvPMemoryReportRequestConstructor( michael@0: PMemoryReportRequestChild* child, michael@0: const uint32_t& generation, michael@0: const bool& minimizeMemoryUsage, michael@0: const nsString& aDMDDumpIdent) michael@0: { michael@0: MemoryReportRequestChild *actor = static_cast(child); michael@0: nsresult rv; michael@0: michael@0: if (minimizeMemoryUsage) { michael@0: nsCOMPtr mgr = do_GetService("@mozilla.org/memory-reporter-manager;1"); michael@0: rv = mgr->MinimizeMemoryUsage(actor); michael@0: // mgr will eventually call actor->Run() michael@0: } else { michael@0: rv = actor->Run(); michael@0: } michael@0: michael@0: return !NS_WARN_IF(NS_FAILED(rv)); michael@0: } michael@0: michael@0: NS_IMETHODIMP MemoryReportRequestChild::Run() michael@0: { michael@0: ContentChild *child = static_cast(Manager()); michael@0: nsCOMPtr mgr = do_GetService("@mozilla.org/memory-reporter-manager;1"); michael@0: michael@0: InfallibleTArray reports; michael@0: michael@0: nsCString process; michael@0: child->GetProcessName(process); michael@0: child->AppendProcessId(process); michael@0: michael@0: // Run the reporters. The callback will turn each measurement into a michael@0: // MemoryReport. michael@0: nsRefPtr wrappedReports = michael@0: new MemoryReportsWrapper(&reports); michael@0: nsRefPtr cb = new MemoryReportCallback(process); michael@0: mgr->GetReportsForThisProcessExtended(cb, wrappedReports, mDMDDumpIdent); michael@0: michael@0: bool sent = Send__delete__(this, mGeneration, reports); michael@0: return sent ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvAudioChannelNotify() michael@0: { michael@0: nsRefPtr service = michael@0: AudioChannelService::GetAudioChannelService(); michael@0: if (service) { michael@0: service->Notify(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPMemoryReportRequestChild(PMemoryReportRequestChild* actor) michael@0: { michael@0: static_cast(actor)->Release(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvDumpGCAndCCLogsToFile(const nsString& aIdentifier, michael@0: const bool& aDumpAllTraces, michael@0: const bool& aDumpChildProcesses) michael@0: { michael@0: nsCOMPtr dumper = do_GetService("@mozilla.org/memory-info-dumper;1"); michael@0: michael@0: nsString gcLogPath, ccLogPath; michael@0: dumper->DumpGCAndCCLogsToFile(aIdentifier, aDumpAllTraces, michael@0: aDumpChildProcesses, gcLogPath, ccLogPath); michael@0: return true; michael@0: } michael@0: michael@0: PCompositorChild* michael@0: ContentChild::AllocPCompositorChild(mozilla::ipc::Transport* aTransport, michael@0: base::ProcessId aOtherProcess) michael@0: { michael@0: return CompositorChild::Create(aTransport, aOtherProcess); michael@0: } michael@0: michael@0: PImageBridgeChild* michael@0: ContentChild::AllocPImageBridgeChild(mozilla::ipc::Transport* aTransport, michael@0: base::ProcessId aOtherProcess) michael@0: { michael@0: return ImageBridgeChild::StartUpInChildProcess(aTransport, aOtherProcess); michael@0: } michael@0: michael@0: PBackgroundChild* michael@0: ContentChild::AllocPBackgroundChild(Transport* aTransport, michael@0: ProcessId aOtherProcess) michael@0: { michael@0: return BackgroundChild::Alloc(aTransport, aOtherProcess); michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvSetProcessSandbox() michael@0: { michael@0: // We may want to move the sandbox initialization somewhere else michael@0: // at some point; see bug 880808. michael@0: #if defined(MOZ_CONTENT_SANDBOX) michael@0: #if defined(XP_LINUX) michael@0: SetCurrentProcessSandbox(); michael@0: #elif defined(XP_WIN) michael@0: mozilla::SandboxTarget::Instance()->StartSandbox(); michael@0: #endif michael@0: #endif michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvSpeakerManagerNotify() michael@0: { michael@0: #ifdef MOZ_WIDGET_GONK michael@0: nsRefPtr service = michael@0: SpeakerManagerService::GetSpeakerManagerService(); michael@0: if (service) { michael@0: service->Notify(); michael@0: } michael@0: return true; michael@0: #endif michael@0: return false; michael@0: } michael@0: michael@0: static CancelableTask* sFirstIdleTask; michael@0: michael@0: static void FirstIdle(void) michael@0: { michael@0: MOZ_ASSERT(sFirstIdleTask); michael@0: sFirstIdleTask = nullptr; michael@0: ContentChild::GetSingleton()->SendFirstIdle(); michael@0: } michael@0: michael@0: mozilla::jsipc::PJavaScriptChild * michael@0: ContentChild::AllocPJavaScriptChild() michael@0: { michael@0: nsCOMPtr svc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1"); michael@0: NS_ENSURE_TRUE(svc, nullptr); michael@0: michael@0: JSRuntime *rt; michael@0: svc->GetRuntime(&rt); michael@0: NS_ENSURE_TRUE(svc, nullptr); michael@0: michael@0: mozilla::jsipc::JavaScriptChild *child = new mozilla::jsipc::JavaScriptChild(rt); michael@0: if (!child->init()) { michael@0: delete child; michael@0: return nullptr; michael@0: } michael@0: return child; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPJavaScriptChild(PJavaScriptChild *child) michael@0: { michael@0: delete child; michael@0: return true; michael@0: } michael@0: michael@0: PBrowserChild* michael@0: ContentChild::AllocPBrowserChild(const IPCTabContext& aContext, michael@0: const uint32_t& aChromeFlags) michael@0: { michael@0: // We'll happily accept any kind of IPCTabContext here; we don't need to michael@0: // check that it's of a certain type for security purposes, because we michael@0: // believe whatever the parent process tells us. michael@0: michael@0: MaybeInvalidTabContext tc(aContext); michael@0: if (!tc.IsValid()) { michael@0: NS_ERROR(nsPrintfCString("Received an invalid TabContext from " michael@0: "the parent process. (%s) Crashing...", michael@0: tc.GetInvalidReason()).get()); michael@0: MOZ_CRASH("Invalid TabContext received from the parent process."); michael@0: } michael@0: michael@0: nsRefPtr child = TabChild::Create(this, tc.GetTabContext(), aChromeFlags); michael@0: michael@0: // The ref here is released in DeallocPBrowserChild. michael@0: return child.forget().take(); michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvPBrowserConstructor(PBrowserChild* actor, michael@0: const IPCTabContext& context, michael@0: const uint32_t& chromeFlags) michael@0: { michael@0: // This runs after AllocPBrowserChild() returns and the IPC machinery for this michael@0: // PBrowserChild has been set up. michael@0: michael@0: nsCOMPtr os = services::GetObserverService(); michael@0: if (os) { michael@0: nsITabChild* tc = michael@0: static_cast(static_cast(actor)); michael@0: os->NotifyObservers(tc, "tab-child-created", nullptr); michael@0: } michael@0: michael@0: static bool hasRunOnce = false; michael@0: if (!hasRunOnce) { michael@0: hasRunOnce = true; michael@0: michael@0: MOZ_ASSERT(!sFirstIdleTask); michael@0: sFirstIdleTask = NewRunnableFunction(FirstIdle); michael@0: MessageLoop::current()->PostIdleTask(FROM_HERE, sFirstIdleTask); michael@0: michael@0: // Redo InitProcessAttributes() when the app or browser is really michael@0: // launching so the attributes will be correct. michael@0: InitProcessAttributes(); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: PFileDescriptorSetChild* michael@0: ContentChild::AllocPFileDescriptorSetChild(const FileDescriptor& aFD) michael@0: { michael@0: return new FileDescriptorSetChild(aFD); michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor) michael@0: { michael@0: delete static_cast(aActor); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPBrowserChild(PBrowserChild* iframe) michael@0: { michael@0: TabChild* child = static_cast(iframe); michael@0: NS_RELEASE(child); michael@0: return true; michael@0: } michael@0: michael@0: PBlobChild* michael@0: ContentChild::AllocPBlobChild(const BlobConstructorParams& aParams) michael@0: { michael@0: return BlobChild::Create(this, aParams); michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPBlobChild(PBlobChild* aActor) michael@0: { michael@0: delete aActor; michael@0: return true; michael@0: } michael@0: michael@0: BlobChild* michael@0: ContentChild::GetOrCreateActorForBlob(nsIDOMBlob* aBlob) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: MOZ_ASSERT(aBlob); michael@0: michael@0: // If the blob represents a remote blob then we can simply pass its actor back michael@0: // here. michael@0: if (nsCOMPtr remoteBlob = do_QueryInterface(aBlob)) { michael@0: BlobChild* actor = michael@0: static_cast( michael@0: static_cast(remoteBlob->GetPBlob())); michael@0: MOZ_ASSERT(actor); michael@0: return actor; michael@0: } michael@0: michael@0: // XXX This is only safe so long as all blob implementations in our tree michael@0: // inherit nsDOMFileBase. If that ever changes then this will need to grow michael@0: // a real interface or something. michael@0: const nsDOMFileBase* blob = static_cast(aBlob); michael@0: michael@0: // We often pass blobs that are multipart but that only contain one sub-blob michael@0: // (WebActivities does this a bunch). Unwrap to reduce the number of actors michael@0: // that we have to maintain. michael@0: const nsTArray >* subBlobs = blob->GetSubBlobs(); michael@0: if (subBlobs && subBlobs->Length() == 1) { michael@0: const nsCOMPtr& subBlob = subBlobs->ElementAt(0); michael@0: MOZ_ASSERT(subBlob); michael@0: michael@0: // We can only take this shortcut if the multipart and the sub-blob are both michael@0: // Blob objects or both File objects. michael@0: nsCOMPtr multipartBlobAsFile = do_QueryInterface(aBlob); michael@0: nsCOMPtr subBlobAsFile = do_QueryInterface(subBlob); michael@0: if (!multipartBlobAsFile == !subBlobAsFile) { michael@0: // The wrapping was unnecessary, see if we can simply pass an existing michael@0: // remote blob. michael@0: if (nsCOMPtr remoteBlob = do_QueryInterface(subBlob)) { michael@0: BlobChild* actor = michael@0: static_cast( michael@0: static_cast(remoteBlob->GetPBlob())); michael@0: MOZ_ASSERT(actor); michael@0: return actor; michael@0: } michael@0: michael@0: // No need to add a reference here since the original blob must have a michael@0: // strong reference in the caller and it must also have a strong reference michael@0: // to this sub-blob. michael@0: aBlob = subBlob; michael@0: blob = static_cast(aBlob); michael@0: subBlobs = blob->GetSubBlobs(); michael@0: } michael@0: } michael@0: michael@0: // All blobs shared between processes must be immutable. michael@0: nsCOMPtr mutableBlob = do_QueryInterface(aBlob); michael@0: if (!mutableBlob || NS_FAILED(mutableBlob->SetMutable(false))) { michael@0: NS_WARNING("Failed to make blob immutable!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: ParentBlobConstructorParams params; michael@0: michael@0: if (blob->IsSizeUnknown() || blob->IsDateUnknown()) { michael@0: // We don't want to call GetSize or GetLastModifiedDate michael@0: // yet since that may stat a file on the main thread michael@0: // here. Instead we'll learn the size lazily from the michael@0: // other process. michael@0: params.blobParams() = MysteryBlobConstructorParams(); michael@0: params.optionalInputStreamParams() = void_t(); michael@0: } michael@0: else { michael@0: nsString contentType; michael@0: nsresult rv = aBlob->GetType(contentType); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: uint64_t length; michael@0: rv = aBlob->GetSize(&length); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: nsCOMPtr stream; michael@0: rv = aBlob->GetInternalStream(getter_AddRefs(stream)); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: InputStreamParams inputStreamParams; michael@0: nsTArray fds; michael@0: SerializeInputStream(stream, inputStreamParams, fds); michael@0: MOZ_ASSERT(fds.IsEmpty()); michael@0: michael@0: params.optionalInputStreamParams() = inputStreamParams; michael@0: michael@0: nsCOMPtr file = do_QueryInterface(aBlob); michael@0: if (file) { michael@0: FileBlobConstructorParams fileParams; michael@0: michael@0: rv = file->GetName(fileParams.name()); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: rv = file->GetMozLastModifiedDate(&fileParams.modDate()); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: fileParams.contentType() = contentType; michael@0: fileParams.length() = length; michael@0: michael@0: params.blobParams() = fileParams; michael@0: } else { michael@0: NormalBlobConstructorParams blobParams; michael@0: blobParams.contentType() = contentType; michael@0: blobParams.length() = length; michael@0: params.blobParams() = blobParams; michael@0: } michael@0: } michael@0: michael@0: BlobChild* actor = BlobChild::Create(this, aBlob); michael@0: NS_ENSURE_TRUE(actor, nullptr); michael@0: michael@0: return SendPBlobConstructor(actor, params) ? actor : nullptr; michael@0: } michael@0: michael@0: PCrashReporterChild* michael@0: ContentChild::AllocPCrashReporterChild(const mozilla::dom::NativeThreadId& id, michael@0: const uint32_t& processType) michael@0: { michael@0: #ifdef MOZ_CRASHREPORTER michael@0: return new CrashReporterChild(); michael@0: #else michael@0: return nullptr; michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPCrashReporterChild(PCrashReporterChild* crashreporter) michael@0: { michael@0: delete crashreporter; michael@0: return true; michael@0: } michael@0: michael@0: PHalChild* michael@0: ContentChild::AllocPHalChild() michael@0: { michael@0: return CreateHalChild(); michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPHalChild(PHalChild* aHal) michael@0: { michael@0: delete aHal; michael@0: return true; michael@0: } michael@0: michael@0: PIndexedDBChild* michael@0: ContentChild::AllocPIndexedDBChild() michael@0: { michael@0: NS_NOTREACHED("Should never get here!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPIndexedDBChild(PIndexedDBChild* aActor) michael@0: { michael@0: delete aActor; michael@0: return true; michael@0: } michael@0: michael@0: asmjscache::PAsmJSCacheEntryChild* michael@0: ContentChild::AllocPAsmJSCacheEntryChild( michael@0: const asmjscache::OpenMode& aOpenMode, michael@0: const asmjscache::WriteParams& aWriteParams, michael@0: const IPC::Principal& aPrincipal) michael@0: { michael@0: NS_NOTREACHED("Should never get here!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPAsmJSCacheEntryChild(PAsmJSCacheEntryChild* aActor) michael@0: { michael@0: asmjscache::DeallocEntryChild(aActor); michael@0: return true; michael@0: } michael@0: michael@0: PTestShellChild* michael@0: ContentChild::AllocPTestShellChild() michael@0: { michael@0: return new TestShellChild(); michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPTestShellChild(PTestShellChild* shell) michael@0: { michael@0: delete shell; michael@0: return true; michael@0: } michael@0: michael@0: jsipc::JavaScriptChild * michael@0: ContentChild::GetCPOWManager() michael@0: { michael@0: if (ManagedPJavaScriptChild().Length()) { michael@0: return static_cast(ManagedPJavaScriptChild()[0]); michael@0: } michael@0: JavaScriptChild* actor = static_cast(SendPJavaScriptConstructor()); michael@0: return actor; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvPTestShellConstructor(PTestShellChild* actor) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: PDeviceStorageRequestChild* michael@0: ContentChild::AllocPDeviceStorageRequestChild(const DeviceStorageParams& aParams) michael@0: { michael@0: return new DeviceStorageRequestChild(); michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPDeviceStorageRequestChild(PDeviceStorageRequestChild* aDeviceStorage) michael@0: { michael@0: delete aDeviceStorage; michael@0: return true; michael@0: } michael@0: michael@0: PFileSystemRequestChild* michael@0: ContentChild::AllocPFileSystemRequestChild(const FileSystemParams& aParams) michael@0: { michael@0: NS_NOTREACHED("Should never get here!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPFileSystemRequestChild(PFileSystemRequestChild* aFileSystem) michael@0: { michael@0: mozilla::dom::FileSystemTaskBase* child = michael@0: static_cast(aFileSystem); michael@0: // The reference is increased in FileSystemTaskBase::Start of michael@0: // FileSystemTaskBase.cpp. We should decrease it after IPC. michael@0: NS_RELEASE(child); michael@0: return true; michael@0: } michael@0: michael@0: PNeckoChild* michael@0: ContentChild::AllocPNeckoChild() michael@0: { michael@0: return new NeckoChild(); michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPNeckoChild(PNeckoChild* necko) michael@0: { michael@0: delete necko; michael@0: return true; michael@0: } michael@0: michael@0: PExternalHelperAppChild* michael@0: ContentChild::AllocPExternalHelperAppChild(const OptionalURIParams& uri, michael@0: const nsCString& aMimeContentType, michael@0: const nsCString& aContentDisposition, michael@0: const uint32_t& aContentDispositionHint, michael@0: const nsString& aContentDispositionFilename, michael@0: const bool& aForceSave, michael@0: const int64_t& aContentLength, michael@0: const OptionalURIParams& aReferrer, michael@0: PBrowserChild* aBrowser) michael@0: { michael@0: ExternalHelperAppChild *child = new ExternalHelperAppChild(); michael@0: child->AddRef(); michael@0: return child; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPExternalHelperAppChild(PExternalHelperAppChild* aService) michael@0: { michael@0: ExternalHelperAppChild *child = static_cast(aService); michael@0: child->Release(); michael@0: return true; michael@0: } michael@0: michael@0: PSmsChild* michael@0: ContentChild::AllocPSmsChild() michael@0: { michael@0: return new SmsChild(); michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPSmsChild(PSmsChild* aSms) michael@0: { michael@0: delete aSms; michael@0: return true; michael@0: } michael@0: michael@0: PTelephonyChild* michael@0: ContentChild::AllocPTelephonyChild() michael@0: { michael@0: MOZ_CRASH("No one should be allocating PTelephonyChild actors"); michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPTelephonyChild(PTelephonyChild* aActor) michael@0: { michael@0: delete aActor; michael@0: return true; michael@0: } michael@0: michael@0: PStorageChild* michael@0: ContentChild::AllocPStorageChild() michael@0: { michael@0: NS_NOTREACHED("We should never be manually allocating PStorageChild actors"); michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPStorageChild(PStorageChild* aActor) michael@0: { michael@0: DOMStorageDBChild* child = static_cast(aActor); michael@0: child->ReleaseIPDLReference(); michael@0: return true; michael@0: } michael@0: michael@0: PBluetoothChild* michael@0: ContentChild::AllocPBluetoothChild() michael@0: { michael@0: #ifdef MOZ_B2G_BT michael@0: MOZ_CRASH("No one should be allocating PBluetoothChild actors"); michael@0: #else michael@0: MOZ_CRASH("No support for bluetooth on this platform!"); michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPBluetoothChild(PBluetoothChild* aActor) michael@0: { michael@0: #ifdef MOZ_B2G_BT michael@0: delete aActor; michael@0: return true; michael@0: #else michael@0: MOZ_CRASH("No support for bluetooth on this platform!"); michael@0: #endif michael@0: } michael@0: michael@0: PFMRadioChild* michael@0: ContentChild::AllocPFMRadioChild() michael@0: { michael@0: #ifdef MOZ_B2G_FM michael@0: NS_RUNTIMEABORT("No one should be allocating PFMRadioChild actors"); michael@0: return nullptr; michael@0: #else michael@0: NS_RUNTIMEABORT("No support for FMRadio on this platform!"); michael@0: return nullptr; michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPFMRadioChild(PFMRadioChild* aActor) michael@0: { michael@0: #ifdef MOZ_B2G_FM michael@0: delete aActor; michael@0: return true; michael@0: #else michael@0: NS_RUNTIMEABORT("No support for FMRadio on this platform!"); michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: PSpeechSynthesisChild* michael@0: ContentChild::AllocPSpeechSynthesisChild() michael@0: { michael@0: #ifdef MOZ_WEBSPEECH michael@0: MOZ_CRASH("No one should be allocating PSpeechSynthesisChild actors"); michael@0: #else michael@0: return nullptr; michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: ContentChild::DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor) michael@0: { michael@0: #ifdef MOZ_WEBSPEECH michael@0: delete aActor; michael@0: return true; michael@0: #else michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvRegisterChrome(const InfallibleTArray& packages, michael@0: const InfallibleTArray& resources, michael@0: const InfallibleTArray& overrides, michael@0: const nsCString& locale) michael@0: { michael@0: nsCOMPtr registrySvc = nsChromeRegistry::GetService(); michael@0: nsChromeRegistryContent* chromeRegistry = michael@0: static_cast(registrySvc.get()); michael@0: chromeRegistry->RegisterRemoteChrome(packages, resources, overrides, locale); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvSetOffline(const bool& offline) michael@0: { michael@0: nsCOMPtr io (do_GetIOService()); michael@0: NS_ASSERTION(io, "IO Service can not be null"); michael@0: michael@0: io->SetOffline(offline); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: ContentChild::ActorDestroy(ActorDestroyReason why) michael@0: { michael@0: if (AbnormalShutdown == why) { michael@0: NS_WARNING("shutting down early because of crash!"); michael@0: QuickExit(); michael@0: } michael@0: michael@0: #ifndef DEBUG michael@0: // In release builds, there's no point in the content process michael@0: // going through the full XPCOM shutdown path, because it doesn't michael@0: // keep persistent state. michael@0: QuickExit(); michael@0: #endif michael@0: michael@0: if (sFirstIdleTask) { michael@0: sFirstIdleTask->Cancel(); michael@0: } michael@0: michael@0: mAlertObservers.Clear(); michael@0: michael@0: mIdleObservers.Clear(); michael@0: michael@0: nsCOMPtr svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID)); michael@0: if (svc) { michael@0: svc->UnregisterListener(mConsoleListener); michael@0: mConsoleListener->mChild = nullptr; michael@0: } michael@0: michael@0: XRE_ShutdownChildProcess(); michael@0: } michael@0: michael@0: void michael@0: ContentChild::ProcessingError(Result what) michael@0: { michael@0: switch (what) { michael@0: case MsgDropped: michael@0: QuickExit(); michael@0: michael@0: case MsgNotKnown: michael@0: NS_RUNTIMEABORT("aborting because of MsgNotKnown"); michael@0: case MsgNotAllowed: michael@0: NS_RUNTIMEABORT("aborting because of MsgNotAllowed"); michael@0: case MsgPayloadError: michael@0: NS_RUNTIMEABORT("aborting because of MsgPayloadError"); michael@0: case MsgProcessingError: michael@0: NS_RUNTIMEABORT("aborting because of MsgProcessingError"); michael@0: case MsgRouteError: michael@0: NS_RUNTIMEABORT("aborting because of MsgRouteError"); michael@0: case MsgValueError: michael@0: NS_RUNTIMEABORT("aborting because of MsgValueError"); michael@0: michael@0: default: michael@0: NS_RUNTIMEABORT("not reached"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: ContentChild::QuickExit() michael@0: { michael@0: NS_WARNING("content process _exit()ing"); michael@0: _exit(0); michael@0: } michael@0: michael@0: nsresult michael@0: ContentChild::AddRemoteAlertObserver(const nsString& aData, michael@0: nsIObserver* aObserver) michael@0: { michael@0: NS_ASSERTION(aObserver, "Adding a null observer?"); michael@0: mAlertObservers.AppendElement(new AlertObserver(aObserver, aData)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvPreferenceUpdate(const PrefSetting& aPref) michael@0: { michael@0: Preferences::SetPreference(aPref); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvNotifyAlertsObserver(const nsCString& aType, const nsString& aData) michael@0: { michael@0: for (uint32_t i = 0; i < mAlertObservers.Length(); michael@0: /*we mutate the array during the loop; ++i iff no mutation*/) { michael@0: AlertObserver* observer = mAlertObservers[i]; michael@0: if (observer->Observes(aData) && observer->Notify(aType)) { michael@0: // if aType == alertfinished, this alert is done. we can michael@0: // remove the observer. michael@0: if (aType.Equals(nsDependentCString("alertfinished"))) { michael@0: mAlertObservers.RemoveElementAt(i); michael@0: continue; michael@0: } michael@0: } michael@0: ++i; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvNotifyVisited(const URIParams& aURI) michael@0: { michael@0: nsCOMPtr newURI = DeserializeURI(aURI); michael@0: if (!newURI) { michael@0: return false; michael@0: } michael@0: nsCOMPtr history = services::GetHistoryService(); michael@0: if (history) { michael@0: history->NotifyVisited(newURI); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvAsyncMessage(const nsString& aMsg, michael@0: const ClonedMessageData& aData, michael@0: const InfallibleTArray& aCpows, michael@0: const IPC::Principal& aPrincipal) michael@0: { michael@0: nsRefPtr cpm = nsFrameMessageManager::sChildProcessManager; michael@0: if (cpm) { michael@0: StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForChild(aData); michael@0: CpowIdHolder cpows(GetCPOWManager(), aCpows); michael@0: cpm->ReceiveMessage(static_cast(cpm.get()), michael@0: aMsg, false, &cloneData, &cpows, aPrincipal, nullptr); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvGeolocationUpdate(const GeoPosition& somewhere) michael@0: { michael@0: nsCOMPtr gs = do_GetService("@mozilla.org/geolocation/service;1"); michael@0: if (!gs) { michael@0: return true; michael@0: } michael@0: nsCOMPtr position = somewhere; michael@0: gs->Update(position); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvAddPermission(const IPC::Permission& permission) michael@0: { michael@0: #if MOZ_PERMISSIONS michael@0: nsCOMPtr permissionManagerIface = michael@0: do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); michael@0: nsPermissionManager* permissionManager = michael@0: static_cast(permissionManagerIface.get()); michael@0: NS_ABORT_IF_FALSE(permissionManager, michael@0: "We have no permissionManager in the Content process !"); michael@0: michael@0: nsCOMPtr uri; michael@0: NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + nsCString(permission.host)); michael@0: NS_ENSURE_TRUE(uri, true); michael@0: michael@0: nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); michael@0: MOZ_ASSERT(secMan); michael@0: michael@0: nsCOMPtr principal; michael@0: nsresult rv = secMan->GetAppCodebasePrincipal(uri, permission.appId, michael@0: permission.isInBrowserElement, michael@0: getter_AddRefs(principal)); michael@0: NS_ENSURE_SUCCESS(rv, true); michael@0: michael@0: permissionManager->AddInternal(principal, michael@0: nsCString(permission.type), michael@0: permission.capability, michael@0: 0, michael@0: permission.expireType, michael@0: permission.expireTime, michael@0: nsPermissionManager::eNotify, michael@0: nsPermissionManager::eNoDBOperation); michael@0: #endif michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvScreenSizeChanged(const gfxIntSize& size) michael@0: { michael@0: #ifdef ANDROID michael@0: mScreenSize = size; michael@0: #else michael@0: NS_RUNTIMEABORT("Message currently only expected on android"); michael@0: #endif michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvFlushMemory(const nsString& reason) michael@0: { michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: if (IsNuwaProcess()) { michael@0: // Don't flush memory in the nuwa process: the GC thread could be frozen. michael@0: return true; michael@0: } michael@0: #endif michael@0: nsCOMPtr os = michael@0: mozilla::services::GetObserverService(); michael@0: if (os) michael@0: os->NotifyObservers(nullptr, "memory-pressure", reason.get()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvActivateA11y() michael@0: { michael@0: #ifdef ACCESSIBILITY michael@0: // Start accessibility in content process if it's running in chrome michael@0: // process. michael@0: nsCOMPtr accService = michael@0: do_GetService("@mozilla.org/accessibilityService;1"); michael@0: #endif michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvGarbageCollect() michael@0: { michael@0: // Rebroadcast the "child-gc-request" so that workers will GC. michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: if (obs) { michael@0: obs->NotifyObservers(nullptr, "child-gc-request", nullptr); michael@0: } michael@0: nsJSContext::GarbageCollectNow(JS::gcreason::DOM_IPC); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvCycleCollect() michael@0: { michael@0: // Rebroadcast the "child-cc-request" so that workers will CC. michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: if (obs) { michael@0: obs->NotifyObservers(nullptr, "child-cc-request", nullptr); michael@0: } michael@0: nsJSContext::CycleCollectNow(); michael@0: return true; michael@0: } michael@0: michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: static void michael@0: OnFinishNuwaPreparation () michael@0: { michael@0: MakeNuwaProcess(); michael@0: } michael@0: #endif michael@0: michael@0: static void michael@0: PreloadSlowThings() michael@0: { michael@0: // This fetches and creates all the built-in stylesheets. michael@0: nsLayoutStylesheetCache::UserContentSheet(); michael@0: michael@0: TabChild::PreloadSlowThings(); michael@0: michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID, michael@0: const nsCString& name, const nsCString& UAName) michael@0: { michael@0: mAppInfo.version.Assign(version); michael@0: mAppInfo.buildID.Assign(buildID); michael@0: mAppInfo.name.Assign(name); michael@0: mAppInfo.UAName.Assign(UAName); michael@0: michael@0: if (!Preferences::GetBool("dom.ipc.processPrelaunch.enabled", false)) { michael@0: return true; michael@0: } michael@0: michael@0: // If we're part of the mozbrowser machinery, go ahead and start michael@0: // preloading things. We can only do this for mozbrowser because michael@0: // PreloadSlowThings() may set the docshell of the first TabChild michael@0: // inactive, and we can only safely restore it to active from michael@0: // BrowserElementChild.js. michael@0: if ((mIsForApp || mIsForBrowser) michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: && !IsNuwaProcess() michael@0: #endif michael@0: ) { michael@0: PreloadSlowThings(); michael@0: } michael@0: michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: if (IsNuwaProcess()) { michael@0: ContentChild::GetSingleton()->RecvGarbageCollect(); michael@0: MessageLoop::current()->PostTask( michael@0: FROM_HERE, NewRunnableFunction(OnFinishNuwaPreparation)); michael@0: } michael@0: #endif michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvLastPrivateDocShellDestroyed() michael@0: { michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvFilePathUpdate(const nsString& aStorageType, michael@0: const nsString& aStorageName, michael@0: const nsString& aPath, michael@0: const nsCString& aReason) michael@0: { michael@0: nsRefPtr dsf = new DeviceStorageFile(aStorageType, aStorageName, aPath); michael@0: michael@0: nsString reason; michael@0: CopyASCIItoUTF16(aReason, reason); michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: obs->NotifyObservers(dsf, "file-watcher-update", reason.get()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvFileSystemUpdate(const nsString& aFsName, michael@0: const nsString& aVolumeName, michael@0: const int32_t& aState, michael@0: const int32_t& aMountGeneration, michael@0: const bool& aIsMediaPresent, michael@0: const bool& aIsSharing, michael@0: const bool& aIsFormatting) michael@0: { michael@0: #ifdef MOZ_WIDGET_GONK michael@0: nsRefPtr volume = new nsVolume(aFsName, aVolumeName, aState, michael@0: aMountGeneration, aIsMediaPresent, michael@0: aIsSharing, aIsFormatting); michael@0: michael@0: nsRefPtr vs = nsVolumeService::GetSingleton(); michael@0: if (vs) { michael@0: vs->UpdateVolume(volume); michael@0: } michael@0: #else michael@0: // Remove warnings about unused arguments michael@0: unused << aFsName; michael@0: unused << aVolumeName; michael@0: unused << aState; michael@0: unused << aMountGeneration; michael@0: unused << aIsMediaPresent; michael@0: unused << aIsSharing; michael@0: unused << aIsFormatting; michael@0: #endif michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvNotifyProcessPriorityChanged( michael@0: const hal::ProcessPriority& aPriority) michael@0: { michael@0: nsCOMPtr os = services::GetObserverService(); michael@0: NS_ENSURE_TRUE(os, true); michael@0: michael@0: nsRefPtr props = new nsHashPropertyBag(); michael@0: props->SetPropertyAsInt32(NS_LITERAL_STRING("priority"), michael@0: static_cast(aPriority)); michael@0: michael@0: os->NotifyObservers(static_cast(props), michael@0: "ipc:process-priority-changed", nullptr); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvMinimizeMemoryUsage() michael@0: { michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: if (IsNuwaProcess()) { michael@0: // Don't minimize memory in the nuwa process: it will perform GC, but the michael@0: // GC thread could be frozen. michael@0: return true; michael@0: } michael@0: #endif michael@0: nsCOMPtr mgr = michael@0: do_GetService("@mozilla.org/memory-reporter-manager;1"); michael@0: NS_ENSURE_TRUE(mgr, true); michael@0: michael@0: mgr->MinimizeMemoryUsage(/* callback = */ nullptr); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvNotifyPhoneStateChange(const nsString& aState) michael@0: { michael@0: nsCOMPtr os = services::GetObserverService(); michael@0: if (os) { michael@0: os->NotifyObservers(nullptr, "phone-state-changed", aState.get()); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: ContentChild::AddIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS) michael@0: { michael@0: MOZ_ASSERT(aObserver, "null idle observer"); michael@0: // Make sure aObserver isn't released while we wait for the parent michael@0: aObserver->AddRef(); michael@0: SendAddIdleObserver(reinterpret_cast(aObserver), aIdleTimeInS); michael@0: mIdleObservers.PutEntry(aObserver); michael@0: } michael@0: michael@0: void michael@0: ContentChild::RemoveIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS) michael@0: { michael@0: MOZ_ASSERT(aObserver, "null idle observer"); michael@0: SendRemoveIdleObserver(reinterpret_cast(aObserver), aIdleTimeInS); michael@0: aObserver->Release(); michael@0: mIdleObservers.RemoveEntry(aObserver); michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvNotifyIdleObserver(const uint64_t& aObserver, michael@0: const nsCString& aTopic, michael@0: const nsString& aTimeStr) michael@0: { michael@0: nsIObserver* observer = reinterpret_cast(aObserver); michael@0: if (mIdleObservers.Contains(observer)) { michael@0: observer->Observe(nullptr, aTopic.get(), aTimeStr.get()); michael@0: } else { michael@0: NS_WARNING("Received notification for an idle observer that was removed."); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvLoadAndRegisterSheet(const URIParams& aURI, const uint32_t& aType) michael@0: { michael@0: nsCOMPtr uri = DeserializeURI(aURI); michael@0: if (!uri) { michael@0: return true; michael@0: } michael@0: michael@0: nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance(); michael@0: if (sheetService) { michael@0: sheetService->LoadAndRegisterSheet(uri, aType); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ContentChild::RecvUnregisterSheet(const URIParams& aURI, const uint32_t& aType) michael@0: { michael@0: nsCOMPtr uri = DeserializeURI(aURI); michael@0: if (!uri) { michael@0: return true; michael@0: } michael@0: michael@0: nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance(); michael@0: if (sheetService) { michael@0: sheetService->UnregisterSheet(uri, aType); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: class CallNuwaSpawn : public nsRunnable michael@0: { michael@0: public: michael@0: NS_IMETHOD Run() michael@0: { michael@0: NuwaSpawn(); michael@0: if (IsNuwaProcess()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // In the new process. michael@0: ContentChild* child = ContentChild::GetSingleton(); michael@0: child->SetProcessName(NS_LITERAL_STRING("(Preallocated app)"), false); michael@0: mozilla::ipc::Transport* transport = child->GetTransport(); michael@0: int fd = transport->GetFileDescriptor(); michael@0: transport->ResetFileDescriptor(fd); michael@0: michael@0: IToplevelProtocol* toplevel = child->GetFirstOpenedActors(); michael@0: while (toplevel != nullptr) { michael@0: transport = toplevel->GetTransport(); michael@0: fd = transport->GetFileDescriptor(); michael@0: transport->ResetFileDescriptor(fd); michael@0: michael@0: toplevel = toplevel->getNext(); michael@0: } michael@0: michael@0: // Perform other after-fork initializations. michael@0: InitOnContentProcessCreated(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: static void michael@0: DoNuwaFork() michael@0: { michael@0: NS_ASSERTION(NuwaSpawnPrepare != nullptr, michael@0: "NuwaSpawnPrepare() is not available!"); michael@0: NuwaSpawnPrepare(); // NuwaSpawn will be blocked. michael@0: michael@0: { michael@0: nsCOMPtr callSpawn(new CallNuwaSpawn()); michael@0: NS_DispatchToMainThread(callSpawn); michael@0: } michael@0: michael@0: // IOThread should be blocked here for waiting NuwaSpawn(). michael@0: NS_ASSERTION(NuwaSpawnWait != nullptr, michael@0: "NuwaSpawnWait() is not available!"); michael@0: NuwaSpawnWait(); // Now! NuwaSpawn can go. michael@0: // Here, we can make sure the spawning was finished. michael@0: } michael@0: michael@0: /** michael@0: * This function should keep IO thread in a stable state and freeze it michael@0: * until the spawning is finished. michael@0: */ michael@0: static void michael@0: RunNuwaFork() michael@0: { michael@0: if (NuwaCheckpointCurrentThread()) { michael@0: DoNuwaFork(); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: bool michael@0: ContentChild::RecvNuwaFork() michael@0: { michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: if (sNuwaForking) { // No reentry. michael@0: return true; michael@0: } michael@0: sNuwaForking = true; michael@0: michael@0: // We want to ensure that the PBackground actor gets cloned in the Nuwa michael@0: // process before we freeze. Also, we have to do this to avoid deadlock. michael@0: // Protocols that are "opened" (e.g. PBackground, PCompositor) block the michael@0: // main thread to wait for the IPC thread during the open operation. michael@0: // NuwaSpawnWait() blocks the IPC thread to wait for the main thread when michael@0: // the Nuwa process is forked. Unless we ensure that the two cannot happen michael@0: // at the same time then we risk deadlock. Spinning the event loop here michael@0: // guarantees the ordering is safe for PBackground. michael@0: while (!BackgroundChild::GetForCurrentThread()) { michael@0: if (NS_WARN_IF(!NS_ProcessNextEvent())) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: MessageLoop* ioloop = XRE_GetIOMessageLoop(); michael@0: ioloop->PostTask(FROM_HERE, NewRunnableFunction(RunNuwaFork)); michael@0: return true; michael@0: #else michael@0: return false; // Makes the underlying IPC channel abort. michael@0: #endif michael@0: } michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: extern "C" { michael@0: michael@0: #if defined(MOZ_NUWA_PROCESS) michael@0: NS_EXPORT void michael@0: GetProtoFdInfos(NuwaProtoFdInfo* aInfoList, michael@0: size_t aInfoListSize, michael@0: size_t* aInfoSize) michael@0: { michael@0: size_t i = 0; michael@0: michael@0: mozilla::dom::ContentChild* content = michael@0: mozilla::dom::ContentChild::GetSingleton(); michael@0: aInfoList[i].protoId = content->GetProtocolId(); michael@0: aInfoList[i].originFd = michael@0: content->GetTransport()->GetFileDescriptor(); michael@0: i++; michael@0: michael@0: for (IToplevelProtocol* actor = content->GetFirstOpenedActors(); michael@0: actor != nullptr; michael@0: actor = actor->getNext()) { michael@0: if (i >= aInfoListSize) { michael@0: NS_RUNTIMEABORT("Too many top level protocols!"); michael@0: } michael@0: michael@0: aInfoList[i].protoId = actor->GetProtocolId(); michael@0: aInfoList[i].originFd = michael@0: actor->GetTransport()->GetFileDescriptor(); michael@0: i++; michael@0: } michael@0: michael@0: if (i > NUWA_TOPLEVEL_MAX) { michael@0: NS_RUNTIMEABORT("Too many top level protocols!"); michael@0: } michael@0: *aInfoSize = i; michael@0: } michael@0: michael@0: class RunAddNewIPCProcess : public nsRunnable michael@0: { michael@0: public: michael@0: RunAddNewIPCProcess(pid_t aPid, michael@0: nsTArray& aMaps) michael@0: : mPid(aPid) michael@0: { michael@0: mMaps.SwapElements(aMaps); michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: mozilla::dom::ContentChild::GetSingleton()-> michael@0: SendAddNewProcess(mPid, mMaps); michael@0: michael@0: MOZ_ASSERT(sNuwaForking); michael@0: sNuwaForking = false; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: pid_t mPid; michael@0: nsTArray mMaps; michael@0: }; michael@0: michael@0: /** michael@0: * AddNewIPCProcess() is called by Nuwa process to tell the parent michael@0: * process that a new process is created. michael@0: * michael@0: * In the newly created process, ResetContentChildTransport() is called to michael@0: * reset fd for the IPC Channel and the session. michael@0: */ michael@0: NS_EXPORT void michael@0: AddNewIPCProcess(pid_t aPid, NuwaProtoFdInfo* aInfoList, size_t aInfoListSize) michael@0: { michael@0: nsTArray maps; michael@0: michael@0: for (size_t i = 0; i < aInfoListSize; i++) { michael@0: int _fd = aInfoList[i].newFds[NUWA_NEWFD_PARENT]; michael@0: mozilla::ipc::FileDescriptor fd(_fd); michael@0: mozilla::ipc::ProtocolFdMapping map(aInfoList[i].protoId, fd); michael@0: maps.AppendElement(map); michael@0: } michael@0: michael@0: nsRefPtr runner = new RunAddNewIPCProcess(aPid, maps); michael@0: NS_DispatchToMainThread(runner); michael@0: } michael@0: michael@0: NS_EXPORT void michael@0: OnNuwaProcessReady() michael@0: { michael@0: mozilla::dom::ContentChild* content = michael@0: mozilla::dom::ContentChild::GetSingleton(); michael@0: content->SendNuwaReady(); michael@0: } michael@0: michael@0: NS_EXPORT void michael@0: AfterNuwaFork() michael@0: { michael@0: SetCurrentProcessPrivileges(base::PRIVILEGES_DEFAULT); michael@0: } michael@0: michael@0: #endif // MOZ_NUWA_PROCESS michael@0: michael@0: }