diff options
Diffstat (limited to 'dom/ipc/ContentParent.cpp')
-rw-r--r-- | dom/ipc/ContentParent.cpp | 5185 |
1 files changed, 5185 insertions, 0 deletions
diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp new file mode 100644 index 000000000..ff40db8d7 --- /dev/null +++ b/dom/ipc/ContentParent.cpp @@ -0,0 +1,5185 @@ +/* -*- 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/. */ + +#include "mozilla/DebugOnly.h" + +#include "base/basictypes.h" + +#include "ContentParent.h" +#include "TabParent.h" + +#if defined(ANDROID) || defined(LINUX) +# include <sys/time.h> +# include <sys/resource.h> +#endif + +#ifdef MOZ_WIDGET_GONK +#include <sys/types.h> +#include <sys/wait.h> +#endif + +#include "chrome/common/process_watcher.h" + +#include "mozilla/a11y/PDocAccessible.h" +#include "AppProcessChecker.h" +#include "AudioChannelService.h" +#include "BlobParent.h" +#include "CrashReporterParent.h" +#include "GMPServiceParent.h" +#include "HandlerServiceParent.h" +#include "IHistory.h" +#include "imgIContainer.h" +#include "mozIApplication.h" +#if defined(XP_WIN) && defined(ACCESSIBILITY) +#include "mozilla/a11y/AccessibleWrap.h" +#endif +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/StyleSheetInlines.h" +#include "mozilla/DataStorage.h" +#include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h" +#include "mozilla/docshell/OfflineCacheUpdateParent.h" +#include "mozilla/dom/DataTransfer.h" +#include "mozilla/dom/DOMStorageIPC.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/FileSystemSecurity.h" +#include "mozilla/dom/ExternalHelperAppParent.h" +#include "mozilla/dom/GetFilesHelper.h" +#include "mozilla/dom/GeolocationBinding.h" +#include "mozilla/dom/Notification.h" +#include "mozilla/dom/PContentBridgeParent.h" +#include "mozilla/dom/PContentPermissionRequestParent.h" +#include "mozilla/dom/PCycleCollectWithLogsParent.h" +#include "mozilla/dom/PMemoryReportRequestParent.h" +#include "mozilla/dom/ServiceWorkerRegistrar.h" +#include "mozilla/dom/power/PowerManagerService.h" +#include "mozilla/dom/Permissions.h" +#include "mozilla/dom/PresentationParent.h" +#include "mozilla/dom/PPresentationParent.h" +#include "mozilla/dom/PushNotifier.h" +#include "mozilla/dom/FlyWebPublishedServerIPC.h" +#include "mozilla/dom/quota/QuotaManagerService.h" +#include "mozilla/dom/time/DateCacheCleaner.h" +#include "mozilla/embedding/printingui/PrintingParent.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/hal_sandbox/PHalParent.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/BackgroundParent.h" +#include "mozilla/ipc/FileDescriptorUtils.h" +#include "mozilla/ipc/PSendStreamParent.h" +#include "mozilla/ipc/TestShellParent.h" +#include "mozilla/ipc/InputStreamUtils.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" +#include "mozilla/layers/PAPZParent.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/ImageBridgeParent.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" +#include "mozilla/layout/RenderFrameParent.h" +#include "mozilla/LookAndFeel.h" +#include "mozilla/media/MediaParent.h" +#include "mozilla/Move.h" +#include "mozilla/net/NeckoParent.h" +#include "mozilla/plugins/PluginBridge.h" +#include "mozilla/Preferences.h" +#include "mozilla/ProcessHangMonitor.h" +#include "mozilla/ProcessHangMonitorIPC.h" +#ifdef MOZ_ENABLE_PROFILER_SPS +#include "mozilla/ProfileGatherer.h" +#endif +#include "mozilla/ScopeExit.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/Telemetry.h" +#include "mozilla/WebBrowserPersistDocumentParent.h" +#include "mozilla/Unused.h" +#include "nsAnonymousTemporaryFile.h" +#include "nsAppRunner.h" +#include "nsCDefaultURIFixup.h" +#include "nsCExternalHandlerService.h" +#include "nsCOMPtr.h" +#include "nsChromeRegistryChrome.h" +#include "nsConsoleMessage.h" +#include "nsConsoleService.h" +#include "nsContentUtils.h" +#include "nsDebugImpl.h" +#include "nsFrameMessageManager.h" +#include "nsHashPropertyBag.h" +#include "nsIAlertsService.h" +#include "nsIAppsService.h" +#include "nsIClipboard.h" +#include "nsContentPermissionHelper.h" +#include "nsICycleCollectorListener.h" +#include "nsIDocShellTreeOwner.h" +#include "nsIDocument.h" +#include "nsIDOMGeoGeolocation.h" +#include "nsIDOMGeoPositionError.h" +#include "nsIDragService.h" +#include "mozilla/dom/WakeLock.h" +#include "nsIDOMWindow.h" +#include "nsIExternalProtocolService.h" +#include "nsIFormProcessor.h" +#include "nsIGfxInfo.h" +#include "nsIIdleService.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIMemoryInfoDumper.h" +#include "nsIMemoryReporter.h" +#include "nsIMozBrowserFrame.h" +#include "nsIMutable.h" +#include "nsINSSU2FToken.h" +#include "nsIObserverService.h" +#include "nsIParentChannel.h" +#include "nsIPresShell.h" +#include "nsIRemoteWindowContext.h" +#include "nsIScriptError.h" +#include "nsIScriptSecurityManager.h" +#include "nsISiteSecurityService.h" +#include "nsISpellChecker.h" +#include "nsISupportsPrimitives.h" +#include "nsITimer.h" +#include "nsIURIFixup.h" +#include "nsIDocShellTreeOwner.h" +#include "nsIXULWindow.h" +#include "nsIDOMChromeWindow.h" +#include "nsIWindowWatcher.h" +#include "nsPIWindowWatcher.h" +#include "nsWindowWatcher.h" +#include "nsIXULRuntime.h" +#include "mozilla/dom/nsMixedContentBlocker.h" +#include "nsMemoryInfoDumper.h" +#include "nsMemoryReporterManager.h" +#include "nsServiceManagerUtils.h" +#include "nsStyleSheetService.h" +#include "nsThreadUtils.h" +#include "nsToolkitCompsCID.h" +#include "nsWidgetsCID.h" +#include "PreallocatedProcessManager.h" +#include "ProcessPriorityManager.h" +#include "SandboxHal.h" +#include "ScreenManagerParent.h" +#include "SourceSurfaceRawData.h" +#include "TabParent.h" +#include "URIUtils.h" +#include "nsIWebBrowserChrome.h" +#include "nsIDocShell.h" +#include "nsDocShell.h" +#include "nsOpenURIInFrameParams.h" +#include "mozilla/net/NeckoMessageUtils.h" +#include "gfxPrefs.h" +#include "prio.h" +#include "private/pprio.h" +#include "ContentProcessManager.h" +#include "mozilla/dom/ipc/StructuredCloneData.h" +#include "mozilla/psm/PSMContentListener.h" +#include "nsPluginHost.h" +#include "nsPluginTags.h" +#include "nsIBlocklistService.h" +#include "mozilla/StyleSheet.h" +#include "mozilla/StyleSheetInlines.h" +#include "nsHostObjectProtocolHandler.h" +#include "nsICaptivePortalService.h" + +#include "nsIBidiKeyboard.h" + +#include "nsLayoutStylesheetCache.h" + +#ifdef MOZ_WEBRTC +#include "signaling/src/peerconnection/WebrtcGlobalParent.h" +#endif + +#if defined(ANDROID) || defined(LINUX) +#include "nsSystemInfo.h" +#endif + +#if defined(XP_LINUX) +#include "mozilla/Hal.h" +#endif + +#ifdef ANDROID +# include "gfxAndroidPlatform.h" +#endif + +#ifdef MOZ_PERMISSIONS +# include "nsPermissionManager.h" +#endif + +#ifdef MOZ_WIDGET_ANDROID +# include "AndroidBridge.h" +#endif + +#ifdef MOZ_WIDGET_GONK +#include "nsIVolume.h" +#include "nsVolumeService.h" +#include "nsIVolumeService.h" +#include "SpeakerManagerService.h" +using namespace mozilla::system; +#endif + +#ifdef MOZ_WIDGET_GTK +#include <gdk/gdk.h> +#endif + +#include "mozilla/RemoteSpellCheckEngineParent.h" + +#include "Crypto.h" + +#ifdef MOZ_WEBSPEECH +#include "mozilla/dom/SpeechSynthesisParent.h" +#endif + +#if defined(MOZ_CONTENT_SANDBOX) && defined(XP_LINUX) +#include "mozilla/SandboxInfo.h" +#include "mozilla/SandboxBroker.h" +#include "mozilla/SandboxBrokerPolicyFactory.h" +#endif + +#ifdef MOZ_TOOLKIT_SEARCH +#include "nsIBrowserSearchService.h" +#endif + +#ifdef MOZ_ENABLE_PROFILER_SPS +#include "nsIProfiler.h" +#include "nsIProfileSaveEvent.h" +#endif + +#ifdef XP_WIN +#include "mozilla/widget/AudioSession.h" +#endif + +#ifdef MOZ_CRASHREPORTER +#include "nsThread.h" +#endif + +#ifdef ACCESSIBILITY +#include "nsAccessibilityService.h" +#endif + +// For VP9Benchmark::sBenchmarkFpsPref +#include "Benchmark.h" + +static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); + +#if defined(XP_WIN) +// e10s forced enable pref, defined in nsAppRunner.cpp +extern const char* kForceEnableE10sPref; +#endif + +using base::ChildPrivileges; +using base::KillProcess; +#ifdef MOZ_ENABLE_PROFILER_SPS +using mozilla::ProfileGatherer; +#endif + +#ifdef MOZ_CRASHREPORTER +using namespace CrashReporter; +#endif +using namespace mozilla::dom::power; +using namespace mozilla::media; +using namespace mozilla::embedding; +using namespace mozilla::gfx; +using namespace mozilla::gmp; +using namespace mozilla::hal; +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; + +// XXX Workaround for bug 986973 to maintain the existing broken semantics +template<> +struct nsIConsoleService::COMTypeInfo<nsConsoleService, void> { + static const nsIID kIID; +}; +const nsIID nsIConsoleService::COMTypeInfo<nsConsoleService, void>::kIID = NS_ICONSOLESERVICE_IID; + +namespace mozilla { +namespace dom { + +#define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline" +#define NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC "ipc:network:set-connectivity" + +class MemoryReportRequestParent : public PMemoryReportRequestParent +{ +public: + explicit MemoryReportRequestParent(uint32_t aGeneration); + + virtual ~MemoryReportRequestParent(); + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + virtual bool RecvReport(const MemoryReport& aReport) override; + virtual bool Recv__delete__() override; + +private: + const uint32_t mGeneration; + // Non-null if we haven't yet called EndProcessReport() on it. + RefPtr<nsMemoryReporterManager> mReporterManager; + + ContentParent* Owner() + { + return static_cast<ContentParent*>(Manager()); + } +}; + +MemoryReportRequestParent::MemoryReportRequestParent(uint32_t aGeneration) + : mGeneration(aGeneration) +{ + MOZ_COUNT_CTOR(MemoryReportRequestParent); + mReporterManager = nsMemoryReporterManager::GetOrCreate(); + NS_WARNING_ASSERTION(mReporterManager, "GetOrCreate failed"); +} + +bool +MemoryReportRequestParent::RecvReport(const MemoryReport& aReport) +{ + if (mReporterManager) { + mReporterManager->HandleChildReport(mGeneration, aReport); + } + return true; +} + +bool +MemoryReportRequestParent::Recv__delete__() +{ + // Notifying the reporter manager is done in ActorDestroy, because + // it needs to happen even if the child process exits mid-report. + // (The reporter manager will time out eventually, but let's avoid + // that if possible.) + return true; +} + +void +MemoryReportRequestParent::ActorDestroy(ActorDestroyReason aWhy) +{ + if (mReporterManager) { + mReporterManager->EndProcessReport(mGeneration, aWhy == Deletion); + mReporterManager = nullptr; + } +} + +MemoryReportRequestParent::~MemoryReportRequestParent() +{ + MOZ_ASSERT(!mReporterManager); + MOZ_COUNT_DTOR(MemoryReportRequestParent); +} + +// IPC receiver for remote GC/CC logging. +class CycleCollectWithLogsParent final : public PCycleCollectWithLogsParent +{ +public: + ~CycleCollectWithLogsParent() + { + MOZ_COUNT_DTOR(CycleCollectWithLogsParent); + } + + static bool AllocAndSendConstructor(ContentParent* aManager, + bool aDumpAllTraces, + nsICycleCollectorLogSink* aSink, + nsIDumpGCAndCCLogsCallback* aCallback) + { + CycleCollectWithLogsParent *actor; + FILE* gcLog; + FILE* ccLog; + nsresult rv; + + actor = new CycleCollectWithLogsParent(aSink, aCallback); + rv = actor->mSink->Open(&gcLog, &ccLog); + if (NS_WARN_IF(NS_FAILED(rv))) { + delete actor; + return false; + } + + return aManager-> + SendPCycleCollectWithLogsConstructor(actor, + aDumpAllTraces, + FILEToFileDescriptor(gcLog), + FILEToFileDescriptor(ccLog)); + } + +private: + virtual bool RecvCloseGCLog() override + { + Unused << mSink->CloseGCLog(); + return true; + } + + virtual bool RecvCloseCCLog() override + { + Unused << mSink->CloseCCLog(); + return true; + } + + virtual bool Recv__delete__() override + { + // Report completion to mCallback only on successful + // completion of the protocol. + nsCOMPtr<nsIFile> gcLog, ccLog; + mSink->GetGcLog(getter_AddRefs(gcLog)); + mSink->GetCcLog(getter_AddRefs(ccLog)); + Unused << mCallback->OnDump(gcLog, ccLog, /* parent = */ false); + return true; + } + + virtual void ActorDestroy(ActorDestroyReason aReason) override + { + // If the actor is unexpectedly destroyed, we deliberately + // don't call Close[GC]CLog on the sink, because the logs may + // be incomplete. See also the nsCycleCollectorLogSinkToFile + // implementaiton of those methods, and its destructor. + } + + CycleCollectWithLogsParent(nsICycleCollectorLogSink *aSink, + nsIDumpGCAndCCLogsCallback *aCallback) + : mSink(aSink), mCallback(aCallback) + { + MOZ_COUNT_CTOR(CycleCollectWithLogsParent); + } + + nsCOMPtr<nsICycleCollectorLogSink> mSink; + nsCOMPtr<nsIDumpGCAndCCLogsCallback> mCallback; +}; + +// A memory reporter for ContentParent objects themselves. +class ContentParentsMemoryReporter final : public nsIMemoryReporter +{ + ~ContentParentsMemoryReporter() {} +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIMEMORYREPORTER +}; + +NS_IMPL_ISUPPORTS(ContentParentsMemoryReporter, nsIMemoryReporter) + +NS_IMETHODIMP +ContentParentsMemoryReporter::CollectReports( + nsIHandleReportCallback* aHandleReport, + nsISupports* aData, + bool aAnonymize) +{ + AutoTArray<ContentParent*, 16> cps; + ContentParent::GetAllEvenIfDead(cps); + + for (uint32_t i = 0; i < cps.Length(); i++) { + ContentParent* cp = cps[i]; + MessageChannel* channel = cp->GetIPCChannel(); + + nsString friendlyName; + cp->FriendlyName(friendlyName, aAnonymize); + + cp->AddRef(); + nsrefcnt refcnt = cp->Release(); + + const char* channelStr = "no channel"; + uint32_t numQueuedMessages = 0; + if (channel) { + if (channel->Unsound_IsClosed()) { + channelStr = "closed channel"; + } else { + channelStr = "open channel"; + } + numQueuedMessages = channel->Unsound_NumQueuedMessages(); + } + + nsPrintfCString path("queued-ipc-messages/content-parent" + "(%s, pid=%d, %s, 0x%p, refcnt=%d)", + NS_ConvertUTF16toUTF8(friendlyName).get(), + cp->Pid(), channelStr, + static_cast<nsIContentParent*>(cp), refcnt); + + NS_NAMED_LITERAL_CSTRING(desc, + "The number of unset IPC messages held in this ContentParent's " + "channel. A large value here might indicate that we're leaking " + "messages. Similarly, a ContentParent object for a process that's no " + "longer running could indicate that we're leaking ContentParents."); + + aHandleReport->Callback(/* process */ EmptyCString(), path, + KIND_OTHER, UNITS_COUNT, + numQueuedMessages, desc, aData); + } + + return NS_OK; +} + +nsDataHashtable<nsStringHashKey, ContentParent*>* ContentParent::sAppContentParents; +nsTArray<ContentParent*>* ContentParent::sNonAppContentParents; +nsTArray<ContentParent*>* ContentParent::sLargeAllocationContentParents; +nsTArray<ContentParent*>* ContentParent::sPrivateContent; +StaticAutoPtr<LinkedList<ContentParent> > ContentParent::sContentParents; +#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX) +UniquePtr<SandboxBrokerPolicyFactory> ContentParent::sSandboxBrokerPolicyFactory; +#endif + +// This is true when subprocess launching is enabled. This is the +// case between StartUp() and ShutDown() or JoinAllSubprocesses(). +static bool sCanLaunchSubprocesses; + +// Set to true if the DISABLE_UNSAFE_CPOW_WARNINGS environment variable is +// set. +static bool sDisableUnsafeCPOWWarnings = false; + +// The first content child has ID 1, so the chrome process can have ID 0. +static uint64_t gContentChildID = 1; + +// We want the prelaunched process to know that it's for apps, but not +// actually for any app in particular. Use a magic manifest URL. +// Can't be a static constant. +#define MAGIC_PREALLOCATED_APP_MANIFEST_URL NS_LITERAL_STRING("{{template}}") + +static const char* sObserverTopics[] = { + "xpcom-shutdown", + "profile-before-change", + NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC, + NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC, + NS_IPC_CAPTIVE_PORTAL_SET_STATE, + "memory-pressure", + "child-gc-request", + "child-cc-request", + "child-mmu-request", + "last-pb-context-exited", + "file-watcher-update", +#ifdef MOZ_WIDGET_GONK + NS_VOLUME_STATE_CHANGED, + NS_VOLUME_REMOVED, + "phone-state-changed", +#endif +#ifdef ACCESSIBILITY + "a11y-init-or-shutdown", +#endif +#ifdef MOZ_ENABLE_PROFILER_SPS + "profiler-started", + "profiler-stopped", + "profiler-paused", + "profiler-resumed", + "profiler-subprocess-gather", + "profiler-subprocess", +#endif + "cacheservice:empty-cache", +}; + +// PreallocateAppProcess is called by the PreallocatedProcessManager. +// ContentParent then takes this process back within +// GetNewOrPreallocatedAppProcess. +/*static*/ already_AddRefed<ContentParent> +ContentParent::PreallocateAppProcess() +{ + RefPtr<ContentParent> process = + new ContentParent(/* app = */ nullptr, + /* aOpener = */ nullptr, + /* isForBrowserElement = */ false, + /* isForPreallocated = */ true); + + if (!process->LaunchSubprocess(PROCESS_PRIORITY_PREALLOC)) { + return nullptr; + } + + process->Init(); + return process.forget(); +} + +/*static*/ already_AddRefed<ContentParent> +ContentParent::GetNewOrPreallocatedAppProcess(mozIApplication* aApp, + ProcessPriority aInitialPriority, + ContentParent* aOpener, + /*out*/ bool* aTookPreAllocated) +{ + MOZ_ASSERT(aApp); + RefPtr<ContentParent> process = PreallocatedProcessManager::Take(); + + if (process) { + if (!process->SetPriorityAndCheckIsAlive(aInitialPriority)) { + // Kill the process just in case it's not actually dead; we don't want + // to "leak" this process! + process->KillHard("GetNewOrPreallocatedAppProcess"); + } + else { + nsAutoString manifestURL; + if (NS_FAILED(aApp->GetManifestURL(manifestURL))) { + NS_ERROR("Failed to get manifest URL"); + return nullptr; + } + process->TransformPreallocatedIntoApp(aOpener, manifestURL); + process->ForwardKnownInfo(); + + if (aTookPreAllocated) { + *aTookPreAllocated = true; + } + return process.forget(); + } + } + + NS_WARNING("Unable to use pre-allocated app process"); + process = new ContentParent(aApp, + /* aOpener = */ aOpener, + /* isForBrowserElement = */ false, + /* isForPreallocated = */ false); + + if (!process->LaunchSubprocess(aInitialPriority)) { + return nullptr; + } + + process->Init(); + process->ForwardKnownInfo(); + + if (aTookPreAllocated) { + *aTookPreAllocated = false; + } + + return process.forget(); +} + +/*static*/ void +ContentParent::StartUp() +{ + // We could launch sub processes from content process + // FIXME Bug 1023701 - Stop using ContentParent static methods in + // child process + sCanLaunchSubprocesses = true; + + if (!XRE_IsParentProcess()) { + return; + } + +#if defined(MOZ_CONTENT_SANDBOX) && defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19 + // Require sandboxing on B2G >= KitKat. This condition must stay + // in sync with ContentChild::RecvSetProcessSandbox. + if (!SandboxInfo::Get().CanSandboxContent()) { + // MOZ_CRASH strings are only for debug builds; make sure the + // message is clear on non-debug builds as well: + printf_stderr("Sandboxing support is required on this platform. " + "Recompile kernel with CONFIG_SECCOMP_FILTER=y\n"); + MOZ_CRASH("Sandboxing support is required on this platform."); + } +#endif + + // Note: This reporter measures all ContentParents. + RegisterStrongMemoryReporter(new ContentParentsMemoryReporter()); + + mozilla::dom::time::InitializeDateCacheCleaner(); + + BlobParent::Startup(BlobParent::FriendKey()); + + BackgroundChild::Startup(); + + // Try to preallocate a process that we can transform into an app later. + PreallocatedProcessManager::AllocateAfterDelay(); + + sDisableUnsafeCPOWWarnings = PR_GetEnv("DISABLE_UNSAFE_CPOW_WARNINGS"); + +#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX) + sSandboxBrokerPolicyFactory = MakeUnique<SandboxBrokerPolicyFactory>(); +#endif +} + +/*static*/ void +ContentParent::ShutDown() +{ + // No-op for now. We rely on normal process shutdown and + // ClearOnShutdown() to clean up our state. + sCanLaunchSubprocesses = false; + +#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX) + sSandboxBrokerPolicyFactory = nullptr; +#endif +} + +/*static*/ void +ContentParent::JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses, + Monitor* aMonitor, bool* aDone) +{ + const nsTArray<ContentParent*>& processes = *aProcesses; + for (uint32_t i = 0; i < processes.Length(); ++i) { + if (GeckoChildProcessHost* process = processes[i]->mSubprocess) { + process->Join(); + } + } + { + MonitorAutoLock lock(*aMonitor); + *aDone = true; + lock.Notify(); + } + // Don't touch any arguments to this function from now on. +} + +/*static*/ void +ContentParent::JoinAllSubprocesses() +{ + MOZ_ASSERT(NS_IsMainThread()); + + AutoTArray<ContentParent*, 8> processes; + GetAll(processes); + if (processes.IsEmpty()) { + printf_stderr("There are no live subprocesses."); + return; + } + + printf_stderr("Subprocesses are still alive. Doing emergency join.\n"); + + bool done = false; + Monitor monitor("mozilla.dom.ContentParent.JoinAllSubprocesses"); + XRE_GetIOMessageLoop()->PostTask(NewRunnableFunction( + &ContentParent::JoinProcessesIOThread, + &processes, &monitor, &done)); + { + MonitorAutoLock lock(monitor); + while (!done) { + lock.Wait(); + } + } + + sCanLaunchSubprocesses = false; +} + +/*static*/ already_AddRefed<ContentParent> +ContentParent::GetNewOrUsedBrowserProcess(bool aForBrowserElement, + ProcessPriority aPriority, + ContentParent* aOpener, + bool aLargeAllocationProcess) +{ + nsTArray<ContentParent*>* contentParents; + int32_t maxContentParents; + + // Decide which pool of content parents we are going to be pulling from based + // on the aLargeAllocationProcess flag. + if (aLargeAllocationProcess) { + if (!sLargeAllocationContentParents) { + sLargeAllocationContentParents = new nsTArray<ContentParent*>(); + } + contentParents = sLargeAllocationContentParents; + + maxContentParents = Preferences::GetInt("dom.ipc.dedicatedProcessCount", 2); + } else { + if (!sNonAppContentParents) { + sNonAppContentParents = new nsTArray<ContentParent*>(); + } + contentParents = sNonAppContentParents; + + maxContentParents = Preferences::GetInt("dom.ipc.processCount", 1); + } + + if (maxContentParents < 1) { + maxContentParents = 1; + } + + if (contentParents->Length() >= uint32_t(maxContentParents)) { + uint32_t maxSelectable = std::min(static_cast<uint32_t>(contentParents->Length()), + static_cast<uint32_t>(maxContentParents)); + uint32_t startIdx = rand() % maxSelectable; + uint32_t currIdx = startIdx; + do { + RefPtr<ContentParent> p = (*contentParents)[currIdx]; + NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sNonAppContntParents?"); + if (p->mOpener == aOpener) { + return p.forget(); + } + currIdx = (currIdx + 1) % maxSelectable; + } while (currIdx != startIdx); + } + + // Try to take and transform the preallocated process into browser. + RefPtr<ContentParent> p = PreallocatedProcessManager::Take(); + if (p) { + p->TransformPreallocatedIntoBrowser(aOpener); + } else { + // Failed in using the preallocated process: fork from the chrome process. + p = new ContentParent(/* app = */ nullptr, + aOpener, + aForBrowserElement, + /* isForPreallocated = */ false); + + if (!p->LaunchSubprocess(aPriority)) { + return nullptr; + } + + p->Init(); + } + + p->mLargeAllocationProcess = aLargeAllocationProcess; + + p->ForwardKnownInfo(); + + contentParents->AppendElement(p); + return p.forget(); +} + +/*static*/ ProcessPriority +ContentParent::GetInitialProcessPriority(Element* aFrameElement) +{ + // Frames with mozapptype == critical which are expecting a system message + // get FOREGROUND_HIGH priority. + + if (!aFrameElement) { + return PROCESS_PRIORITY_FOREGROUND; + } + + if (aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype, + NS_LITERAL_STRING("inputmethod"), eCaseMatters)) { + return PROCESS_PRIORITY_FOREGROUND_KEYBOARD; + } else if (!aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype, + NS_LITERAL_STRING("critical"), eCaseMatters)) { + return PROCESS_PRIORITY_FOREGROUND; + } + + nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(aFrameElement); + if (!browserFrame) { + return PROCESS_PRIORITY_FOREGROUND; + } + + return PROCESS_PRIORITY_FOREGROUND; +} + +#if defined(XP_WIN) +extern const wchar_t* kPluginWidgetContentParentProperty; + +/*static*/ void +ContentParent::SendAsyncUpdate(nsIWidget* aWidget) +{ + if (!aWidget || aWidget->Destroyed()) { + return; + } + // Fire off an async request to the plugin to paint its window + HWND hwnd = (HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW); + NS_ASSERTION(hwnd, "Expected valid hwnd value."); + ContentParent* cp = reinterpret_cast<ContentParent*>( + ::GetPropW(hwnd, kPluginWidgetContentParentProperty)); + if (cp && !cp->IsDestroyed()) { + Unused << cp->SendUpdateWindow((uintptr_t)hwnd); + } +} +#endif // defined(XP_WIN) + +bool +ContentParent::PreallocatedProcessReady() +{ + return true; +} + +bool +ContentParent::RecvCreateChildProcess(const IPCTabContext& aContext, + const hal::ProcessPriority& aPriority, + const TabId& aOpenerTabId, + ContentParentId* aCpId, + bool* aIsForApp, + bool* aIsForBrowser, + TabId* aTabId) +{ +#if 0 + if (!CanOpenBrowser(aContext)) { + return false; + } +#endif + RefPtr<ContentParent> cp; + MaybeInvalidTabContext tc(aContext); + if (!tc.IsValid()) { + NS_ERROR(nsPrintfCString("Received an invalid TabContext from " + "the child process. (%s)", + tc.GetInvalidReason()).get()); + return false; + } + + nsCOMPtr<mozIApplication> ownApp = tc.GetTabContext().GetOwnApp(); + if (ownApp) { + cp = GetNewOrPreallocatedAppProcess(ownApp, aPriority, this); + } + else { + cp = GetNewOrUsedBrowserProcess(/* isBrowserElement = */ true, + aPriority, this); + } + + if (!cp) { + *aCpId = 0; + *aIsForApp = false; + *aIsForBrowser = false; + return true; + } + + *aCpId = cp->ChildID(); + *aIsForApp = cp->IsForApp(); + *aIsForBrowser = cp->IsForBrowser(); + + ContentProcessManager *cpm = ContentProcessManager::GetSingleton(); + cpm->AddContentProcess(cp, this->ChildID()); + + if (cpm->AddGrandchildProcess(this->ChildID(), cp->ChildID())) { + // Pre-allocate a TabId here to save one time IPC call at app startup. + *aTabId = AllocateTabId(aOpenerTabId, aContext, cp->ChildID()); + return (*aTabId != 0); + } + + return false; +} + +bool +ContentParent::RecvBridgeToChildProcess(const ContentParentId& aCpId) +{ + ContentProcessManager *cpm = ContentProcessManager::GetSingleton(); + ContentParent* cp = cpm->GetContentProcessById(aCpId); + + if (cp) { + ContentParentId parentId; + if (cpm->GetParentProcessId(cp->ChildID(), &parentId) && + parentId == this->ChildID()) { + return NS_SUCCEEDED(PContentBridge::Bridge(this, cp)); + } + } + + // You can't bridge to a process you didn't open! + KillHard("BridgeToChildProcess"); + return false; +} + +static nsIDocShell* GetOpenerDocShellHelper(Element* aFrameElement) +{ + // Propagate the private-browsing status of the element's parent + // docshell to the remote docshell, via the chrome flags. + nsCOMPtr<Element> frameElement = do_QueryInterface(aFrameElement); + MOZ_ASSERT(frameElement); + nsPIDOMWindowOuter* win = frameElement->OwnerDoc()->GetWindow(); + if (!win) { + NS_WARNING("Remote frame has no window"); + return nullptr; + } + nsIDocShell* docShell = win->GetDocShell(); + if (!docShell) { + NS_WARNING("Remote frame has no docshell"); + return nullptr; + } + + return docShell; +} + +bool +ContentParent::RecvCreateGMPService() +{ + return PGMPService::Open(this); +} + +bool +ContentParent::RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv, uint32_t* aRunID) +{ + *aRv = NS_OK; + return mozilla::plugins::SetupBridge(aPluginId, this, false, aRv, aRunID); +} + +bool +ContentParent::RecvUngrabPointer(const uint32_t& aTime) +{ +#if !defined(MOZ_WIDGET_GTK) + NS_RUNTIMEABORT("This message only makes sense on GTK platforms"); + return false; +#else + gdk_pointer_ungrab(aTime); + return true; +#endif +} + +bool +ContentParent::RecvRemovePermission(const IPC::Principal& aPrincipal, + const nsCString& aPermissionType, + nsresult* aRv) { + *aRv = Permissions::RemovePermission(aPrincipal, aPermissionType.get()); + return true; +} + +bool +ContentParent::RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv) +{ + *aRv = NS_OK; + // We don't need to get the run ID for the plugin, since we already got it + // in the first call to SetupBridge in RecvLoadPlugin, so we pass in a dummy + // pointer and just throw it away. + uint32_t dummy = 0; + return mozilla::plugins::SetupBridge(aPluginId, this, true, aRv, &dummy); +} + +bool +ContentParent::RecvGetBlocklistState(const uint32_t& aPluginId, + uint32_t* aState) +{ + *aState = nsIBlocklistService::STATE_BLOCKED; + + RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst(); + if (!pluginHost) { + NS_WARNING("Plugin host not found"); + return false; + } + nsPluginTag* tag = pluginHost->PluginWithId(aPluginId); + + if (!tag) { + // Default state is blocked anyway + NS_WARNING("Plugin tag not found. This should never happen, but to avoid a crash we're forcibly blocking it"); + return true; + } + + return NS_SUCCEEDED(tag->GetBlocklistState(aState)); +} + +bool +ContentParent::RecvFindPlugins(const uint32_t& aPluginEpoch, + nsresult* aRv, + nsTArray<PluginTag>* aPlugins, + uint32_t* aNewPluginEpoch) +{ + *aRv = mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch); + return true; +} + +/*static*/ TabParent* +ContentParent::CreateBrowserOrApp(const TabContext& aContext, + Element* aFrameElement, + ContentParent* aOpenerContentParent, + bool aFreshProcess) +{ + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); + + if (!sCanLaunchSubprocesses) { + return nullptr; + } + + if (TabParent* parent = TabParent::GetNextTabParent()) { + parent->SetOwnerElement(aFrameElement); + return parent; + } + + ProcessPriority initialPriority = GetInitialProcessPriority(aFrameElement); + bool isInContentProcess = !XRE_IsParentProcess(); + TabId tabId; + + nsIDocShell* docShell = GetOpenerDocShellHelper(aFrameElement); + TabId openerTabId; + if (docShell) { + openerTabId = TabParent::GetTabIdFrom(docShell); + } + + if (aContext.IsMozBrowserElement() || !aContext.HasOwnApp()) { + RefPtr<nsIContentParent> constructorSender; + if (isInContentProcess) { + MOZ_ASSERT(aContext.IsMozBrowserElement()); + constructorSender = CreateContentBridgeParent(aContext, initialPriority, + openerTabId, &tabId); + } else { + if (aOpenerContentParent) { + constructorSender = aOpenerContentParent; + } else { + constructorSender = + GetNewOrUsedBrowserProcess(aContext.IsMozBrowserElement(), + initialPriority, + nullptr, + aFreshProcess); + if (!constructorSender) { + return nullptr; + } + } + tabId = AllocateTabId(openerTabId, + aContext.AsIPCTabContext(), + constructorSender->ChildID()); + } + if (constructorSender) { + nsCOMPtr<nsIDocShellTreeOwner> treeOwner; + docShell->GetTreeOwner(getter_AddRefs(treeOwner)); + if (!treeOwner) { + return nullptr; + } + + nsCOMPtr<nsIWebBrowserChrome> wbc = do_GetInterface(treeOwner); + if (!wbc) { + return nullptr; + } + uint32_t chromeFlags = 0; + wbc->GetChromeFlags(&chromeFlags); + + nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell); + if (loadContext && loadContext->UsePrivateBrowsing()) { + chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW; + } + if (docShell->GetAffectPrivateSessionLifetime()) { + chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME; + } + + if (tabId == 0) { + return nullptr; + } + RefPtr<TabParent> tp(new TabParent(constructorSender, tabId, + aContext, chromeFlags)); + tp->SetInitedByParent(); + + PBrowserParent* browser = + constructorSender->SendPBrowserConstructor( + // DeallocPBrowserParent() releases this ref. + tp.forget().take(), tabId, + aContext.AsIPCTabContext(), + chromeFlags, + constructorSender->ChildID(), + constructorSender->IsForApp(), + constructorSender->IsForBrowser()); + + if (aFreshProcess) { + Unused << browser->SendSetFreshProcess(); + } + + if (browser) { + RefPtr<TabParent> constructedTabParent = TabParent::GetFrom(browser); + constructedTabParent->SetOwnerElement(aFrameElement); + return constructedTabParent; + } + } + return nullptr; + } + + // If we got here, we have an app and we're not a browser element. ownApp + // shouldn't be null, because we otherwise would have gone into the + // !HasOwnApp() branch above. + RefPtr<nsIContentParent> parent; + bool reused = false; + bool tookPreallocated = false; + nsAutoString manifestURL; + + if (isInContentProcess) { + parent = CreateContentBridgeParent(aContext, + initialPriority, + openerTabId, + &tabId); + } + else { + nsCOMPtr<mozIApplication> ownApp = aContext.GetOwnApp(); + + if (!sAppContentParents) { + sAppContentParents = + new nsDataHashtable<nsStringHashKey, ContentParent*>(); + } + + // Each app gets its own ContentParent instance unless it shares it with + // a parent app. + if (NS_FAILED(ownApp->GetManifestURL(manifestURL))) { + NS_ERROR("Failed to get manifest URL"); + return nullptr; + } + + RefPtr<ContentParent> p = sAppContentParents->Get(manifestURL); + + if (!p && Preferences::GetBool("dom.ipc.reuse_parent_app")) { + nsAutoString parentAppManifestURL; + aFrameElement->GetAttr(kNameSpaceID_None, + nsGkAtoms::parentapp, parentAppManifestURL); + nsAdoptingString systemAppManifestURL = + Preferences::GetString("b2g.system_manifest_url"); + nsCOMPtr<nsIAppsService> appsService = + do_GetService(APPS_SERVICE_CONTRACTID); + if (!parentAppManifestURL.IsEmpty() && + !parentAppManifestURL.Equals(systemAppManifestURL) && + appsService) { + nsCOMPtr<mozIApplication> parentApp; + nsCOMPtr<mozIApplication> app; + appsService->GetAppByManifestURL(parentAppManifestURL, + getter_AddRefs(parentApp)); + appsService->GetAppByManifestURL(manifestURL, + getter_AddRefs(app)); + + // Only let certified apps re-use the same process. + unsigned short parentAppStatus = 0; + unsigned short appStatus = 0; + if (app && + NS_SUCCEEDED(app->GetAppStatus(&appStatus)) && + appStatus == nsIPrincipal::APP_STATUS_CERTIFIED && + parentApp && + NS_SUCCEEDED(parentApp->GetAppStatus(&parentAppStatus)) && + parentAppStatus == nsIPrincipal::APP_STATUS_CERTIFIED) { + // Check if we can re-use the process of the parent app. + p = sAppContentParents->Get(parentAppManifestURL); + } + } + } + + if (p) { + // Check that the process is still alive and set its priority. + // Hopefully the process won't die after this point, if this call + // succeeds. + if (!p->SetPriorityAndCheckIsAlive(initialPriority)) { + p = nullptr; + } + } + + reused = !!p; + if (!p) { + p = GetNewOrPreallocatedAppProcess(ownApp, initialPriority, nullptr, + &tookPreallocated); + MOZ_ASSERT(p); + sAppContentParents->Put(manifestURL, p); + } + tabId = AllocateTabId(openerTabId, aContext.AsIPCTabContext(), + p->ChildID()); + parent = static_cast<nsIContentParent*>(p); + } + + if (!parent || (tabId == 0)) { + return nullptr; + } + + uint32_t chromeFlags = 0; + + RefPtr<TabParent> tp = new TabParent(parent, tabId, aContext, chromeFlags); + tp->SetInitedByParent(); + PBrowserParent* browser = parent->SendPBrowserConstructor( + // DeallocPBrowserParent() releases this ref. + RefPtr<TabParent>(tp).forget().take(), + tabId, + aContext.AsIPCTabContext(), + chromeFlags, + parent->ChildID(), + parent->IsForApp(), + parent->IsForBrowser()); + + if (aFreshProcess) { + Unused << browser->SendSetFreshProcess(); + } + + if (browser) { + RefPtr<TabParent> constructedTabParent = TabParent::GetFrom(browser); + constructedTabParent->SetOwnerElement(aFrameElement); + } + + if (isInContentProcess) { + // Just return directly without the following check in content process. + return TabParent::GetFrom(browser); + } + + if (!browser) { + // We failed to actually start the PBrowser. This can happen if the + // other process has already died. + if (!reused) { + // Don't leave a broken ContentParent in the hashtable. + parent->AsContentParent()->KillHard("CreateBrowserOrApp"); + sAppContentParents->Remove(manifestURL); + parent = nullptr; + } + + // If we took the preallocated process and it was already dead, try + // again with a non-preallocated process. We can be sure this won't + // loop forever, because the next time through there will be no + // preallocated process to take. + if (tookPreallocated) { + return ContentParent::CreateBrowserOrApp(aContext, aFrameElement, + aOpenerContentParent); + } + + // Otherwise just give up. + return nullptr; + } + + return TabParent::GetFrom(browser); +} + +/*static*/ ContentBridgeParent* +ContentParent::CreateContentBridgeParent(const TabContext& aContext, + const hal::ProcessPriority& aPriority, + const TabId& aOpenerTabId, + /*out*/ TabId* aTabId) +{ + MOZ_ASSERT(aTabId); + + ContentChild* child = ContentChild::GetSingleton(); + ContentParentId cpId; + bool isForApp; + bool isForBrowser; + if (!child->SendCreateChildProcess(aContext.AsIPCTabContext(), + aPriority, + aOpenerTabId, + &cpId, + &isForApp, + &isForBrowser, + aTabId)) { + return nullptr; + } + if (cpId == 0) { + return nullptr; + } + if (!child->SendBridgeToChildProcess(cpId)) { + return nullptr; + } + ContentBridgeParent* parent = child->GetLastBridge(); + parent->SetChildID(cpId); + parent->SetIsForApp(isForApp); + parent->SetIsForBrowser(isForBrowser); + return parent; +} + +void +ContentParent::GetAll(nsTArray<ContentParent*>& aArray) +{ + aArray.Clear(); + + for (auto* cp : AllProcesses(eLive)) { + aArray.AppendElement(cp); + } +} + +void +ContentParent::GetAllEvenIfDead(nsTArray<ContentParent*>& aArray) +{ + aArray.Clear(); + + for (auto* cp : AllProcesses(eAll)) { + aArray.AppendElement(cp); + } +} + +void +ContentParent::Init() +{ + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + size_t length = ArrayLength(sObserverTopics); + for (size_t i = 0; i < length; ++i) { + obs->AddObserver(this, sObserverTopics[i], false); + } + } + Preferences::AddStrongObserver(this, ""); + if (obs) { + nsAutoString cpId; + cpId.AppendInt(static_cast<uint64_t>(this->ChildID())); + obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-created", cpId.get()); + } + +#ifdef ACCESSIBILITY + // If accessibility is running in chrome process then start it in content + // process. + if (nsIPresShell::IsAccessibilityActive()) { +#if !defined(XP_WIN) + Unused << SendActivateA11y(0); +#else + // On Windows we currently only enable a11y in the content process + // for testing purposes. + if (Preferences::GetBool(kForceEnableE10sPref, false)) { + Unused << SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID())); + } +#endif + } +#endif + +#ifdef MOZ_ENABLE_PROFILER_SPS + nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1")); + bool profilerActive = false; + DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + if (profilerActive) { + nsCOMPtr<nsIProfilerStartParams> currentProfilerParams; + rv = profiler->GetStartParams(getter_AddRefs(currentProfilerParams)); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + nsCOMPtr<nsISupports> gatherer; + rv = profiler->GetProfileGatherer(getter_AddRefs(gatherer)); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + mGatherer = static_cast<ProfileGatherer*>(gatherer.get()); + + StartProfiler(currentProfilerParams); + } +#endif + + RefPtr<GeckoMediaPluginServiceParent> gmps(GeckoMediaPluginServiceParent::GetSingleton()); + gmps->UpdateContentProcessGMPCapabilities(); +} + +void +ContentParent::ForwardKnownInfo() +{ + MOZ_ASSERT(mMetamorphosed); + if (!mMetamorphosed) { + return; + } +#ifdef MOZ_WIDGET_GONK + InfallibleTArray<VolumeInfo> volumeInfo; + RefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton(); + if (vs) { + vs->GetVolumesForIPC(&volumeInfo); + Unused << SendVolumes(volumeInfo); + } +#endif /* MOZ_WIDGET_GONK */ +} + +namespace { + +class RemoteWindowContext final : public nsIRemoteWindowContext + , public nsIInterfaceRequestor +{ +public: + explicit RemoteWindowContext(TabParent* aTabParent) + : mTabParent(aTabParent) + { + } + + NS_DECL_ISUPPORTS + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIREMOTEWINDOWCONTEXT + +private: + ~RemoteWindowContext(); + RefPtr<TabParent> mTabParent; +}; + +NS_IMPL_ISUPPORTS(RemoteWindowContext, nsIRemoteWindowContext, nsIInterfaceRequestor) + +RemoteWindowContext::~RemoteWindowContext() +{ +} + +NS_IMETHODIMP +RemoteWindowContext::GetInterface(const nsIID& aIID, void** aSink) +{ + return QueryInterface(aIID, aSink); +} + +NS_IMETHODIMP +RemoteWindowContext::OpenURI(nsIURI* aURI) +{ + mTabParent->LoadURL(aURI); + return NS_OK; +} + +} // namespace + +bool +ContentParent::SetPriorityAndCheckIsAlive(ProcessPriority aPriority) +{ + ProcessPriorityManager::SetProcessPriority(this, aPriority); + + // Now that we've set this process's priority, check whether the process is + // still alive. Hopefully we've set the priority to FOREGROUND*, so the + // process won't unexpectedly crash after this point! + // + // Bug 943174: use waitid() with WNOWAIT so that, if the process + // did exit, we won't consume its zombie and confuse the + // GeckoChildProcessHost dtor. +#ifdef MOZ_WIDGET_GONK + siginfo_t info; + info.si_pid = 0; + if (waitid(P_PID, Pid(), &info, WNOWAIT | WNOHANG | WEXITED) == 0 + && info.si_pid != 0) { + return false; + } +#endif + + return true; +} + +// Helper for ContentParent::TransformPreallocatedIntoApp. +static void +TryGetNameFromManifestURL(const nsAString& aManifestURL, + nsAString& aName) +{ + aName.Truncate(); + if (aManifestURL.IsEmpty() || + aManifestURL == MAGIC_PREALLOCATED_APP_MANIFEST_URL) { + return; + } + + nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID); + NS_ENSURE_TRUE_VOID(appsService); + + nsCOMPtr<mozIApplication> app; + appsService->GetAppByManifestURL(aManifestURL, getter_AddRefs(app)); + + if (!app) { + return; + } + + app->GetName(aName); +} + +void +ContentParent::TransformPreallocatedIntoApp(ContentParent* aOpener, + const nsAString& aAppManifestURL) +{ + MOZ_ASSERT(IsPreallocated()); + mMetamorphosed = true; + mOpener = aOpener; + mAppManifestURL = aAppManifestURL; + TryGetNameFromManifestURL(aAppManifestURL, mAppName); +} + +void +ContentParent::TransformPreallocatedIntoBrowser(ContentParent* aOpener) +{ + // Reset mAppManifestURL, mIsForBrowser and mOSPrivileges for browser. + mMetamorphosed = true; + mOpener = aOpener; + mAppManifestURL.Truncate(); + mIsForBrowser = true; +} + +void +ContentParent::ShutDownProcess(ShutDownMethod aMethod) +{ + // Shutting down by sending a shutdown message works differently than the + // other methods. We first call Shutdown() in the child. After the child is + // ready, it calls FinishShutdown() on us. Then we close the channel. + if (aMethod == SEND_SHUTDOWN_MESSAGE) { + if (mIPCOpen && !mShutdownPending && SendShutdown()) { + mShutdownPending = true; + // Start the force-kill timer if we haven't already. + StartForceKillTimer(); + } + + // If call was not successful, the channel must have been broken + // somehow, and we will clean up the error in ActorDestroy. + return; + } + + using mozilla::dom::quota::QuotaManagerService; + + if (QuotaManagerService* quotaManagerService = QuotaManagerService::Get()) { + quotaManagerService->AbortOperationsForProcess(mChildID); + } + + // If Close() fails with an error, we'll end up back in this function, but + // with aMethod = CLOSE_CHANNEL_WITH_ERROR. + + if (aMethod == CLOSE_CHANNEL && !mCalledClose) { + // Close() can only be called once: It kicks off the destruction + // sequence. + mCalledClose = true; + Close(); + } + + const ManagedContainer<POfflineCacheUpdateParent>& ocuParents = + ManagedPOfflineCacheUpdateParent(); + for (auto iter = ocuParents.ConstIter(); !iter.Done(); iter.Next()) { + RefPtr<mozilla::docshell::OfflineCacheUpdateParent> ocuParent = + static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(iter.Get()->GetKey()); + ocuParent->StopSendingMessagesToChild(); + } + + // NB: must MarkAsDead() here so that this isn't accidentally + // returned from Get*() while in the midst of shutdown. + MarkAsDead(); + + // A ContentParent object might not get freed until after XPCOM shutdown has + // shut down the cycle collector. But by then it's too late to release any + // CC'ed objects, so we need to null them out here, while we still can. See + // bug 899761. + ShutDownMessageManager(); +} + +bool +ContentParent::RecvFinishShutdown() +{ + // At this point, we already called ShutDownProcess once with + // SEND_SHUTDOWN_MESSAGE. To actually close the channel, we call + // ShutDownProcess again with CLOSE_CHANNEL. + MOZ_ASSERT(mShutdownPending); + ShutDownProcess(CLOSE_CHANNEL); + return true; +} + +void +ContentParent::ShutDownMessageManager() +{ + if (!mMessageManager) { + return; + } + + mMessageManager->ReceiveMessage( + static_cast<nsIContentFrameMessageManager*>(mMessageManager.get()), nullptr, + CHILD_PROCESS_SHUTDOWN_MESSAGE, false, + nullptr, nullptr, nullptr, nullptr); + + mMessageManager->Disconnect(); + mMessageManager = nullptr; +} + +void +ContentParent::MarkAsDead() +{ + if (!mAppManifestURL.IsEmpty()) { + if (sAppContentParents) { + sAppContentParents->Remove(mAppManifestURL); + if (!sAppContentParents->Count()) { + delete sAppContentParents; + sAppContentParents = nullptr; + } + } + } else { + if (sNonAppContentParents) { + sNonAppContentParents->RemoveElement(this); + if (!sNonAppContentParents->Length()) { + delete sNonAppContentParents; + sNonAppContentParents = nullptr; + } + } + + if (sLargeAllocationContentParents) { + sLargeAllocationContentParents->RemoveElement(this); + if (!sLargeAllocationContentParents->Length()) { + delete sLargeAllocationContentParents; + sLargeAllocationContentParents = nullptr; + } + } + } + + if (sPrivateContent) { + sPrivateContent->RemoveElement(this); + if (!sPrivateContent->Length()) { + delete sPrivateContent; + sPrivateContent = nullptr; + } + } + + mIsAlive = false; +} + +void +ContentParent::OnChannelError() +{ + RefPtr<ContentParent> content(this); + PContentParent::OnChannelError(); +} + +void +ContentParent::OnChannelConnected(int32_t pid) +{ + SetOtherProcessId(pid); + +#if defined(ANDROID) || defined(LINUX) + // Check nice preference + int32_t nice = Preferences::GetInt("dom.ipc.content.nice", 0); + + // Environment variable overrides preference + char* relativeNicenessStr = getenv("MOZ_CHILD_PROCESS_RELATIVE_NICENESS"); + if (relativeNicenessStr) { + nice = atoi(relativeNicenessStr); + } + + /* make the GUI thread have higher priority on single-cpu devices */ + nsCOMPtr<nsIPropertyBag2> infoService = do_GetService(NS_SYSTEMINFO_CONTRACTID); + if (infoService) { + int32_t cpus; + nsresult rv = infoService->GetPropertyAsInt32(NS_LITERAL_STRING("cpucount"), &cpus); + if (NS_FAILED(rv)) { + cpus = 1; + } + if (nice != 0 && cpus == 1) { + setpriority(PRIO_PROCESS, pid, getpriority(PRIO_PROCESS, pid) + nice); + } + } +#endif +} + +void +ContentParent::ProcessingError(Result aCode, const char* aReason) +{ + if (MsgDropped == aCode) { + return; + } + // Other errors are big deals. + KillHard(aReason); +} + +/* static */ +bool +ContentParent::AllocateLayerTreeId(TabParent* aTabParent, uint64_t* aId) +{ + return AllocateLayerTreeId(aTabParent->Manager()->AsContentParent(), + aTabParent, aTabParent->GetTabId(), aId); +} + +/* static */ +bool +ContentParent::AllocateLayerTreeId(ContentParent* aContent, + TabParent* aTopLevel, const TabId& aTabId, + uint64_t* aId) +{ + GPUProcessManager* gpu = GPUProcessManager::Get(); + + *aId = gpu->AllocateLayerTreeId(); + + if (!aContent || !aTopLevel) { + return false; + } + + gpu->MapLayerTreeId(*aId, aContent->OtherPid()); + + if (!gfxPlatform::AsyncPanZoomEnabled()) { + return true; + } + + return aContent->SendNotifyLayerAllocated(aTabId, *aId); +} + +bool +ContentParent::RecvAllocateLayerTreeId(const ContentParentId& aCpId, + const TabId& aTabId, uint64_t* aId) +{ + // Protect against spoofing by a compromised child. aCpId must either + // correspond to the process that this ContentParent represents or be a + // child of it. + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + if (ChildID() != aCpId) { + ContentParentId parent; + if (!cpm->GetParentProcessId(aCpId, &parent) || + ChildID() != parent) { + return false; + } + } + + // GetTopLevelTabParentByProcessAndTabId will make sure that aTabId + // lives in the process for aCpId. + RefPtr<ContentParent> contentParent = cpm->GetContentProcessById(aCpId); + RefPtr<TabParent> browserParent = + cpm->GetTopLevelTabParentByProcessAndTabId(aCpId, aTabId); + MOZ_ASSERT(contentParent && browserParent); + + return AllocateLayerTreeId(contentParent, browserParent, aTabId, aId); +} + +bool +ContentParent::RecvDeallocateLayerTreeId(const uint64_t& aId) +{ + GPUProcessManager* gpu = GPUProcessManager::Get(); + + if (!gpu->IsLayerTreeIdMapped(aId, OtherPid())) + { + // You can't deallocate layer tree ids that you didn't allocate + KillHard("DeallocateLayerTreeId"); + } + + gpu->UnmapLayerTreeId(aId, OtherPid()); + + return true; +} + +namespace { + +void +DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess) +{ + RefPtr<DeleteTask<GeckoChildProcessHost>> task = new DeleteTask<GeckoChildProcessHost>(aSubprocess); + XRE_GetIOMessageLoop()->PostTask(task.forget()); +} + +// This runnable only exists to delegate ownership of the +// ContentParent to this runnable, until it's deleted by the event +// system. +struct DelayedDeleteContentParentTask : public Runnable +{ + explicit DelayedDeleteContentParentTask(ContentParent* aObj) : mObj(aObj) { } + + // No-op + NS_IMETHOD Run() override { return NS_OK; } + + RefPtr<ContentParent> mObj; +}; + +} // namespace + +void +ContentParent::ActorDestroy(ActorDestroyReason why) +{ + if (mForceKillTimer) { + mForceKillTimer->Cancel(); + mForceKillTimer = nullptr; + } + + // Signal shutdown completion regardless of error state, so we can + // finish waiting in the xpcom-shutdown/profile-before-change observer. + mIPCOpen = false; + + if (mHangMonitorActor) { + ProcessHangMonitor::RemoveProcess(mHangMonitorActor); + mHangMonitorActor = nullptr; + } + + RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get(); + if (fss) { + fss->Forget(ChildID()); + } + + if (why == NormalShutdown && !mCalledClose) { + // If we shut down normally but haven't called Close, assume somebody + // else called Close on us. In that case, we still need to call + // ShutDownProcess below to perform other necessary clean up. + mCalledClose = true; + } + + // Make sure we always clean up. + ShutDownProcess(why == NormalShutdown ? CLOSE_CHANNEL + : CLOSE_CHANNEL_WITH_ERROR); + + RefPtr<ContentParent> kungFuDeathGrip(this); + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + size_t length = ArrayLength(sObserverTopics); + for (size_t i = 0; i < length; ++i) { + obs->RemoveObserver(static_cast<nsIObserver*>(this), + sObserverTopics[i]); + } + } + + // remove the global remote preferences observers + Preferences::RemoveObserver(this, ""); + gfxVars::RemoveReceiver(this); + + if (GPUProcessManager* gpu = GPUProcessManager::Get()) { + // Note: the manager could have shutdown already. + gpu->RemoveListener(this); + } + + RecvRemoveGeolocationListener(); + + mConsoleService = nullptr; + +#ifdef MOZ_ENABLE_PROFILER_SPS + if (mGatherer && !mProfile.IsEmpty()) { + mGatherer->OOPExitProfile(mProfile); + } +#endif + + if (obs) { + RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag(); + + props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), mChildID); + + if (AbnormalShutdown == why) { + Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT, + NS_LITERAL_CSTRING("content"), 1); + + props->SetPropertyAsBool(NS_LITERAL_STRING("abnormal"), true); + +#ifdef MOZ_CRASHREPORTER + // There's a window in which child processes can crash + // after IPC is established, but before a crash reporter + // is created. + if (PCrashReporterParent* p = LoneManagedOrNullAsserts(ManagedPCrashReporterParent())) { + CrashReporterParent* crashReporter = + static_cast<CrashReporterParent*>(p); + + // If we're an app process, always stomp the latest URI + // loaded in the child process with our manifest URL. We + // would rather associate the crashes with apps than + // random child windows loaded in them. + // + // XXX would be nice if we could get both ... + if (!mAppManifestURL.IsEmpty()) { + crashReporter->AnnotateCrashReport(NS_LITERAL_CSTRING("URL"), + NS_ConvertUTF16toUTF8(mAppManifestURL)); + } + + // if mCreatedPairedMinidumps is true, we've already generated + // parent/child dumps for dekstop crashes. + if (!mCreatedPairedMinidumps) { + crashReporter->GenerateCrashReport(this, nullptr); + } + + nsAutoString dumpID(crashReporter->ChildDumpID()); + props->SetPropertyAsAString(NS_LITERAL_STRING("dumpID"), dumpID); + } +#endif + } + nsAutoString cpId; + cpId.AppendInt(static_cast<uint64_t>(this->ChildID())); + obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", cpId.get()); + } + + // Remove any and all idle listeners. + nsCOMPtr<nsIIdleService> idleService = + do_GetService("@mozilla.org/widget/idleservice;1"); + MOZ_ASSERT(idleService); + RefPtr<ParentIdleListener> listener; + for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) { + listener = static_cast<ParentIdleListener*>(mIdleListeners[i].get()); + idleService->RemoveIdleObserver(listener, listener->mTime); + } + mIdleListeners.Clear(); + + MessageLoop::current()-> + PostTask(NewRunnableFunction(DelayedDeleteSubprocess, mSubprocess)); + mSubprocess = nullptr; + + // IPDL rules require actors to live on past ActorDestroy, but it + // may be that the kungFuDeathGrip above is the last reference to + // |this|. If so, when we go out of scope here, we're deleted and + // all hell breaks loose. + // + // This runnable ensures that a reference to |this| lives on at + // least until after the current task finishes running. + NS_DispatchToCurrentThread(new DelayedDeleteContentParentTask(this)); + + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + nsTArray<ContentParentId> childIDArray = + cpm->GetAllChildProcessById(this->ChildID()); + + // Destroy any processes created by this ContentParent + for(uint32_t i = 0; i < childIDArray.Length(); i++) { + ContentParent* cp = cpm->GetContentProcessById(childIDArray[i]); + MessageLoop::current()->PostTask(NewRunnableMethod + <ShutDownMethod>(cp, + &ContentParent::ShutDownProcess, + SEND_SHUTDOWN_MESSAGE)); + } + cpm->RemoveContentProcess(this->ChildID()); + + if (mDriverCrashGuard) { + mDriverCrashGuard->NotifyCrashed(); + } + + // Unregister all the BlobURLs registered by the ContentChild. + for (uint32_t i = 0; i < mBlobURLs.Length(); ++i) { + nsHostObjectProtocolHandler::RemoveDataEntry(mBlobURLs[i]); + } + + mBlobURLs.Clear(); + +#if defined(XP_WIN32) && defined(ACCESSIBILITY) + a11y::AccessibleWrap::ReleaseContentProcessIdFor(ChildID()); +#endif +} + +void +ContentParent::NotifyTabDestroying(const TabId& aTabId, + const ContentParentId& aCpId) +{ + if (XRE_IsParentProcess()) { + // There can be more than one PBrowser for a given app process + // because of popup windows. PBrowsers can also destroy + // concurrently. When all the PBrowsers are destroying, kick off + // another task to ensure the child process *really* shuts down, + // even if the PBrowsers themselves never finish destroying. + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + ContentParent* cp = cpm->GetContentProcessById(aCpId); + if (!cp) { + return; + } + ++cp->mNumDestroyingTabs; + nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(aCpId); + if (static_cast<size_t>(cp->mNumDestroyingTabs) != tabIds.Length()) { + return; + } + + uint32_t numberOfParents = sNonAppContentParents ? sNonAppContentParents->Length() : 0; + int32_t processesToKeepAlive = Preferences::GetInt("dom.ipc.keepProcessesAlive", 0); + if (!cp->mLargeAllocationProcess && static_cast<int32_t>(numberOfParents) <= processesToKeepAlive) { + return; + } + + // We're dying now, so prevent this content process from being + // recycled during its shutdown procedure. + cp->MarkAsDead(); + cp->StartForceKillTimer(); + } else { + ContentChild::GetSingleton()->SendNotifyTabDestroying(aTabId, aCpId); + } +} + +void +ContentParent::StartForceKillTimer() +{ + if (mForceKillTimer || !mIPCOpen) { + 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(ContentParent::ForceKillTimerCallback, + this, + timeoutSecs * 1000, + nsITimer::TYPE_ONE_SHOT); + } +} + +void +ContentParent::NotifyTabDestroyed(const TabId& aTabId, + bool aNotifiedDestroying) +{ + if (aNotifiedDestroying) { + --mNumDestroyingTabs; + } + + nsTArray<PContentPermissionRequestParent*> parentArray = + nsContentPermissionUtils::GetContentPermissionRequestParentById(aTabId); + + // Need to close undeleted ContentPermissionRequestParents before tab is closed. + for (auto& permissionRequestParent : parentArray) { + Unused << PContentPermissionRequestParent::Send__delete__(permissionRequestParent); + } + + // There can be more than one PBrowser for a given app process + // because of popup windows. When the last one closes, shut + // us down. + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(this->ChildID()); + + // We might want to keep alive some content processes for testing, because of performance + // reasons, but we don't want to alter behavior if the pref is not set. + uint32_t numberOfParents = sNonAppContentParents ? sNonAppContentParents->Length() : 0; + int32_t processesToKeepAlive = Preferences::GetInt("dom.ipc.keepProcessesAlive", 0); + bool shouldKeepAliveAny = !mLargeAllocationProcess && processesToKeepAlive > 0; + bool shouldKeepAliveThis = shouldKeepAliveAny && static_cast<int32_t>(numberOfParents) <= processesToKeepAlive; + + if (tabIds.Length() == 1 && !shouldKeepAliveThis) { + // In the case of normal shutdown, send a shutdown message to child to + // allow it to perform shutdown tasks. + MessageLoop::current()->PostTask(NewRunnableMethod + <ShutDownMethod>(this, + &ContentParent::ShutDownProcess, + SEND_SHUTDOWN_MESSAGE)); + } +} + +jsipc::CPOWManager* +ContentParent::GetCPOWManager() +{ + if (PJavaScriptParent* p = LoneManagedOrNullAsserts(ManagedPJavaScriptParent())) { + return CPOWManagerFor(p); + } + return nullptr; +} + +TestShellParent* +ContentParent::CreateTestShell() +{ + return static_cast<TestShellParent*>(SendPTestShellConstructor()); +} + +bool +ContentParent::DestroyTestShell(TestShellParent* aTestShell) +{ + return PTestShellParent::Send__delete__(aTestShell); +} + +TestShellParent* +ContentParent::GetTestShellSingleton() +{ + PTestShellParent* p = LoneManagedOrNullAsserts(ManagedPTestShellParent()); + return static_cast<TestShellParent*>(p); +} + +void +ContentParent::InitializeMembers() +{ + mSubprocess = nullptr; + mChildID = gContentChildID++; + mGeolocationWatchID = -1; + mNumDestroyingTabs = 0; + mIsAlive = true; + mMetamorphosed = false; + mSendPermissionUpdates = false; + mCalledClose = false; + mCalledKillHard = false; + mCreatedPairedMinidumps = false; + mShutdownPending = false; + mIPCOpen = true; + mHangMonitorActor = nullptr; +} + +bool +ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */) +{ + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); + + std::vector<std::string> extraArgs; + + if (gSafeMode) { + extraArgs.push_back("-safeMode"); + } + + if (!mSubprocess->LaunchAndWaitForProcessHandle(extraArgs)) { + MarkAsDead(); + return false; + } + + Open(mSubprocess->GetChannel(), + base::GetProcId(mSubprocess->GetChildProcessHandle())); + + InitInternal(aInitialPriority, + true, /* Setup off-main thread compositing */ + true /* Send registered chrome */); + + ContentProcessManager::GetSingleton()->AddContentProcess(this); + + ProcessHangMonitor::AddProcess(this); + + // Set a reply timeout for CPOWs. + SetReplyTimeoutMs(Preferences::GetInt("dom.ipc.cpow.timeout", 0)); + + return true; +} + +ContentParent::ContentParent(mozIApplication* aApp, + ContentParent* aOpener, + bool aIsForBrowser, + bool aIsForPreallocated) + : nsIContentParent() + , mOpener(aOpener) + , mIsForBrowser(aIsForBrowser) + , mLargeAllocationProcess(false) +{ + InitializeMembers(); // Perform common initialization. + + // No more than one of !!aApp, aIsForBrowser, aIsForPreallocated should be + // true. + MOZ_ASSERT(!!aApp + aIsForBrowser + aIsForPreallocated <= 1); + + mMetamorphosed = true; + + // Insert ourselves into the global linked list of ContentParent objects. + if (!sContentParents) { + sContentParents = new LinkedList<ContentParent>(); + } + sContentParents->insertBack(this); + + if (aApp) { + aApp->GetManifestURL(mAppManifestURL); + aApp->GetName(mAppName); + } else if (aIsForPreallocated) { + mAppManifestURL = MAGIC_PREALLOCATED_APP_MANIFEST_URL; + } + + // From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the + // PID along with the warning. + nsDebugImpl::SetMultiprocessMode("Parent"); + +#if defined(XP_WIN) && !defined(MOZ_B2G) + // Request Windows message deferral behavior on our side of the PContent + // channel. Generally only applies to the situation where we get caught in + // a deadlock with the plugin process when sending CPOWs. + GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION); +#endif + + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + ChildPrivileges privs = base::PRIVILEGES_DEFAULT; + mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, privs); +} + +ContentParent::~ContentParent() +{ + if (mForceKillTimer) { + mForceKillTimer->Cancel(); + } + + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + // We should be removed from all these lists in ActorDestroy. + MOZ_ASSERT(!sPrivateContent || !sPrivateContent->Contains(this)); + if (mAppManifestURL.IsEmpty()) { + MOZ_ASSERT((!sNonAppContentParents || + !sNonAppContentParents->Contains(this)) && + (!sLargeAllocationContentParents || + !sLargeAllocationContentParents->Contains(this))); + } else { + // In general, we expect sAppContentParents->Get(mAppManifestURL) to be + // nullptr. But it could be that we created another ContentParent for + // this app after we did this->ActorDestroy(), so the right check is + // that sAppContentParents->Get(mAppManifestURL) != this. + MOZ_ASSERT(!sAppContentParents || + sAppContentParents->Get(mAppManifestURL) != this); + } +} + +void +ContentParent::InitInternal(ProcessPriority aInitialPriority, + bool aSetupOffMainThreadCompositing, + bool aSendRegisteredChrome) +{ + if (aSendRegisteredChrome) { + nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService(); + nsChromeRegistryChrome* chromeRegistry = + static_cast<nsChromeRegistryChrome*>(registrySvc.get()); + chromeRegistry->SendRegisteredChrome(this); + } + + if (gAppData) { + nsCString version(gAppData->version); + nsCString buildID(gAppData->buildID); + nsCString name(gAppData->name); + nsCString UAName(gAppData->UAName); + nsCString ID(gAppData->ID); + nsCString vendor(gAppData->vendor); + + // Sending all information to content process. + Unused << SendAppInfo(version, buildID, name, UAName, ID, vendor); + } + + // Initialize the message manager (and load delayed scripts) now that we + // have established communications with the child. + mMessageManager->InitWithCallback(this); + + // Set the subprocess's priority. We do this early on because we're likely + // /lowering/ the process's CPU and memory priority, which it has inherited + // from this process. + // + // This call can cause us to send IPC messages to the child process, so it + // must come after the Open() call above. + ProcessPriorityManager::SetProcessPriority(this, aInitialPriority); + + if (aSetupOffMainThreadCompositing) { + // NB: internally, this will send an IPC message to the child + // process to get it to create the CompositorBridgeChild. This + // message goes through the regular IPC queue for this + // channel, so delivery will happen-before any other messages + // we send. The CompositorBridgeChild must be created before any + // PBrowsers are created, because they rely on the Compositor + // already being around. (Creation is async, so can't happen + // on demand.) + bool useOffMainThreadCompositing = !!CompositorThreadHolder::Loop(); + if (useOffMainThreadCompositing) { + GPUProcessManager* gpm = GPUProcessManager::Get(); + + Endpoint<PCompositorBridgeChild> compositor; + Endpoint<PImageBridgeChild> imageBridge; + Endpoint<PVRManagerChild> vrBridge; + Endpoint<PVideoDecoderManagerChild> videoManager; + + DebugOnly<bool> opened = gpm->CreateContentBridges( + OtherPid(), + &compositor, + &imageBridge, + &vrBridge, + &videoManager); + MOZ_ASSERT(opened); + + Unused << SendInitRendering( + Move(compositor), + Move(imageBridge), + Move(vrBridge), + Move(videoManager)); + + gpm->AddListener(this); + } + } + + if (gAppData) { + // Sending all information to content process. + Unused << SendAppInit(); + } + + nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance(); + if (sheetService) { + // This looks like a lot of work, but in a normal browser session we just + // send two loads. + + for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) { + URIParams uri; + SerializeURI(sheet->GetSheetURI(), uri); + Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AGENT_SHEET); + } + + for (StyleSheet* sheet : *sheetService->UserStyleSheets()) { + URIParams uri; + SerializeURI(sheet->GetSheetURI(), uri); + Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::USER_SHEET); + } + + for (StyleSheet* sheet : *sheetService->AuthorStyleSheets()) { + URIParams uri; + SerializeURI(sheet->GetSheetURI(), uri); + Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AUTHOR_SHEET); + } + } + +#ifdef MOZ_CONTENT_SANDBOX + bool shouldSandbox = true; + MaybeFileDesc brokerFd = void_t(); +#ifdef XP_LINUX + // XXX: Checking the pref here makes it possible to enable/disable sandboxing + // during an active session. Currently the pref is only used for testing + // purpose. If the decision is made to permanently rely on the pref, this + // should be changed so that it is required to restart firefox for the change + // of value to take effect. + shouldSandbox = (Preferences::GetInt("security.sandbox.content.level") > 0) && + !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX"); + + if (shouldSandbox) { + MOZ_ASSERT(!mSandboxBroker); + UniquePtr<SandboxBroker::Policy> policy = + sSandboxBrokerPolicyFactory->GetContentPolicy(Pid()); + if (policy) { + brokerFd = FileDescriptor(); + mSandboxBroker = SandboxBroker::Create(Move(policy), Pid(), brokerFd); + if (!mSandboxBroker) { + KillHard("SandboxBroker::Create failed"); + return; + } + MOZ_ASSERT(static_cast<const FileDescriptor&>(brokerFd).IsValid()); + } + } +#endif + if (shouldSandbox && !SendSetProcessSandbox(brokerFd)) { + KillHard("SandboxInitFailed"); + } +#endif +#if defined(XP_WIN) + // Send the info needed to join the browser process's audio session. + nsID id; + nsString sessionName; + nsString iconPath; + if (NS_SUCCEEDED(mozilla::widget::GetAudioSessionData(id, sessionName, + iconPath))) { + Unused << SendSetAudioSessionData(id, sessionName, iconPath); + } +#endif + + { + RefPtr<ServiceWorkerRegistrar> swr = ServiceWorkerRegistrar::Get(); + MOZ_ASSERT(swr); + + nsTArray<ServiceWorkerRegistrationData> registrations; + swr->GetRegistrations(registrations); + Unused << SendInitServiceWorkers(ServiceWorkerConfiguration(registrations)); + } + + { + nsTArray<BlobURLRegistrationData> registrations; + if (nsHostObjectProtocolHandler::GetAllBlobURLEntries(registrations, + this)) { + Unused << SendInitBlobURLs(registrations); + } + } +} + +bool +ContentParent::IsAlive() const +{ + return mIsAlive; +} + +bool +ContentParent::IsForApp() const +{ + return !mAppManifestURL.IsEmpty(); +} + +int32_t +ContentParent::Pid() const +{ + if (!mSubprocess || !mSubprocess->GetChildProcessHandle()) { + return -1; + } + return base::GetProcId(mSubprocess->GetChildProcessHandle()); +} + +bool +ContentParent::RecvReadPrefsArray(InfallibleTArray<PrefSetting>* aPrefs) +{ + Preferences::GetPreferences(aPrefs); + return true; +} + +bool +ContentParent::RecvGetGfxVars(InfallibleTArray<GfxVarUpdate>* aVars) +{ + // Ensure gfxVars is initialized (for xpcshell tests). + gfxVars::Initialize(); + + *aVars = gfxVars::FetchNonDefaultVars(); + + // Now that content has initialized gfxVars, we can start listening for + // updates. + gfxVars::AddReceiver(this); + return true; +} + +void +ContentParent::OnCompositorUnexpectedShutdown() +{ + GPUProcessManager* gpm = GPUProcessManager::Get(); + + Endpoint<PCompositorBridgeChild> compositor; + Endpoint<PImageBridgeChild> imageBridge; + Endpoint<PVRManagerChild> vrBridge; + Endpoint<PVideoDecoderManagerChild> videoManager; + + DebugOnly<bool> opened = gpm->CreateContentBridges( + OtherPid(), + &compositor, + &imageBridge, + &vrBridge, + &videoManager); + MOZ_ASSERT(opened); + + Unused << SendReinitRendering( + Move(compositor), + Move(imageBridge), + Move(vrBridge), + Move(videoManager)); +} + +void +ContentParent::OnVarChanged(const GfxVarUpdate& aVar) +{ + if (!mIPCOpen) { + return; + } + Unused << SendVarUpdate(aVar); +} + +bool +ContentParent::RecvReadFontList(InfallibleTArray<FontListEntry>* retValue) +{ +#ifdef ANDROID + gfxAndroidPlatform::GetPlatform()->GetSystemFontList(retValue); +#endif + return true; +} + +bool +ContentParent::RecvReadDataStorageArray(const nsString& aFilename, + InfallibleTArray<DataStorageItem>* aValues) +{ + // If we're shutting down, the DataStorage object may have been cleared + // already, and setting it up is pointless anyways since we're about to die. + if (mShutdownPending) { + return true; + } + + // Ensure the SSS is initialized before we try to use its storage. + nsCOMPtr<nsISiteSecurityService> sss = do_GetService("@mozilla.org/ssservice;1"); + + RefPtr<DataStorage> storage = DataStorage::Get(aFilename); + storage->GetAll(aValues); + return true; +} + +bool +ContentParent::RecvReadPermissions(InfallibleTArray<IPC::Permission>* aPermissions) +{ +#ifdef MOZ_PERMISSIONS + nsCOMPtr<nsIPermissionManager> permissionManagerIface = + services::GetPermissionManager(); + nsPermissionManager* permissionManager = + static_cast<nsPermissionManager*>(permissionManagerIface.get()); + MOZ_ASSERT(permissionManager, + "We have no permissionManager in the Chrome process !"); + + nsCOMPtr<nsISimpleEnumerator> enumerator; + DebugOnly<nsresult> rv = permissionManager->GetEnumerator(getter_AddRefs(enumerator)); + MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not get enumerator!"); + while(1) { + bool hasMore; + enumerator->HasMoreElements(&hasMore); + if (!hasMore) + break; + + nsCOMPtr<nsISupports> supp; + enumerator->GetNext(getter_AddRefs(supp)); + nsCOMPtr<nsIPermission> perm = do_QueryInterface(supp); + + nsCOMPtr<nsIPrincipal> principal; + perm->GetPrincipal(getter_AddRefs(principal)); + nsCString origin; + if (principal) { + principal->GetOrigin(origin); + } + nsCString type; + perm->GetType(type); + uint32_t capability; + perm->GetCapability(&capability); + uint32_t expireType; + perm->GetExpireType(&expireType); + int64_t expireTime; + perm->GetExpireTime(&expireTime); + + aPermissions->AppendElement(IPC::Permission(origin, type, + capability, expireType, + expireTime)); + } + + // Ask for future changes + mSendPermissionUpdates = true; +#endif + + return true; +} + +bool +ContentParent::RecvSetClipboard(const IPCDataTransfer& aDataTransfer, + const bool& aIsPrivateData, + const IPC::Principal& aRequestingPrincipal, + const int32_t& aWhichClipboard) +{ + nsresult rv; + nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv)); + NS_ENSURE_SUCCESS(rv, true); + + nsCOMPtr<nsITransferable> trans = + do_CreateInstance("@mozilla.org/widget/transferable;1", &rv); + NS_ENSURE_SUCCESS(rv, true); + trans->Init(nullptr); + + rv = nsContentUtils::IPCTransferableToTransferable(aDataTransfer, + aIsPrivateData, + aRequestingPrincipal, + trans, this, nullptr); + NS_ENSURE_SUCCESS(rv, true); + + clipboard->SetData(trans, nullptr, aWhichClipboard); + return true; +} + +bool +ContentParent::RecvGetClipboard(nsTArray<nsCString>&& aTypes, + const int32_t& aWhichClipboard, + IPCDataTransfer* aDataTransfer) +{ + nsresult rv; + nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv)); + NS_ENSURE_SUCCESS(rv, true); + + nsCOMPtr<nsITransferable> trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv); + NS_ENSURE_SUCCESS(rv, true); + trans->Init(nullptr); + + for (uint32_t t = 0; t < aTypes.Length(); t++) { + trans->AddDataFlavor(aTypes[t].get()); + } + + clipboard->GetData(trans, aWhichClipboard); + nsContentUtils::TransferableToIPCTransferable(trans, aDataTransfer, + true, nullptr, this); + return true; +} + +bool +ContentParent::RecvEmptyClipboard(const int32_t& aWhichClipboard) +{ + nsresult rv; + nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv)); + NS_ENSURE_SUCCESS(rv, true); + + clipboard->EmptyClipboard(aWhichClipboard); + + return true; +} + +bool +ContentParent::RecvClipboardHasType(nsTArray<nsCString>&& aTypes, + const int32_t& aWhichClipboard, + bool* aHasType) +{ + nsresult rv; + nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv)); + NS_ENSURE_SUCCESS(rv, true); + + const char** typesChrs = new const char *[aTypes.Length()]; + for (uint32_t t = 0; t < aTypes.Length(); t++) { + typesChrs[t] = aTypes[t].get(); + } + + clipboard->HasDataMatchingFlavors(typesChrs, aTypes.Length(), + aWhichClipboard, aHasType); + + delete [] typesChrs; + return true; +} + +bool +ContentParent::RecvGetSystemColors(const uint32_t& colorsCount, + InfallibleTArray<uint32_t>* colors) +{ +#ifdef MOZ_WIDGET_ANDROID + NS_ASSERTION(AndroidBridge::Bridge() != nullptr, "AndroidBridge is not available"); + if (AndroidBridge::Bridge() == nullptr) { + // Do not fail - the colors won't be right, but it's not critical + return true; + } + + colors->AppendElements(colorsCount); + + // The array elements correspond to the members of AndroidSystemColors structure, + // so just pass the pointer to the elements buffer + AndroidBridge::Bridge()->GetSystemColors((AndroidSystemColors*)colors->Elements()); +#endif + return true; +} + +bool +ContentParent::RecvGetIconForExtension(const nsCString& aFileExt, + const uint32_t& aIconSize, + InfallibleTArray<uint8_t>* bits) +{ +#ifdef MOZ_WIDGET_ANDROID + NS_ASSERTION(AndroidBridge::Bridge() != nullptr, "AndroidBridge is not available"); + if (AndroidBridge::Bridge() == nullptr) { + // Do not fail - just no icon will be shown + return true; + } + + bits->AppendElements(aIconSize * aIconSize * 4); + + AndroidBridge::Bridge()->GetIconForExtension(aFileExt, aIconSize, bits->Elements()); +#endif + return true; +} + +bool +ContentParent::RecvGetShowPasswordSetting(bool* showPassword) +{ + // default behavior is to show the last password character + *showPassword = true; +#ifdef MOZ_WIDGET_ANDROID + NS_ASSERTION(AndroidBridge::Bridge() != nullptr, "AndroidBridge is not available"); + + *showPassword = java::GeckoAppShell::GetShowPasswordSetting(); +#endif + return true; +} + +bool +ContentParent::RecvFirstIdle() +{ + // When the ContentChild goes idle, it sends us a FirstIdle message + // which we use as a good time to prelaunch another process. If we + // prelaunch any sooner than this, then we'll be competing with the + // child process and slowing it down. + PreallocatedProcessManager::AllocateAfterDelay(); + return true; +} + +bool +ContentParent::RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel, + const bool& aHidden) +{ + RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate(); + MOZ_ASSERT(service); + service->SetDefaultVolumeControlChannelInternal(aChannel, aHidden, mChildID); + return true; +} + +bool +ContentParent::RecvAudioChannelServiceStatus( + const bool& aTelephonyChannel, + const bool& aContentOrNormalChannel, + const bool& aAnyChannel) +{ + RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate(); + MOZ_ASSERT(service); + + service->ChildStatusReceived(mChildID, aTelephonyChannel, + aContentOrNormalChannel, aAnyChannel); + return true; +} + +// We want ContentParent to show up in CC logs for debugging purposes, but we +// don't actually cycle collect it. +NS_IMPL_CYCLE_COLLECTION_0(ContentParent) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(ContentParent) +NS_IMPL_CYCLE_COLLECTING_RELEASE(ContentParent) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ContentParent) + NS_INTERFACE_MAP_ENTRY(nsIContentParent) + NS_INTERFACE_MAP_ENTRY(nsIObserver) + NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionCallback) + NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionErrorCallback) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver) +NS_INTERFACE_MAP_END + +NS_IMETHODIMP +ContentParent::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + if (mSubprocess && (!strcmp(aTopic, "profile-before-change") || + !strcmp(aTopic, "xpcom-shutdown"))) { + // Okay to call ShutDownProcess multiple times. + ShutDownProcess(SEND_SHUTDOWN_MESSAGE); + + // Wait for shutdown to complete, so that we receive any shutdown + // data (e.g. telemetry) from the child before we quit. + // This loop terminate prematurely based on mForceKillTimer. + while (mIPCOpen && !mCalledKillHard) { + NS_ProcessNextEvent(nullptr, true); + } + NS_ASSERTION(!mSubprocess, "Close should have nulled mSubprocess"); + } + +#ifdef MOZ_ENABLE_PROFILER_SPS + // Need to do this before the mIsAlive check to avoid missing profiles. + if (!strcmp(aTopic, "profiler-subprocess-gather")) { + if (mGatherer) { + mGatherer->WillGatherOOPProfile(); + if (mIsAlive && mSubprocess) { + Unused << SendGatherProfile(); + } + } + } + else if (!strcmp(aTopic, "profiler-subprocess")) { + nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject); + if (pse) { + if (!mProfile.IsEmpty()) { + pse->AddSubProfile(mProfile.get()); + mProfile.Truncate(); + } + } + } +#endif + + if (!mIsAlive || !mSubprocess) + return NS_OK; + + // listening for memory pressure event + if (!strcmp(aTopic, "memory-pressure") && + !StringEndsWith(nsDependentString(aData), + NS_LITERAL_STRING("-no-forward"))) { + Unused << SendFlushMemory(nsDependentString(aData)); + } + // listening for remotePrefs... + else if (!strcmp(aTopic, "nsPref:changed")) { + // We know prefs are ASCII here. + NS_LossyConvertUTF16toASCII strData(aData); + + PrefSetting pref(strData, null_t(), null_t()); + Preferences::GetPreference(&pref); + if (!SendPreferenceUpdate(pref)) { + return NS_ERROR_NOT_AVAILABLE; + } + } + else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC)) { + NS_ConvertUTF16toUTF8 dataStr(aData); + const char *offline = dataStr.get(); + if (!SendSetOffline(!strcmp(offline, "true") ? true : false)) { + return NS_ERROR_NOT_AVAILABLE; + } + } + else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC)) { + if (!SendSetConnectivity(NS_LITERAL_STRING("true").Equals(aData))) { + return NS_ERROR_NOT_AVAILABLE; + } + } else if (!strcmp(aTopic, NS_IPC_CAPTIVE_PORTAL_SET_STATE)) { + nsCOMPtr<nsICaptivePortalService> cps = do_QueryInterface(aSubject); + MOZ_ASSERT(cps, "Should QI to a captive portal service"); + if (!cps) { + return NS_ERROR_FAILURE; + } + int32_t state; + cps->GetState(&state); + if (!SendSetCaptivePortalState(state)) { + return NS_ERROR_NOT_AVAILABLE; + } + } + // listening for alert notifications + else if (!strcmp(aTopic, "alertfinished") || + !strcmp(aTopic, "alertclickcallback") || + !strcmp(aTopic, "alertshow") || + !strcmp(aTopic, "alertdisablecallback") || + !strcmp(aTopic, "alertsettingscallback")) { + if (!SendNotifyAlertsObserver(nsDependentCString(aTopic), + nsDependentString(aData))) + return NS_ERROR_NOT_AVAILABLE; + } + else if (!strcmp(aTopic, "child-gc-request")){ + Unused << SendGarbageCollect(); + } + else if (!strcmp(aTopic, "child-cc-request")){ + Unused << SendCycleCollect(); + } + else if (!strcmp(aTopic, "child-mmu-request")){ + Unused << SendMinimizeMemoryUsage(); + } + else if (!strcmp(aTopic, "last-pb-context-exited")) { + Unused << SendLastPrivateDocShellDestroyed(); + } +#ifdef MOZ_WIDGET_GONK + else if(!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) { + nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject); + if (!vol) { + return NS_ERROR_NOT_AVAILABLE; + } + + nsString volName; + nsString mountPoint; + int32_t state; + int32_t mountGeneration; + bool isMediaPresent; + bool isSharing; + bool isFormatting; + bool isFake; + bool isUnmounting; + bool isRemovable; + bool isHotSwappable; + + vol->GetName(volName); + vol->GetMountPoint(mountPoint); + vol->GetState(&state); + vol->GetMountGeneration(&mountGeneration); + vol->GetIsMediaPresent(&isMediaPresent); + vol->GetIsSharing(&isSharing); + vol->GetIsFormatting(&isFormatting); + vol->GetIsFake(&isFake); + vol->GetIsUnmounting(&isUnmounting); + vol->GetIsRemovable(&isRemovable); + vol->GetIsHotSwappable(&isHotSwappable); + + Unused << SendFileSystemUpdate(volName, mountPoint, state, + mountGeneration, isMediaPresent, + isSharing, isFormatting, isFake, + isUnmounting, isRemovable, isHotSwappable); + } else if (!strcmp(aTopic, "phone-state-changed")) { + nsString state(aData); + Unused << SendNotifyPhoneStateChange(state); + } + else if(!strcmp(aTopic, NS_VOLUME_REMOVED)) { + nsString volName(aData); + Unused << SendVolumeRemoved(volName); + } +#endif +#ifdef ACCESSIBILITY + else if (aData && !strcmp(aTopic, "a11y-init-or-shutdown")) { + if (*aData == '1') { + // Make sure accessibility is running in content process when + // accessibility gets initiated in chrome process. +#if !defined(XP_WIN) + Unused << SendActivateA11y(0); +#else + // On Windows we currently only enable a11y in the content process + // for testing purposes. + if (Preferences::GetBool(kForceEnableE10sPref, false)) { + Unused << SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID())); + } +#endif + } else { + // If possible, shut down accessibility in content process when + // accessibility gets shutdown in chrome process. + Unused << SendShutdownA11y(); + } + } +#endif +#ifdef MOZ_ENABLE_PROFILER_SPS + else if (!strcmp(aTopic, "profiler-started")) { + nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject)); + StartProfiler(params); + } + else if (!strcmp(aTopic, "profiler-stopped")) { + mGatherer = nullptr; + Unused << SendStopProfiler(); + } + else if (!strcmp(aTopic, "profiler-paused")) { + Unused << SendPauseProfiler(true); + } + else if (!strcmp(aTopic, "profiler-resumed")) { + Unused << SendPauseProfiler(false); + } +#endif + else if (!strcmp(aTopic, "cacheservice:empty-cache")) { + Unused << SendNotifyEmptyHTTPCache(); + } + return NS_OK; +} + +PGMPServiceParent* +ContentParent::AllocPGMPServiceParent(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess) +{ + return GMPServiceParent::Create(aTransport, aOtherProcess); +} + +PBackgroundParent* +ContentParent::AllocPBackgroundParent(Transport* aTransport, + ProcessId aOtherProcess) +{ + return BackgroundParent::Alloc(this, aTransport, aOtherProcess); +} + +PProcessHangMonitorParent* +ContentParent::AllocPProcessHangMonitorParent(Transport* aTransport, + ProcessId aOtherProcess) +{ + mHangMonitorActor = CreateHangMonitorParent(this, aTransport, aOtherProcess); + return mHangMonitorActor; +} + +bool +ContentParent::RecvGetProcessAttributes(ContentParentId* aCpId, + bool* aIsForApp, bool* aIsForBrowser) +{ + *aCpId = mChildID; + *aIsForApp = IsForApp(); + *aIsForBrowser = mIsForBrowser; + + return true; +} + +bool +ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline, + bool* aIsConnected, + int32_t* aCaptivePortalState, + bool* aIsLangRTL, + bool* aHaveBidiKeyboards, + InfallibleTArray<nsString>* dictionaries, + ClipboardCapabilities* clipboardCaps, + DomainPolicyClone* domainPolicy, + StructuredCloneData* aInitialData, + OptionalURIParams* aUserContentCSSURL) +{ + nsCOMPtr<nsIIOService> io(do_GetIOService()); + MOZ_ASSERT(io, "No IO service?"); + DebugOnly<nsresult> rv = io->GetOffline(aIsOffline); + MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting offline?"); + + rv = io->GetConnectivity(aIsConnected); + MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting connectivity?"); + + *aCaptivePortalState = nsICaptivePortalService::UNKNOWN; + nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CONTRACTID); + if (cps) { + cps->GetState(aCaptivePortalState); + } + + nsIBidiKeyboard* bidi = nsContentUtils::GetBidiKeyboard(); + + *aIsLangRTL = false; + *aHaveBidiKeyboards = false; + if (bidi) { + bidi->IsLangRTL(aIsLangRTL); + bidi->GetHaveBidiKeyboards(aHaveBidiKeyboards); + } + + nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID)); + MOZ_ASSERT(spellChecker, "No spell checker?"); + + spellChecker->GetDictionaryList(dictionaries); + + nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1")); + MOZ_ASSERT(clipboard, "No clipboard?"); + + rv = clipboard->SupportsSelectionClipboard(&clipboardCaps->supportsSelectionClipboard()); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + rv = clipboard->SupportsFindClipboard(&clipboardCaps->supportsFindClipboard()); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + // Let's copy the domain policy from the parent to the child (if it's active). + nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); + NS_ENSURE_TRUE(ssm, false); + ssm->CloneDomainPolicy(domainPolicy); + + if (nsFrameMessageManager* mm = nsFrameMessageManager::sParentProcessManager) { + AutoJSAPI jsapi; + if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) { + return false; + } + JS::RootedValue init(jsapi.cx()); + nsresult result = mm->GetInitialProcessData(jsapi.cx(), &init); + if (NS_FAILED(result)) { + return false; + } + + ErrorResult rv; + aInitialData->Write(jsapi.cx(), init, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return false; + } + } + + // Content processes have no permission to access profile directory, so we + // send the file URL instead. + StyleSheet* ucs = nsLayoutStylesheetCache::For(StyleBackendType::Gecko)->UserContentSheet(); + if (ucs) { + SerializeURI(ucs->GetSheetURI(), *aUserContentCSSURL); + } else { + SerializeURI(nullptr, *aUserContentCSSURL); + } + + return true; +} + +mozilla::jsipc::PJavaScriptParent * +ContentParent::AllocPJavaScriptParent() +{ + MOZ_ASSERT(ManagedPJavaScriptParent().IsEmpty()); + return nsIContentParent::AllocPJavaScriptParent(); +} + +bool +ContentParent::DeallocPJavaScriptParent(PJavaScriptParent *parent) +{ + return nsIContentParent::DeallocPJavaScriptParent(parent); +} + +PBrowserParent* +ContentParent::AllocPBrowserParent(const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpId, + const bool& aIsForApp, + const bool& aIsForBrowser) +{ + return nsIContentParent::AllocPBrowserParent(aTabId, + aContext, + aChromeFlags, + aCpId, + aIsForApp, + aIsForBrowser); +} + +bool +ContentParent::DeallocPBrowserParent(PBrowserParent* frame) +{ + return nsIContentParent::DeallocPBrowserParent(frame); +} + +PBlobParent* +ContentParent::AllocPBlobParent(const BlobConstructorParams& aParams) +{ + return nsIContentParent::AllocPBlobParent(aParams); +} + +bool +ContentParent::DeallocPBlobParent(PBlobParent* aActor) +{ + return nsIContentParent::DeallocPBlobParent(aActor); +} + +bool +ContentParent::RecvPBlobConstructor(PBlobParent* aActor, + const BlobConstructorParams& aParams) +{ + const ParentBlobConstructorParams& params = aParams.get_ParentBlobConstructorParams(); + if (params.blobParams().type() == AnyBlobConstructorParams::TKnownBlobConstructorParams) { + return aActor->SendCreatedFromKnownBlob(); + } + + return true; +} + +mozilla::PRemoteSpellcheckEngineParent * +ContentParent::AllocPRemoteSpellcheckEngineParent() +{ + mozilla::RemoteSpellcheckEngineParent *parent = new mozilla::RemoteSpellcheckEngineParent(); + return parent; +} + +bool +ContentParent::DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent *parent) +{ + delete parent; + return true; +} + +/* static */ void +ContentParent::ForceKillTimerCallback(nsITimer* aTimer, void* aClosure) +{ + // We don't want to time out the content process during XPCShell tests. This + // is the easiest way to ensure that. + if (PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) { + return; + } + + auto self = static_cast<ContentParent*>(aClosure); + self->KillHard("ShutDownKill"); +} + +// WARNING: aReason appears in telemetry, so any new value passed in requires +// data review. +void +ContentParent::KillHard(const char* aReason) +{ + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); + + // On Windows, calling KillHard multiple times causes problems - the + // process handle becomes invalid on the first call, causing a second call + // to crash our process - more details in bug 890840. + if (mCalledKillHard) { + return; + } + mCalledKillHard = true; + mForceKillTimer = nullptr; + +#if defined(MOZ_CRASHREPORTER) && !defined(MOZ_B2G) + // We're about to kill the child process associated with this content. + // Something has gone wrong to get us here, so we generate a minidump + // of the parent and child for submission to the crash server. + if (PCrashReporterParent* p = LoneManagedOrNullAsserts(ManagedPCrashReporterParent())) { + CrashReporterParent* crashReporter = + static_cast<CrashReporterParent*>(p); + // GeneratePairedMinidump creates two minidumps for us - the main + // one is for the content process we're about to kill, and the other + // one is for the main browser process. That second one is the extra + // minidump tagging along, so we have to tell the crash reporter that + // it exists and is being appended. + nsAutoCString additionalDumps("browser"); + crashReporter->AnnotateCrashReport( + NS_LITERAL_CSTRING("additional_minidumps"), + additionalDumps); + nsDependentCString reason(aReason); + crashReporter->AnnotateCrashReport( + NS_LITERAL_CSTRING("ipc_channel_error"), + reason); + + // Generate the report and insert into the queue for submittal. + mCreatedPairedMinidumps = crashReporter->GenerateCompleteMinidump(this); + + Telemetry::Accumulate(Telemetry::SUBPROCESS_KILL_HARD, reason, 1); + } +#endif + ProcessHandle otherProcessHandle; + if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle)) { + NS_ERROR("Failed to open child process when attempting kill."); + return; + } + + if (!KillProcess(otherProcessHandle, base::PROCESS_END_KILLED_BY_USER, + false)) { + NS_WARNING("failed to kill subprocess!"); + } + + if (mSubprocess) { + mSubprocess->SetAlreadyDead(); + } + + // EnsureProcessTerminated has responsibilty for closing otherProcessHandle. + XRE_GetIOMessageLoop()->PostTask( + NewRunnableFunction(&ProcessWatcher::EnsureProcessTerminated, + otherProcessHandle, /*force=*/true)); +} + +bool +ContentParent::IsPreallocated() const +{ + return mAppManifestURL == MAGIC_PREALLOCATED_APP_MANIFEST_URL; +} + +void +ContentParent::FriendlyName(nsAString& aName, bool aAnonymize) +{ + aName.Truncate(); + if (IsPreallocated()) { + aName.AssignLiteral("(Preallocated)"); + } else if (mIsForBrowser) { + aName.AssignLiteral("Browser"); + } else if (aAnonymize) { + aName.AssignLiteral("<anonymized-name>"); + } else if (!mAppName.IsEmpty()) { + aName = mAppName; + } else if (!mAppManifestURL.IsEmpty()) { + aName.AssignLiteral("Unknown app: "); + aName.Append(mAppManifestURL); + } else { + aName.AssignLiteral("???"); + } +} + +PCrashReporterParent* +ContentParent::AllocPCrashReporterParent(const NativeThreadId& tid, + const uint32_t& processType) +{ +#ifdef MOZ_CRASHREPORTER + return new CrashReporterParent(); +#else + return nullptr; +#endif +} + +bool +ContentParent::RecvPCrashReporterConstructor(PCrashReporterParent* actor, + const NativeThreadId& tid, + const uint32_t& processType) +{ + static_cast<CrashReporterParent*>(actor)->SetChildData(tid, processType); + return true; +} + +bool +ContentParent::DeallocPCrashReporterParent(PCrashReporterParent* crashreporter) +{ + delete crashreporter; + return true; +} + +hal_sandbox::PHalParent* +ContentParent::AllocPHalParent() +{ + return hal_sandbox::CreateHalParent(); +} + +bool +ContentParent::DeallocPHalParent(hal_sandbox::PHalParent* aHal) +{ + delete aHal; + return true; +} + +devtools::PHeapSnapshotTempFileHelperParent* +ContentParent::AllocPHeapSnapshotTempFileHelperParent() +{ + return devtools::HeapSnapshotTempFileHelperParent::Create(); +} + +bool +ContentParent::DeallocPHeapSnapshotTempFileHelperParent( + devtools::PHeapSnapshotTempFileHelperParent* aHeapSnapshotHelper) +{ + delete aHeapSnapshotHelper; + return true; +} + +PMemoryReportRequestParent* +ContentParent::AllocPMemoryReportRequestParent(const uint32_t& aGeneration, + const bool &aAnonymize, + const bool &aMinimizeMemoryUsage, + const MaybeFileDesc &aDMDFile) +{ + MemoryReportRequestParent* parent = + new MemoryReportRequestParent(aGeneration); + return parent; +} + +bool +ContentParent::DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor) +{ + delete actor; + return true; +} + +PCycleCollectWithLogsParent* +ContentParent::AllocPCycleCollectWithLogsParent(const bool& aDumpAllTraces, + const FileDescriptor& aGCLog, + const FileDescriptor& aCCLog) +{ + MOZ_CRASH("Don't call this; use ContentParent::CycleCollectWithLogs"); +} + +bool +ContentParent::DeallocPCycleCollectWithLogsParent(PCycleCollectWithLogsParent* aActor) +{ + delete aActor; + return true; +} + +bool +ContentParent::CycleCollectWithLogs(bool aDumpAllTraces, + nsICycleCollectorLogSink* aSink, + nsIDumpGCAndCCLogsCallback* aCallback) +{ + return CycleCollectWithLogsParent::AllocAndSendConstructor(this, + aDumpAllTraces, + aSink, + aCallback); +} + +PTestShellParent* +ContentParent::AllocPTestShellParent() +{ + return new TestShellParent(); +} + +bool +ContentParent::DeallocPTestShellParent(PTestShellParent* shell) +{ + delete shell; + return true; +} + +PNeckoParent* +ContentParent::AllocPNeckoParent() +{ + return new NeckoParent(); +} + +bool +ContentParent::DeallocPNeckoParent(PNeckoParent* necko) +{ + delete necko; + return true; +} + +PPrintingParent* +ContentParent::AllocPPrintingParent() +{ +#ifdef NS_PRINTING + MOZ_ASSERT(!mPrintingParent, + "Only one PrintingParent should be created per process."); + + // Create the printing singleton for this process. + mPrintingParent = new PrintingParent(); + return mPrintingParent.get(); +#else + MOZ_ASSERT_UNREACHABLE("Should never be created if no printing."); + return nullptr; +#endif +} + +bool +ContentParent::DeallocPPrintingParent(PPrintingParent* printing) +{ +#ifdef NS_PRINTING + MOZ_ASSERT(mPrintingParent == printing, + "Only one PrintingParent should have been created per process."); + + mPrintingParent = nullptr; +#else + MOZ_ASSERT_UNREACHABLE("Should never have been created if no printing."); +#endif + return true; +} + +#ifdef NS_PRINTING +already_AddRefed<embedding::PrintingParent> +ContentParent::GetPrintingParent() +{ + MOZ_ASSERT(mPrintingParent); + + RefPtr<embedding::PrintingParent> printingParent = mPrintingParent; + return printingParent.forget(); +} +#endif + +PSendStreamParent* +ContentParent::AllocPSendStreamParent() +{ + return nsIContentParent::AllocPSendStreamParent(); +} + +bool +ContentParent::DeallocPSendStreamParent(PSendStreamParent* aActor) +{ + return nsIContentParent::DeallocPSendStreamParent(aActor); +} + +PScreenManagerParent* +ContentParent::AllocPScreenManagerParent(uint32_t* aNumberOfScreens, + float* aSystemDefaultScale, + bool* aSuccess) +{ + return new ScreenManagerParent(aNumberOfScreens, aSystemDefaultScale, aSuccess); +} + +bool +ContentParent::DeallocPScreenManagerParent(PScreenManagerParent* aActor) +{ + delete aActor; + return true; +} + +PPSMContentDownloaderParent* +ContentParent::AllocPPSMContentDownloaderParent(const uint32_t& aCertType) +{ + RefPtr<PSMContentDownloaderParent> downloader = + new PSMContentDownloaderParent(aCertType); + return downloader.forget().take(); +} + +bool +ContentParent::DeallocPPSMContentDownloaderParent(PPSMContentDownloaderParent* aListener) +{ + auto* listener = static_cast<PSMContentDownloaderParent*>(aListener); + RefPtr<PSMContentDownloaderParent> downloader = dont_AddRef(listener); + return true; +} + +PExternalHelperAppParent* +ContentParent::AllocPExternalHelperAppParent(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, + PBrowserParent* aBrowser) +{ + ExternalHelperAppParent *parent = + new ExternalHelperAppParent(uri, aContentLength, aWasFileChannel); + parent->AddRef(); + parent->Init(this, + aMimeContentType, + aContentDisposition, + aContentDispositionHint, + aContentDispositionFilename, + aForceSave, + aReferrer, + aBrowser); + return parent; +} + +bool +ContentParent::DeallocPExternalHelperAppParent(PExternalHelperAppParent* aService) +{ + ExternalHelperAppParent *parent = static_cast<ExternalHelperAppParent *>(aService); + parent->Release(); + return true; +} + +PHandlerServiceParent* +ContentParent::AllocPHandlerServiceParent() +{ + HandlerServiceParent* actor = new HandlerServiceParent(); + actor->AddRef(); + return actor; +} + +bool +ContentParent::DeallocPHandlerServiceParent(PHandlerServiceParent* aHandlerServiceParent) +{ + static_cast<HandlerServiceParent*>(aHandlerServiceParent)->Release(); + return true; +} + +media::PMediaParent* +ContentParent::AllocPMediaParent() +{ + return media::AllocPMediaParent(); +} + +bool +ContentParent::DeallocPMediaParent(media::PMediaParent *aActor) +{ + return media::DeallocPMediaParent(aActor); +} + +PStorageParent* +ContentParent::AllocPStorageParent() +{ + return new DOMStorageDBParent(); +} + +bool +ContentParent::DeallocPStorageParent(PStorageParent* aActor) +{ + DOMStorageDBParent* child = static_cast<DOMStorageDBParent*>(aActor); + child->ReleaseIPDLReference(); + return true; +} + +PPresentationParent* +ContentParent::AllocPPresentationParent() +{ + RefPtr<PresentationParent> actor = new PresentationParent(); + return actor.forget().take(); +} + +bool +ContentParent::DeallocPPresentationParent(PPresentationParent* aActor) +{ + RefPtr<PresentationParent> actor = + dont_AddRef(static_cast<PresentationParent*>(aActor)); + return true; +} + +bool +ContentParent::RecvPPresentationConstructor(PPresentationParent* aActor) +{ + return static_cast<PresentationParent*>(aActor)->Init(mChildID); +} + +PFlyWebPublishedServerParent* +ContentParent::AllocPFlyWebPublishedServerParent(const nsString& name, + const FlyWebPublishOptions& params) +{ + RefPtr<FlyWebPublishedServerParent> actor = + new FlyWebPublishedServerParent(name, params); + return actor.forget().take(); +} + +bool +ContentParent::DeallocPFlyWebPublishedServerParent(PFlyWebPublishedServerParent* aActor) +{ + RefPtr<FlyWebPublishedServerParent> actor = + dont_AddRef(static_cast<FlyWebPublishedServerParent*>(aActor)); + return true; +} + +PSpeechSynthesisParent* +ContentParent::AllocPSpeechSynthesisParent() +{ +#ifdef MOZ_WEBSPEECH + return new mozilla::dom::SpeechSynthesisParent(); +#else + return nullptr; +#endif +} + +bool +ContentParent::DeallocPSpeechSynthesisParent(PSpeechSynthesisParent* aActor) +{ +#ifdef MOZ_WEBSPEECH + delete aActor; + return true; +#else + return false; +#endif +} + +bool +ContentParent::RecvPSpeechSynthesisConstructor(PSpeechSynthesisParent* aActor) +{ +#ifdef MOZ_WEBSPEECH + return true; +#else + return false; +#endif +} + +bool +ContentParent::RecvSpeakerManagerGetSpeakerStatus(bool* aValue) +{ +#ifdef MOZ_WIDGET_GONK + *aValue = false; + RefPtr<SpeakerManagerService> service = + SpeakerManagerService::GetOrCreateSpeakerManagerService(); + MOZ_ASSERT(service); + + *aValue = service->GetSpeakerStatus(); + return true; +#endif + return false; +} + +bool +ContentParent::RecvSpeakerManagerForceSpeaker(const bool& aEnable) +{ +#ifdef MOZ_WIDGET_GONK + RefPtr<SpeakerManagerService> service = + SpeakerManagerService::GetOrCreateSpeakerManagerService(); + MOZ_ASSERT(service); + service->ForceSpeaker(aEnable, mChildID); + + return true; +#endif + return false; +} + +bool +ContentParent::RecvStartVisitedQuery(const URIParams& aURI) +{ + nsCOMPtr<nsIURI> newURI = DeserializeURI(aURI); + if (!newURI) { + return false; + } + nsCOMPtr<IHistory> history = services::GetHistoryService(); + if (history) { + history->RegisterVisitedCallback(newURI, nullptr); + } + return true; +} + + +bool +ContentParent::RecvVisitURI(const URIParams& uri, + const OptionalURIParams& referrer, + const uint32_t& flags) +{ + nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri); + if (!ourURI) { + return false; + } + nsCOMPtr<nsIURI> ourReferrer = DeserializeURI(referrer); + nsCOMPtr<IHistory> history = services::GetHistoryService(); + if (history) { + history->VisitURI(ourURI, ourReferrer, flags); + } + return true; +} + + +bool +ContentParent::RecvSetURITitle(const URIParams& uri, + const nsString& title) +{ + nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri); + if (!ourURI) { + return false; + } + nsCOMPtr<IHistory> history = services::GetHistoryService(); + if (history) { + history->SetURITitle(ourURI, title); + } + return true; +} + +bool +ContentParent::RecvNSSU2FTokenIsCompatibleVersion(const nsString& aVersion, + bool* aIsCompatible) +{ + MOZ_ASSERT(aIsCompatible); + + nsCOMPtr<nsINSSU2FToken> nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID)); + if (NS_WARN_IF(!nssToken)) { + return false; + } + + nsresult rv = nssToken->IsCompatibleVersion(aVersion, aIsCompatible); + return NS_SUCCEEDED(rv); +} + +bool +ContentParent::RecvNSSU2FTokenIsRegistered(nsTArray<uint8_t>&& aKeyHandle, + bool* aIsValidKeyHandle) +{ + MOZ_ASSERT(aIsValidKeyHandle); + + nsCOMPtr<nsINSSU2FToken> nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID)); + if (NS_WARN_IF(!nssToken)) { + return false; + } + + nsresult rv = nssToken->IsRegistered(aKeyHandle.Elements(), aKeyHandle.Length(), + aIsValidKeyHandle); + return NS_SUCCEEDED(rv); +} + +bool +ContentParent::RecvNSSU2FTokenRegister(nsTArray<uint8_t>&& aApplication, + nsTArray<uint8_t>&& aChallenge, + nsTArray<uint8_t>* aRegistration) +{ + MOZ_ASSERT(aRegistration); + + nsCOMPtr<nsINSSU2FToken> nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID)); + if (NS_WARN_IF(!nssToken)) { + return false; + } + uint8_t* buffer; + uint32_t bufferlen; + nsresult rv = nssToken->Register(aApplication.Elements(), aApplication.Length(), + aChallenge.Elements(), aChallenge.Length(), + &buffer, &bufferlen); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + MOZ_ASSERT(buffer); + aRegistration->ReplaceElementsAt(0, aRegistration->Length(), buffer, bufferlen); + free(buffer); + return NS_SUCCEEDED(rv); +} + +bool +ContentParent::RecvNSSU2FTokenSign(nsTArray<uint8_t>&& aApplication, + nsTArray<uint8_t>&& aChallenge, + nsTArray<uint8_t>&& aKeyHandle, + nsTArray<uint8_t>* aSignature) +{ + MOZ_ASSERT(aSignature); + + nsCOMPtr<nsINSSU2FToken> nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID)); + if (NS_WARN_IF(!nssToken)) { + return false; + } + uint8_t* buffer; + uint32_t bufferlen; + nsresult rv = nssToken->Sign(aApplication.Elements(), aApplication.Length(), + aChallenge.Elements(), aChallenge.Length(), + aKeyHandle.Elements(), aKeyHandle.Length(), + &buffer, &bufferlen); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + MOZ_ASSERT(buffer); + aSignature->ReplaceElementsAt(0, aSignature->Length(), buffer, bufferlen); + free(buffer); + return NS_SUCCEEDED(rv); +} + +bool +ContentParent::RecvGetLookAndFeelCache(nsTArray<LookAndFeelInt>* aLookAndFeelIntCache) +{ + *aLookAndFeelIntCache = LookAndFeel::GetIntCache(); + return true; +} + +bool +ContentParent::RecvIsSecureURI(const uint32_t& type, + const URIParams& uri, + const uint32_t& flags, + bool* isSecureURI) +{ + nsCOMPtr<nsISiteSecurityService> sss(do_GetService(NS_SSSERVICE_CONTRACTID)); + if (!sss) { + return false; + } + nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri); + if (!ourURI) { + return false; + } + nsresult rv = sss->IsSecureURI(type, ourURI, flags, nullptr, isSecureURI); + return NS_SUCCEEDED(rv); +} + +bool +ContentParent::RecvAccumulateMixedContentHSTS(const URIParams& aURI, const bool& aActive, const bool& aHSTSPriming) +{ + nsCOMPtr<nsIURI> ourURI = DeserializeURI(aURI); + if (!ourURI) { + return false; + } + nsMixedContentBlocker::AccumulateMixedContentHSTS(ourURI, aActive, aHSTSPriming); + return true; +} + +bool +ContentParent::RecvLoadURIExternal(const URIParams& uri, + PBrowserParent* windowContext) +{ + nsCOMPtr<nsIExternalProtocolService> extProtService(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID)); + if (!extProtService) { + return true; + } + nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri); + if (!ourURI) { + return false; + } + + RefPtr<RemoteWindowContext> context = + new RemoteWindowContext(static_cast<TabParent*>(windowContext)); + extProtService->LoadURI(ourURI, context); + return true; +} + +bool +ContentParent::RecvExtProtocolChannelConnectParent(const uint32_t& registrarId) +{ + nsresult rv; + + // First get the real channel created before redirect on the parent. + nsCOMPtr<nsIChannel> channel; + rv = NS_LinkRedirectChannels(registrarId, nullptr, getter_AddRefs(channel)); + NS_ENSURE_SUCCESS(rv, true); + + nsCOMPtr<nsIParentChannel> parent = do_QueryInterface(channel, &rv); + NS_ENSURE_SUCCESS(rv, true); + + // The channel itself is its own (faked) parent, link it. + rv = NS_LinkRedirectChannels(registrarId, parent, getter_AddRefs(channel)); + NS_ENSURE_SUCCESS(rv, true); + + // Signal the parent channel that it's a redirect-to parent. This will + // make AsyncOpen on it do nothing (what we want). + // Yes, this is a bit of a hack, but I don't think it's necessary to invent + // a new interface just to set this flag on the channel. + parent->SetParentListener(nullptr); + + return true; +} + +bool +ContentParent::HasNotificationPermission(const IPC::Principal& aPrincipal) +{ + return true; +} + +bool +ContentParent::RecvShowAlert(const AlertNotificationType& aAlert) +{ + nsCOMPtr<nsIAlertNotification> alert(dont_AddRef(aAlert)); + if (NS_WARN_IF(!alert)) { + return true; + } + + nsCOMPtr<nsIPrincipal> principal; + nsresult rv = alert->GetPrincipal(getter_AddRefs(principal)); + if (NS_WARN_IF(NS_FAILED(rv)) || + !HasNotificationPermission(IPC::Principal(principal))) { + + return true; + } + + nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID)); + if (sysAlerts) { + sysAlerts->ShowAlert(alert, this); + } + return true; +} + +bool +ContentParent::RecvCloseAlert(const nsString& aName, + const IPC::Principal& aPrincipal) +{ + if (!HasNotificationPermission(aPrincipal)) { + return true; + } + + nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID)); + if (sysAlerts) { + sysAlerts->CloseAlert(aName, aPrincipal); + } + + return true; +} + +bool +ContentParent::RecvDisableNotifications(const IPC::Principal& aPrincipal) +{ + if (HasNotificationPermission(aPrincipal)) { + Unused << Notification::RemovePermission(aPrincipal); + } + return true; +} + +bool +ContentParent::RecvOpenNotificationSettings(const IPC::Principal& aPrincipal) +{ + if (HasNotificationPermission(aPrincipal)) { + Unused << Notification::OpenSettings(aPrincipal); + } + return true; +} + +bool +ContentParent::RecvSyncMessage(const nsString& aMsg, + const ClonedMessageData& aData, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + nsTArray<StructuredCloneData>* aRetvals) +{ + return nsIContentParent::RecvSyncMessage(aMsg, aData, Move(aCpows), + aPrincipal, aRetvals); +} + +bool +ContentParent::RecvRpcMessage(const nsString& aMsg, + const ClonedMessageData& aData, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + nsTArray<StructuredCloneData>* aRetvals) +{ + return nsIContentParent::RecvRpcMessage(aMsg, aData, Move(aCpows), aPrincipal, + aRetvals); +} + +bool +ContentParent::RecvAsyncMessage(const nsString& aMsg, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData) +{ + return nsIContentParent::RecvAsyncMessage(aMsg, Move(aCpows), aPrincipal, + aData); +} + +static int32_t +AddGeolocationListener(nsIDOMGeoPositionCallback* watcher, + nsIDOMGeoPositionErrorCallback* errorCallBack, + bool highAccuracy) +{ + nsCOMPtr<nsIDOMGeoGeolocation> geo = do_GetService("@mozilla.org/geolocation;1"); + if (!geo) { + return -1; + } + + UniquePtr<PositionOptions> options = MakeUnique<PositionOptions>(); + options->mTimeout = 0; + options->mMaximumAge = 0; + options->mEnableHighAccuracy = highAccuracy; + int32_t retval = 1; + geo->WatchPosition(watcher, errorCallBack, Move(options), &retval); + return retval; +} + +bool +ContentParent::RecvAddGeolocationListener(const IPC::Principal& aPrincipal, + const bool& aHighAccuracy) +{ + // To ensure no geolocation updates are skipped, we always force the + // creation of a new listener. + RecvRemoveGeolocationListener(); + mGeolocationWatchID = AddGeolocationListener(this, this, aHighAccuracy); + return true; +} + +bool +ContentParent::RecvRemoveGeolocationListener() +{ + if (mGeolocationWatchID != -1) { + nsCOMPtr<nsIDOMGeoGeolocation> geo = do_GetService("@mozilla.org/geolocation;1"); + if (!geo) { + return true; + } + geo->ClearWatch(mGeolocationWatchID); + mGeolocationWatchID = -1; + } + return true; +} + +bool +ContentParent::RecvSetGeolocationHigherAccuracy(const bool& aEnable) +{ + // This should never be called without a listener already present, + // so this check allows us to forgo securing privileges. + if (mGeolocationWatchID != -1) { + RecvRemoveGeolocationListener(); + mGeolocationWatchID = AddGeolocationListener(this, this, aEnable); + } + return true; +} + +NS_IMETHODIMP +ContentParent::HandleEvent(nsIDOMGeoPosition* postion) +{ + Unused << SendGeolocationUpdate(GeoPosition(postion)); + return NS_OK; +} + +NS_IMETHODIMP +ContentParent::HandleEvent(nsIDOMGeoPositionError* postionError) +{ + int16_t errorCode; + nsresult rv; + rv = postionError->GetCode(&errorCode); + NS_ENSURE_SUCCESS(rv,rv); + Unused << SendGeolocationError(errorCode); + return NS_OK; +} + +nsConsoleService * +ContentParent::GetConsoleService() +{ + if (mConsoleService) { + return mConsoleService.get(); + } + + // XXXkhuey everything about this is terrible. + // Get the ConsoleService by CID rather than ContractID, so that we + // can cast the returned pointer to an nsConsoleService (rather than + // just an nsIConsoleService). This allows us to call the non-idl function + // nsConsoleService::LogMessageWithMode. + NS_DEFINE_CID(consoleServiceCID, NS_CONSOLESERVICE_CID); + nsCOMPtr<nsIConsoleService> consoleService(do_GetService(consoleServiceCID)); + mConsoleService = static_cast<nsConsoleService*>(consoleService.get()); + return mConsoleService.get(); +} + +bool +ContentParent::RecvConsoleMessage(const nsString& aMessage) +{ + RefPtr<nsConsoleService> consoleService = GetConsoleService(); + if (!consoleService) { + return true; + } + + RefPtr<nsConsoleMessage> msg(new nsConsoleMessage(aMessage.get())); + consoleService->LogMessageWithMode(msg, nsConsoleService::SuppressLog); + return true; +} + +bool +ContentParent::RecvScriptError(const nsString& aMessage, + const nsString& aSourceName, + const nsString& aSourceLine, + const uint32_t& aLineNumber, + const uint32_t& aColNumber, + const uint32_t& aFlags, + const nsCString& aCategory) +{ + RefPtr<nsConsoleService> consoleService = GetConsoleService(); + if (!consoleService) { + return true; + } + + nsCOMPtr<nsIScriptError> msg(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID)); + nsresult rv = msg->Init(aMessage, aSourceName, aSourceLine, + aLineNumber, aColNumber, aFlags, aCategory.get()); + if (NS_FAILED(rv)) + return true; + + consoleService->LogMessageWithMode(msg, nsConsoleService::SuppressLog); + return true; +} + +bool +ContentParent::RecvPrivateDocShellsExist(const bool& aExist) +{ + if (!sPrivateContent) + sPrivateContent = new nsTArray<ContentParent*>(); + if (aExist) { + sPrivateContent->AppendElement(this); + } else { + sPrivateContent->RemoveElement(this); + + // Only fire the notification if we have private and non-private + // windows: if privatebrowsing.autostart is true, all windows are + // private. + if (!sPrivateContent->Length() && + !Preferences::GetBool("browser.privatebrowsing.autostart")) { + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr); + delete sPrivateContent; + sPrivateContent = nullptr; + } + } + return true; +} + +bool +ContentParent::DoLoadMessageManagerScript(const nsAString& aURL, + bool aRunInGlobalScope) +{ + MOZ_ASSERT(!aRunInGlobalScope); + return SendLoadProcessScript(nsString(aURL)); +} + +nsresult +ContentParent::DoSendAsyncMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aHelper, + JS::Handle<JSObject *> aCpows, + nsIPrincipal* aPrincipal) +{ + ClonedMessageData data; + if (!BuildClonedMessageDataForParent(this, aHelper, data)) { + return NS_ERROR_DOM_DATA_CLONE_ERR; + } + InfallibleTArray<CpowEntry> cpows; + jsipc::CPOWManager* mgr = GetCPOWManager(); + if (aCpows && (!mgr || !mgr->Wrap(aCx, aCpows, &cpows))) { + return NS_ERROR_UNEXPECTED; + } + if (!SendAsyncMessage(nsString(aMessage), cpows, Principal(aPrincipal), data)) { + return NS_ERROR_UNEXPECTED; + } + return NS_OK; +} + +bool +ContentParent::CheckPermission(const nsAString& aPermission) +{ + return AssertAppProcessPermission(this, NS_ConvertUTF16toUTF8(aPermission).get()); +} + +bool +ContentParent::CheckManifestURL(const nsAString& aManifestURL) +{ + return AssertAppProcessManifestURL(this, NS_ConvertUTF16toUTF8(aManifestURL).get()); +} + +bool +ContentParent::CheckAppHasPermission(const nsAString& aPermission) +{ + return AssertAppHasPermission(this, NS_ConvertUTF16toUTF8(aPermission).get()); +} + +bool +ContentParent::CheckAppHasStatus(unsigned short aStatus) +{ + return AssertAppHasStatus(this, aStatus); +} + +bool +ContentParent::KillChild() +{ + KillHard("KillChild"); + return true; +} + +PBlobParent* +ContentParent::SendPBlobConstructor(PBlobParent* aActor, + const BlobConstructorParams& aParams) +{ + return PContentParent::SendPBlobConstructor(aActor, aParams); +} + +PBrowserParent* +ContentParent::SendPBrowserConstructor(PBrowserParent* aActor, + const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpId, + const bool& aIsForApp, + const bool& aIsForBrowser) +{ + return PContentParent::SendPBrowserConstructor(aActor, + aTabId, + aContext, + aChromeFlags, + aCpId, + aIsForApp, + aIsForBrowser); +} + +bool +ContentParent::RecvCreateFakeVolume(const nsString& fsName, + const nsString& mountPoint) +{ +#ifdef MOZ_WIDGET_GONK + nsresult rv; + nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv); + if (vs) { + vs->CreateFakeVolume(fsName, mountPoint); + } + return true; +#else + NS_WARNING("ContentParent::RecvCreateFakeVolume shouldn't be called when MOZ_WIDGET_GONK is not defined"); + return false; +#endif +} + +bool +ContentParent::RecvSetFakeVolumeState(const nsString& fsName, const int32_t& fsState) +{ +#ifdef MOZ_WIDGET_GONK + nsresult rv; + nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv); + if (vs) { + vs->SetFakeVolumeState(fsName, fsState); + } + return true; +#else + NS_WARNING("ContentParent::RecvSetFakeVolumeState shouldn't be called when MOZ_WIDGET_GONK is not defined"); + return false; +#endif +} + +bool +ContentParent::RecvRemoveFakeVolume(const nsString& fsName) +{ +#ifdef MOZ_WIDGET_GONK + nsresult rv; + nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv); + if (vs) { + vs->RemoveFakeVolume(fsName); + } + return true; +#else + NS_WARNING("ContentParent::RecvRemoveFakeVolume shouldn't be called when MOZ_WIDGET_GONK is not defined"); + return false; +#endif +} + +bool +ContentParent::RecvKeywordToURI(const nsCString& aKeyword, + nsString* aProviderName, + OptionalInputStreamParams* aPostData, + OptionalURIParams* aURI) +{ + *aPostData = void_t(); + *aURI = void_t(); + + nsCOMPtr<nsIURIFixup> fixup = do_GetService(NS_URIFIXUP_CONTRACTID); + if (!fixup) { + return true; + } + + nsCOMPtr<nsIInputStream> postData; + nsCOMPtr<nsIURIFixupInfo> info; + + if (NS_FAILED(fixup->KeywordToURI(aKeyword, getter_AddRefs(postData), + getter_AddRefs(info)))) { + return true; + } + info->GetKeywordProviderName(*aProviderName); + + nsTArray<mozilla::ipc::FileDescriptor> fds; + SerializeInputStream(postData, *aPostData, fds); + MOZ_ASSERT(fds.IsEmpty()); + + nsCOMPtr<nsIURI> uri; + info->GetPreferredURI(getter_AddRefs(uri)); + SerializeURI(uri, *aURI); + return true; +} + +bool +ContentParent::RecvNotifyKeywordSearchLoading(const nsString &aProvider, + const nsString &aKeyword) +{ +#ifdef MOZ_TOOLKIT_SEARCH + nsCOMPtr<nsIBrowserSearchService> searchSvc = do_GetService("@mozilla.org/browser/search-service;1"); + if (searchSvc) { + nsCOMPtr<nsISearchEngine> searchEngine; + searchSvc->GetEngineByName(aProvider, getter_AddRefs(searchEngine)); + if (searchEngine) { + nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); + if (obsSvc) { + // Note that "keyword-search" refers to a search via the url + // bar, not a bookmarks keyword search. + obsSvc->NotifyObservers(searchEngine, "keyword-search", aKeyword.get()); + } + } + } +#endif + return true; +} + +bool +ContentParent::RecvCopyFavicon(const URIParams& aOldURI, + const URIParams& aNewURI, + const IPC::Principal& aLoadingPrincipal, + const bool& aInPrivateBrowsing) +{ + nsCOMPtr<nsIURI> oldURI = DeserializeURI(aOldURI); + if (!oldURI) { + return true; + } + nsCOMPtr<nsIURI> newURI = DeserializeURI(aNewURI); + if (!newURI) { + return true; + } + + nsDocShell::CopyFavicon(oldURI, newURI, aLoadingPrincipal, aInPrivateBrowsing); + return true; +} + +bool +ContentParent::ShouldContinueFromReplyTimeout() +{ + RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get(); + return !monitor || !monitor->ShouldTimeOutCPOWs(); +} + +bool +ContentParent::RecvRecordingDeviceEvents(const nsString& aRecordingStatus, + const nsString& aPageURL, + const bool& aIsAudio, + const bool& aIsVideo) +{ + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + // recording-device-ipc-events needs to gather more information from content process + RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag(); + props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), ChildID()); + props->SetPropertyAsBool(NS_LITERAL_STRING("isApp"), IsForApp()); + props->SetPropertyAsBool(NS_LITERAL_STRING("isAudio"), aIsAudio); + props->SetPropertyAsBool(NS_LITERAL_STRING("isVideo"), aIsVideo); + + nsString requestURL = IsForApp() ? AppManifestURL() : aPageURL; + props->SetPropertyAsAString(NS_LITERAL_STRING("requestURL"), requestURL); + + obs->NotifyObservers((nsIPropertyBag2*) props, + "recording-device-ipc-events", + aRecordingStatus.get()); + } else { + NS_WARNING("Could not get the Observer service for ContentParent::RecvRecordingDeviceEvents."); + } + return true; +} + +bool +ContentParent::RecvGetGraphicsFeatureStatus(const int32_t& aFeature, + int32_t* aStatus, + nsCString* aFailureId, + bool* aSuccess) +{ + nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo(); + if (!gfxInfo) { + *aSuccess = false; + return true; + } + + *aSuccess = NS_SUCCEEDED(gfxInfo->GetFeatureStatus(aFeature, *aFailureId, aStatus)); + return true; +} + +bool +ContentParent::RecvAddIdleObserver(const uint64_t& aObserver, + const uint32_t& aIdleTimeInS) +{ + nsresult rv; + nsCOMPtr<nsIIdleService> idleService = + do_GetService("@mozilla.org/widget/idleservice;1", &rv); + NS_ENSURE_SUCCESS(rv, false); + + RefPtr<ParentIdleListener> listener = + new ParentIdleListener(this, aObserver, aIdleTimeInS); + rv = idleService->AddIdleObserver(listener, aIdleTimeInS); + NS_ENSURE_SUCCESS(rv, false); + mIdleListeners.AppendElement(listener); + return true; +} + +bool +ContentParent::RecvRemoveIdleObserver(const uint64_t& aObserver, + const uint32_t& aIdleTimeInS) +{ + RefPtr<ParentIdleListener> listener; + for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) { + listener = static_cast<ParentIdleListener*>(mIdleListeners[i].get()); + if (listener->mObserver == aObserver && + listener->mTime == aIdleTimeInS) { + nsresult rv; + nsCOMPtr<nsIIdleService> idleService = + do_GetService("@mozilla.org/widget/idleservice;1", &rv); + NS_ENSURE_SUCCESS(rv, false); + idleService->RemoveIdleObserver(listener, aIdleTimeInS); + mIdleListeners.RemoveElementAt(i); + break; + } + } + return true; +} + +bool +ContentParent::RecvBackUpXResources(const FileDescriptor& aXSocketFd) +{ +#ifndef MOZ_X11 + NS_RUNTIMEABORT("This message only makes sense on X11 platforms"); +#else + MOZ_ASSERT(0 > mChildXSocketFdDup.get(), + "Already backed up X resources??"); + if (aXSocketFd.IsValid()) { + auto rawFD = aXSocketFd.ClonePlatformHandle(); + mChildXSocketFdDup.reset(rawFD.release()); + } +#endif + return true; +} + +bool +ContentParent::RecvOpenAnonymousTemporaryFile(FileDescOrError *aFD) +{ + PRFileDesc *prfd; + nsresult rv = NS_OpenAnonymousTemporaryFile(&prfd); + if (NS_WARN_IF(NS_FAILED(rv))) { + // Returning false will kill the child process; instead + // propagate the error and let the child handle it. + *aFD = rv; + return true; + } + *aFD = FileDescriptor(FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(prfd))); + // The FileDescriptor object owns a duplicate of the file handle; we + // must close the original (and clean up the NSPR descriptor). + PR_Close(prfd); + return true; +} + +static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID); + +bool +ContentParent::RecvKeygenProcessValue(const nsString& oldValue, + const nsString& challenge, + const nsString& keytype, + const nsString& keyparams, + nsString* newValue) +{ + nsCOMPtr<nsIFormProcessor> formProcessor = + do_GetService(kFormProcessorCID); + if (!formProcessor) { + newValue->Truncate(); + return true; + } + + formProcessor->ProcessValueIPC(oldValue, challenge, keytype, keyparams, + *newValue); + return true; +} + +bool +ContentParent::RecvKeygenProvideContent(nsString* aAttribute, + nsTArray<nsString>* aContent) +{ + nsCOMPtr<nsIFormProcessor> formProcessor = + do_GetService(kFormProcessorCID); + if (!formProcessor) { + return true; + } + + formProcessor->ProvideContent(NS_LITERAL_STRING("SELECT"), *aContent, + *aAttribute); + return true; +} + +PFileDescriptorSetParent* +ContentParent::AllocPFileDescriptorSetParent(const FileDescriptor& aFD) +{ + return nsIContentParent::AllocPFileDescriptorSetParent(aFD); +} + +bool +ContentParent::DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor) +{ + return nsIContentParent::DeallocPFileDescriptorSetParent(aActor); +} + +bool +ContentParent::IgnoreIPCPrincipal() +{ + static bool sDidAddVarCache = false; + static bool sIgnoreIPCPrincipal = false; + if (!sDidAddVarCache) { + sDidAddVarCache = true; + Preferences::AddBoolVarCache(&sIgnoreIPCPrincipal, + "dom.testing.ignore_ipc_principal", false); + } + return sIgnoreIPCPrincipal; +} + +void +ContentParent::NotifyUpdatedDictionaries() +{ + nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID)); + MOZ_ASSERT(spellChecker, "No spell checker?"); + + InfallibleTArray<nsString> dictionaries; + spellChecker->GetDictionaryList(&dictionaries); + + for (auto* cp : AllProcesses(eLive)) { + Unused << cp->SendUpdateDictionaryList(dictionaries); + } +} + +/*static*/ TabId +ContentParent::AllocateTabId(const TabId& aOpenerTabId, + const IPCTabContext& aContext, + const ContentParentId& aCpId) +{ + TabId tabId; + if (XRE_IsParentProcess()) { + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + tabId = cpm->AllocateTabId(aOpenerTabId, aContext, aCpId); + } + else { + ContentChild::GetSingleton()->SendAllocateTabId(aOpenerTabId, + aContext, + aCpId, + &tabId); + } + return tabId; +} + +/*static*/ void +ContentParent::DeallocateTabId(const TabId& aTabId, + const ContentParentId& aCpId, + bool aMarkedDestroying) +{ + if (XRE_IsParentProcess()) { + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + ContentParent* cp = cpm->GetContentProcessById(aCpId); + + cp->NotifyTabDestroyed(aTabId, aMarkedDestroying); + + ContentProcessManager::GetSingleton()->DeallocateTabId(aCpId, aTabId); + } else { + ContentChild::GetSingleton()->SendDeallocateTabId(aTabId, aCpId, + aMarkedDestroying); + } +} + +bool +ContentParent::RecvAllocateTabId(const TabId& aOpenerTabId, + const IPCTabContext& aContext, + const ContentParentId& aCpId, + TabId* aTabId) +{ + *aTabId = AllocateTabId(aOpenerTabId, aContext, aCpId); + if (!(*aTabId)) { + return false; + } + return true; +} + +bool +ContentParent::RecvDeallocateTabId(const TabId& aTabId, + const ContentParentId& aCpId, + const bool& aMarkedDestroying) +{ + DeallocateTabId(aTabId, aCpId, aMarkedDestroying); + return true; +} + +bool +ContentParent::RecvNotifyTabDestroying(const TabId& aTabId, + const ContentParentId& aCpId) +{ + NotifyTabDestroying(aTabId, aCpId); + return true; +} + +nsTArray<TabContext> +ContentParent::GetManagedTabContext() +{ + return Move(ContentProcessManager::GetSingleton()-> + GetTabContextByContentProcess(this->ChildID())); +} + +mozilla::docshell::POfflineCacheUpdateParent* +ContentParent::AllocPOfflineCacheUpdateParent(const URIParams& aManifestURI, + const URIParams& aDocumentURI, + const PrincipalInfo& aLoadingPrincipalInfo, + const bool& aStickDocument) +{ + RefPtr<mozilla::docshell::OfflineCacheUpdateParent> update = + new mozilla::docshell::OfflineCacheUpdateParent(); + // Use this reference as the IPDL reference. + return update.forget().take(); +} + +bool +ContentParent::RecvPOfflineCacheUpdateConstructor(POfflineCacheUpdateParent* aActor, + const URIParams& aManifestURI, + const URIParams& aDocumentURI, + const PrincipalInfo& aLoadingPrincipal, + const bool& aStickDocument) +{ + MOZ_ASSERT(aActor); + + RefPtr<mozilla::docshell::OfflineCacheUpdateParent> update = + static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(aActor); + + nsresult rv = update->Schedule(aManifestURI, aDocumentURI, aLoadingPrincipal, aStickDocument); + if (NS_FAILED(rv) && IsAlive()) { + // Inform the child of failure. + Unused << update->SendFinish(false, false); + } + + return true; +} + +bool +ContentParent::DeallocPOfflineCacheUpdateParent(POfflineCacheUpdateParent* aActor) +{ + // Reclaim the IPDL reference. + RefPtr<mozilla::docshell::OfflineCacheUpdateParent> update = + dont_AddRef(static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(aActor)); + return true; +} + +PWebrtcGlobalParent * +ContentParent::AllocPWebrtcGlobalParent() +{ +#ifdef MOZ_WEBRTC + return WebrtcGlobalParent::Alloc(); +#else + return nullptr; +#endif +} + +bool +ContentParent::DeallocPWebrtcGlobalParent(PWebrtcGlobalParent *aActor) +{ +#ifdef MOZ_WEBRTC + WebrtcGlobalParent::Dealloc(static_cast<WebrtcGlobalParent*>(aActor)); + return true; +#else + return false; +#endif +} + +bool +ContentParent::RecvSetOfflinePermission(const Principal& aPrincipal) +{ + nsIPrincipal* principal = aPrincipal; + nsContentUtils::MaybeAllowOfflineAppByDefault(principal); + return true; +} + +void +ContentParent::MaybeInvokeDragSession(TabParent* aParent) +{ + nsCOMPtr<nsIDragService> dragService = + do_GetService("@mozilla.org/widget/dragservice;1"); + if (dragService && dragService->MaybeAddChildProcess(this)) { + // We need to send transferable data to child process. + nsCOMPtr<nsIDragSession> session; + dragService->GetCurrentSession(getter_AddRefs(session)); + if (session) { + nsTArray<IPCDataTransfer> dataTransfers; + nsCOMPtr<nsIDOMDataTransfer> domTransfer; + session->GetDataTransfer(getter_AddRefs(domTransfer)); + nsCOMPtr<DataTransfer> transfer = do_QueryInterface(domTransfer); + if (!transfer) { + // Pass eDrop to get DataTransfer with external + // drag formats cached. + transfer = new DataTransfer(nullptr, eDrop, true, -1); + session->SetDataTransfer(transfer); + } + // Note, even though this fills the DataTransfer object with + // external data, the data is usually transfered over IPC lazily when + // needed. + transfer->FillAllExternalData(); + nsCOMPtr<nsILoadContext> lc = aParent ? + aParent->GetLoadContext() : nullptr; + nsCOMPtr<nsIArray> transferables = + transfer->GetTransferables(lc); + nsContentUtils::TransferablesToIPCTransferables(transferables, + dataTransfers, + false, + nullptr, + this); + uint32_t action; + session->GetDragAction(&action); + mozilla::Unused << SendInvokeDragSession(dataTransfers, action); + } + } +} + +bool +ContentParent::RecvUpdateDropEffect(const uint32_t& aDragAction, + const uint32_t& aDropEffect) +{ + nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession(); + if (dragSession) { + dragSession->SetDragAction(aDragAction); + nsCOMPtr<nsIDOMDataTransfer> dt; + dragSession->GetDataTransfer(getter_AddRefs(dt)); + if (dt) { + dt->SetDropEffectInt(aDropEffect); + } + dragSession->UpdateDragEffect(); + } + return true; +} + +PContentPermissionRequestParent* +ContentParent::AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests, + const IPC::Principal& aPrincipal, + const TabId& aTabId) +{ + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + RefPtr<TabParent> tp = + cpm->GetTopLevelTabParentByProcessAndTabId(this->ChildID(), aTabId); + if (!tp) { + return nullptr; + } + + return nsContentPermissionUtils::CreateContentPermissionRequestParent(aRequests, + tp->GetOwnerElement(), + aPrincipal, + aTabId); +} + +bool +ContentParent::DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor) +{ + nsContentPermissionUtils::NotifyRemoveContentPermissionRequestParent(actor); + delete actor; + return true; +} + +PWebBrowserPersistDocumentParent* +ContentParent::AllocPWebBrowserPersistDocumentParent(PBrowserParent* aBrowser, + const uint64_t& aOuterWindowID) +{ + return new WebBrowserPersistDocumentParent(); +} + +bool +ContentParent::DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentParent* aActor) +{ + delete aActor; + return true; +} + +bool +ContentParent::RecvCreateWindow(PBrowserParent* aThisTab, + PBrowserParent* aNewTab, + PRenderFrameParent* aRenderFrame, + const uint32_t& aChromeFlags, + const bool& aCalledFromJS, + const bool& aPositionSpecified, + const bool& aSizeSpecified, + const nsCString& aFeatures, + const nsCString& aBaseURI, + const DocShellOriginAttributes& aOpenerOriginAttributes, + const float& aFullZoom, + nsresult* aResult, + bool* aWindowIsNew, + InfallibleTArray<FrameScriptInfo>* aFrameScripts, + nsCString* aURLToLoad, + TextureFactoryIdentifier* aTextureFactoryIdentifier, + uint64_t* aLayersId) +{ + // We always expect to open a new window here. If we don't, it's an error. + *aWindowIsNew = true; + *aResult = NS_OK; + + // The content process should never be in charge of computing whether or + // not a window should be private or remote - the parent will do that. + const uint32_t badFlags = + nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW + | nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW + | nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME + | nsIWebBrowserChrome::CHROME_REMOTE_WINDOW; + if (!!(aChromeFlags & badFlags)) { + return false; + } + + TabParent* thisTabParent = nullptr; + if (aThisTab) { + thisTabParent = TabParent::GetFrom(aThisTab); + } + + if (NS_WARN_IF(thisTabParent && thisTabParent->IsMozBrowserOrApp())) { + return false; + } + + TabParent* newTab = TabParent::GetFrom(aNewTab); + MOZ_ASSERT(newTab); + + auto destroyNewTabOnError = MakeScopeExit([&] { + if (!*aWindowIsNew || NS_FAILED(*aResult)) { + if (newTab) { + newTab->Destroy(); + } + } + }); + + // Content has requested that we open this new content window, so + // we must have an opener. + newTab->SetHasContentOpener(true); + + nsCOMPtr<nsIContent> frame; + if (thisTabParent) { + frame = do_QueryInterface(thisTabParent->GetOwnerElement()); + } + + nsCOMPtr<nsPIDOMWindowOuter> parent; + if (frame) { + parent = frame->OwnerDoc()->GetWindow(); + + // If our chrome window is in the process of closing, don't try to open a + // new tab in it. + if (parent && parent->Closed()) { + parent = nullptr; + } + } + + nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin; + if (thisTabParent) { + browserDOMWin = thisTabParent->GetBrowserDOMWindow(); + } + + // If we haven't found a chrome window to open in, just use the most recently + // opened one. + if (!parent) { + parent = nsContentUtils::GetMostRecentNonPBWindow(); + if (NS_WARN_IF(!parent)) { + *aResult = NS_ERROR_FAILURE; + return true; + } + + nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(parent); + if (rootChromeWin) { + rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin)); + } + } + + int32_t openLocation = + nsWindowWatcher::GetWindowOpenLocation(parent, aChromeFlags, aCalledFromJS, + aPositionSpecified, aSizeSpecified); + + MOZ_ASSERT(openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB || + openLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW); + + // Opening new tabs is the easy case... + if (openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB) { + if (NS_WARN_IF(!browserDOMWin)) { + *aResult = NS_ERROR_ABORT; + return true; + } + + bool isPrivate = false; + if (thisTabParent) { + nsCOMPtr<nsILoadContext> loadContext = thisTabParent->GetLoadContext(); + loadContext->GetUsePrivateBrowsing(&isPrivate); + } + + nsCOMPtr<nsIOpenURIInFrameParams> params = + new nsOpenURIInFrameParams(aOpenerOriginAttributes); + params->SetReferrer(NS_ConvertUTF8toUTF16(aBaseURI)); + params->SetIsPrivate(isPrivate); + + TabParent::AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad); + + nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner; + browserDOMWin->OpenURIInFrame(nullptr, params, + openLocation, + nsIBrowserDOMWindow::OPEN_NEW, + getter_AddRefs(frameLoaderOwner)); + if (!frameLoaderOwner) { + *aWindowIsNew = false; + } + + newTab->SwapFrameScriptsFrom(*aFrameScripts); + + RenderFrameParent* rfp = static_cast<RenderFrameParent*>(aRenderFrame); + if (!newTab->SetRenderFrame(rfp) || + !newTab->GetRenderFrameInfo(aTextureFactoryIdentifier, aLayersId)) { + *aResult = NS_ERROR_FAILURE; + } + + return true; + } + + TabParent::AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad); + + nsCOMPtr<nsPIWindowWatcher> pwwatch = + do_GetService(NS_WINDOWWATCHER_CONTRACTID, aResult); + + if (NS_WARN_IF(NS_FAILED(*aResult))) { + return true; + } + + nsCOMPtr<nsITabParent> newRemoteTab; + if (!thisTabParent) { + // Because we weren't passed an opener tab, the content process has asked us + // to open a new window that is unrelated to a pre-existing tab. + *aResult = pwwatch->OpenWindowWithoutParent(getter_AddRefs(newRemoteTab)); + } else { + *aResult = pwwatch->OpenWindowWithTabParent(thisTabParent, aFeatures, aCalledFromJS, + aFullZoom, getter_AddRefs(newRemoteTab)); + } + + if (NS_WARN_IF(NS_FAILED(*aResult))) { + return true; + } + + MOZ_ASSERT(TabParent::GetFrom(newRemoteTab) == newTab); + + newTab->SwapFrameScriptsFrom(*aFrameScripts); + + RenderFrameParent* rfp = static_cast<RenderFrameParent*>(aRenderFrame); + if (!newTab->SetRenderFrame(rfp) || + !newTab->GetRenderFrameInfo(aTextureFactoryIdentifier, aLayersId)) { + *aResult = NS_ERROR_FAILURE; + } + + return true; +} + +bool +ContentParent::RecvProfile(const nsCString& aProfile) +{ +#ifdef MOZ_ENABLE_PROFILER_SPS + if (NS_WARN_IF(!mGatherer)) { + return true; + } + mProfile = aProfile; + mGatherer->GatheredOOPProfile(); +#endif + return true; +} + +bool +ContentParent::RecvGetGraphicsDeviceInitData(ContentDeviceData* aOut) +{ + gfxPlatform::GetPlatform()->BuildContentDeviceData(aOut); + return true; +} + +bool +ContentParent::RecvGraphicsError(const nsCString& aError) +{ + gfx::LogForwarder* lf = gfx::Factory::GetLogForwarder(); + if (lf) { + std::stringstream message; + message << "CP+" << aError.get(); + lf->UpdateStringsVector(message.str()); + } + return true; +} + +bool +ContentParent::RecvBeginDriverCrashGuard(const uint32_t& aGuardType, bool* aOutCrashed) +{ + // Only one driver crash guard should be active at a time, per-process. + MOZ_ASSERT(!mDriverCrashGuard); + + UniquePtr<gfx::DriverCrashGuard> guard; + switch (gfx::CrashGuardType(aGuardType)) { + case gfx::CrashGuardType::D3D11Layers: + guard = MakeUnique<gfx::D3D11LayersCrashGuard>(this); + break; + case gfx::CrashGuardType::D3D9Video: + guard = MakeUnique<gfx::D3D9VideoCrashGuard>(this); + break; + case gfx::CrashGuardType::GLContext: + guard = MakeUnique<gfx::GLContextCrashGuard>(this); + break; + case gfx::CrashGuardType::D3D11Video: + guard = MakeUnique<gfx::D3D11VideoCrashGuard>(this); + break; + default: + MOZ_ASSERT_UNREACHABLE("unknown crash guard type"); + return false; + } + + if (guard->Crashed()) { + *aOutCrashed = true; + return true; + } + + *aOutCrashed = false; + mDriverCrashGuard = Move(guard); + return true; +} + +bool +ContentParent::RecvEndDriverCrashGuard(const uint32_t& aGuardType) +{ + mDriverCrashGuard = nullptr; + return true; +} + +bool +ContentParent::RecvGetAndroidSystemInfo(AndroidSystemInfo* aInfo) +{ +#ifdef MOZ_WIDGET_ANDROID + nsSystemInfo::GetAndroidSystemInfo(aInfo); + return true; +#else + MOZ_CRASH("wrong platform!"); + return false; +#endif +} + +bool +ContentParent::RecvNotifyBenchmarkResult(const nsString& aCodecName, + const uint32_t& aDecodeFPS) + +{ + if (aCodecName.EqualsLiteral("VP9")) { + Preferences::SetUint(VP9Benchmark::sBenchmarkFpsPref, aDecodeFPS); + Preferences::SetUint(VP9Benchmark::sBenchmarkFpsVersionCheck, + VP9Benchmark::sBenchmarkVersionID); + } + return true; +} + +void +ContentParent::StartProfiler(nsIProfilerStartParams* aParams) +{ +#ifdef MOZ_ENABLE_PROFILER_SPS + if (NS_WARN_IF(!aParams)) { + return; + } + + ProfilerInitParams ipcParams; + + ipcParams.enabled() = true; + aParams->GetEntries(&ipcParams.entries()); + aParams->GetInterval(&ipcParams.interval()); + ipcParams.features() = aParams->GetFeatures(); + ipcParams.threadFilters() = aParams->GetThreadFilterNames(); + + Unused << SendStartProfiler(ipcParams); + + nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1")); + if (NS_WARN_IF(!profiler)) { + return; + } + nsCOMPtr<nsISupports> gatherer; + profiler->GetProfileGatherer(getter_AddRefs(gatherer)); + mGatherer = static_cast<ProfileGatherer*>(gatherer.get()); +#endif +} + +bool +ContentParent::RecvNotifyPushObservers(const nsCString& aScope, + const IPC::Principal& aPrincipal, + const nsString& aMessageId) +{ + PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing()); + Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers())); + return true; +} + +bool +ContentParent::RecvNotifyPushObserversWithData(const nsCString& aScope, + const IPC::Principal& aPrincipal, + const nsString& aMessageId, + InfallibleTArray<uint8_t>&& aData) +{ + PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Some(aData)); + Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers())); + return true; +} + +bool +ContentParent::RecvNotifyPushSubscriptionChangeObservers(const nsCString& aScope, + const IPC::Principal& aPrincipal) +{ + PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal); + Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers())); + return true; +} + +bool +ContentParent::RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aScope, + const IPC::Principal& aPrincipal) +{ + PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal); + Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers())); + return true; +} + +bool +ContentParent::RecvNotifyLowMemory() +{ +#ifdef MOZ_CRASHREPORTER + nsThread::SaveMemoryReportNearOOM(nsThread::ShouldSaveMemoryReport::kForceReport); +#endif + return true; +} + +/* static */ void +ContentParent::BroadcastBlobURLRegistration(const nsACString& aURI, + BlobImpl* aBlobImpl, + nsIPrincipal* aPrincipal, + ContentParent* aIgnoreThisCP) +{ + nsCString uri(aURI); + IPC::Principal principal(aPrincipal); + + for (auto* cp : AllProcesses(eLive)) { + if (cp != aIgnoreThisCP) { + PBlobParent* blobParent = cp->GetOrCreateActorForBlobImpl(aBlobImpl); + if (blobParent) { + Unused << cp->SendBlobURLRegistration(uri, blobParent, principal); + } + } + } +} + +/* static */ void +ContentParent::BroadcastBlobURLUnregistration(const nsACString& aURI, + ContentParent* aIgnoreThisCP) +{ + nsCString uri(aURI); + + for (auto* cp : AllProcesses(eLive)) { + if (cp != aIgnoreThisCP) { + Unused << cp->SendBlobURLUnregistration(uri); + } + } +} + +bool +ContentParent::RecvStoreAndBroadcastBlobURLRegistration(const nsCString& aURI, + PBlobParent* aBlobParent, + const Principal& aPrincipal) +{ + RefPtr<BlobImpl> blobImpl = + static_cast<BlobParent*>(aBlobParent)->GetBlobImpl(); + if (NS_WARN_IF(!blobImpl)) { + return false; + } + + if (NS_SUCCEEDED(nsHostObjectProtocolHandler::AddDataEntry(aURI, aPrincipal, + blobImpl))) { + BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, this); + + // We want to store this blobURL, so we can unregister it if the child + // crashes. + mBlobURLs.AppendElement(aURI); + } + + BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, this); + return true; +} + +bool +ContentParent::RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI) +{ + nsHostObjectProtocolHandler::RemoveDataEntry(aURI, + false /* Don't broadcast */); + BroadcastBlobURLUnregistration(aURI, this); + mBlobURLs.RemoveElement(aURI); + + return true; +} + +bool +ContentParent::RecvGetA11yContentId(uint32_t* aContentId) +{ +#if defined(XP_WIN32) && defined(ACCESSIBILITY) + *aContentId = a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()); + MOZ_ASSERT(*aContentId); + return true; +#else + return false; +#endif +} + +} // namespace dom +} // namespace mozilla + +NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver) + +NS_IMETHODIMP +ParentIdleListener::Observe(nsISupports*, const char* aTopic, const char16_t* aData) +{ + mozilla::Unused << mParent->SendNotifyIdleObserver(mObserver, + nsDependentCString(aTopic), + nsDependentString(aData)); + return NS_OK; +} + +bool +ContentParent::HandleWindowsMessages(const Message& aMsg) const +{ + MOZ_ASSERT(aMsg.is_sync()); + + // a11y messages can be triggered by windows messages, which means if we + // allow handling windows messages while we wait for the response to a sync + // a11y message we can reenter the ipc message sending code. + if (a11y::PDocAccessible::PDocAccessibleStart < aMsg.type() && + a11y::PDocAccessible::PDocAccessibleEnd > aMsg.type()) { + return false; + } + + return true; +} + +bool +ContentParent::RecvGetFilesRequest(const nsID& aUUID, + const nsString& aDirectoryPath, + const bool& aRecursiveFlag) +{ + MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aUUID)); + + ErrorResult rv; + RefPtr<GetFilesHelper> helper = + GetFilesHelperParent::Create(aUUID, aDirectoryPath, aRecursiveFlag, this, + rv); + + if (NS_WARN_IF(rv.Failed())) { + return SendGetFilesResponse(aUUID, + GetFilesResponseFailure(rv.StealNSResult())); + } + + mGetFilesPendingRequests.Put(aUUID, helper); + return true; +} + +bool +ContentParent::RecvDeleteGetFilesRequest(const nsID& aUUID) +{ + GetFilesHelper* helper = mGetFilesPendingRequests.GetWeak(aUUID); + if (helper) { + mGetFilesPendingRequests.Remove(aUUID); + } + + return true; +} + +void +ContentParent::SendGetFilesResponseAndForget(const nsID& aUUID, + const GetFilesResponseResult& aResult) +{ + GetFilesHelper* helper = mGetFilesPendingRequests.GetWeak(aUUID); + if (helper) { + mGetFilesPendingRequests.Remove(aUUID); + Unused << SendGetFilesResponse(aUUID, aResult); + } +} + +void +ContentParent::ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch) +{ + if (!mHangMonitorActor) { + return; + } + ProcessHangMonitor::ForcePaint(mHangMonitorActor, aTabParent, aLayerObserverEpoch); +} + +bool +ContentParent::RecvAccumulateChildHistogram( + InfallibleTArray<Accumulation>&& aAccumulations) +{ + Telemetry::AccumulateChild(GeckoProcessType_Content, aAccumulations); + return true; +} + +bool +ContentParent::RecvAccumulateChildKeyedHistogram( + InfallibleTArray<KeyedAccumulation>&& aAccumulations) +{ + Telemetry::AccumulateChildKeyed(GeckoProcessType_Content, aAccumulations); + return true; +} |