diff -r 000000000000 -r 6474c204b198 dom/ipc/ContentParent.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dom/ipc/ContentParent.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,3449 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set sw=4 ts=8 et 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" + +#if defined(ANDROID) || defined(LINUX) +# include +# include +#endif + +#ifdef MOZ_WIDGET_GONK +#include +#include +#endif + +#include "chrome/common/process_watcher.h" + +#include "AppProcessChecker.h" +#include "AudioChannelService.h" +#include "CrashReporterParent.h" +#include "IHistory.h" +#include "IDBFactory.h" +#include "IndexedDBParent.h" +#include "IndexedDatabaseManager.h" +#include "mozIApplication.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/dom/asmjscache/AsmJSCache.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/ExternalHelperAppParent.h" +#include "mozilla/dom/PFileDescriptorSetParent.h" +#include "mozilla/dom/PMemoryReportRequestParent.h" +#include "mozilla/dom/power/PowerManagerService.h" +#include "mozilla/dom/DOMStorageIPC.h" +#include "mozilla/dom/bluetooth/PBluetoothParent.h" +#include "mozilla/dom/PFMRadioParent.h" +#include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h" +#include "mozilla/dom/FileSystemRequestParent.h" +#include "mozilla/dom/GeolocationBinding.h" +#include "mozilla/dom/FileDescriptorSetParent.h" +#include "mozilla/dom/telephony/TelephonyParent.h" +#include "mozilla/dom/time/DateCacheCleaner.h" +#include "SmsParent.h" +#include "mozilla/hal_sandbox/PHalParent.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/BackgroundParent.h" +#include "mozilla/ipc/TestShellParent.h" +#include "mozilla/ipc/InputStreamUtils.h" +#include "mozilla/layers/CompositorParent.h" +#include "mozilla/layers/ImageBridgeParent.h" +#include "mozilla/net/NeckoParent.h" +#include "mozilla/Preferences.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/unused.h" +#include "nsAppRunner.h" +#include "nsAutoPtr.h" +#include "nsCDefaultURIFixup.h" +#include "nsCExternalHandlerService.h" +#include "nsCOMPtr.h" +#include "nsChromeRegistryChrome.h" +#include "nsConsoleMessage.h" +#include "nsConsoleService.h" +#include "nsDebugImpl.h" +#include "nsDOMFile.h" +#include "nsFrameMessageManager.h" +#include "nsHashPropertyBag.h" +#include "nsIAlertsService.h" +#include "nsIAppsService.h" +#include "nsIClipboard.h" +#include "nsIDOMGeoGeolocation.h" +#include "mozilla/dom/WakeLock.h" +#include "nsIDOMWindow.h" +#include "nsIExternalProtocolService.h" +#include "nsIGfxInfo.h" +#include "nsIIdleService.h" +#include "nsIMemoryReporter.h" +#include "nsIMozBrowserFrame.h" +#include "nsIMutable.h" +#include "nsIObserverService.h" +#include "nsIPresShell.h" +#include "nsIRemoteBlob.h" +#include "nsIScriptError.h" +#include "nsIStyleSheet.h" +#include "nsISupportsPrimitives.h" +#include "nsIURIFixup.h" +#include "nsIWindowWatcher.h" +#include "nsIXULRuntime.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 "StructuredCloneUtils.h" +#include "TabParent.h" +#include "URIUtils.h" +#include "nsIWebBrowserChrome.h" +#include "nsIDocShell.h" +#include "mozilla/net/NeckoMessageUtils.h" +#include "gfxPrefs.h" + +#if defined(ANDROID) || defined(LINUX) +#include "nsSystemInfo.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 "nsIVolumeService.h" +#include "SpeakerManagerService.h" +using namespace mozilla::system; +#endif + +#ifdef MOZ_B2G_BT +#include "BluetoothParent.h" +#include "BluetoothService.h" +#endif + +#include "JavaScriptParent.h" + +#ifdef MOZ_B2G_FM +#include "mozilla/dom/FMRadioParent.h" +#endif + +#include "Crypto.h" + +#ifdef MOZ_WEBSPEECH +#include "mozilla/dom/SpeechSynthesisParent.h" +#endif + +#ifdef ENABLE_TESTS +#include "BackgroundChildImpl.h" +#include "mozilla/ipc/PBackgroundChild.h" +#include "nsIIPCBackgroundChildCreateCallback.h" +#endif + +static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); +static const char* sClipboardTextFlavors[] = { kUnicodeMime }; + +using base::ChildPrivileges; +using base::KillProcess; +using namespace mozilla::dom::bluetooth; +using namespace mozilla::dom::devicestorage; +using namespace mozilla::dom::indexedDB; +using namespace mozilla::dom::power; +using namespace mozilla::dom::mobilemessage; +using namespace mozilla::dom::telephony; +using namespace mozilla::hal; +using namespace mozilla::ipc; +using namespace mozilla::layers; +using namespace mozilla::net; +using namespace mozilla::jsipc; + +#ifdef ENABLE_TESTS + +class BackgroundTester MOZ_FINAL : public nsIIPCBackgroundChildCreateCallback, + public nsIObserver +{ + static uint32_t sCallbackCount; + +private: + ~BackgroundTester() + { } + + virtual void + ActorCreated(PBackgroundChild* aActor) MOZ_OVERRIDE + { + MOZ_RELEASE_ASSERT(aActor, + "Failed to create a PBackgroundChild actor!"); + + NS_NAMED_LITERAL_CSTRING(testStr, "0123456789"); + + PBackgroundTestChild* testActor = + aActor->SendPBackgroundTestConstructor(testStr); + MOZ_RELEASE_ASSERT(testActor); + + if (!sCallbackCount) { + PBackgroundChild* existingBackgroundChild = + BackgroundChild::GetForCurrentThread(); + + MOZ_RELEASE_ASSERT(existingBackgroundChild); + MOZ_RELEASE_ASSERT(existingBackgroundChild == aActor); + + bool ok = + existingBackgroundChild-> + SendPBackgroundTestConstructor(testStr); + MOZ_RELEASE_ASSERT(ok); + + // Callback 3. + ok = BackgroundChild::GetOrCreateForCurrentThread(this); + MOZ_RELEASE_ASSERT(ok); + } + + sCallbackCount++; + } + + virtual void + ActorFailed() MOZ_OVERRIDE + { + MOZ_CRASH("Failed to create a PBackgroundChild actor!"); + } + + NS_IMETHOD + Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) + MOZ_OVERRIDE + { + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + MOZ_RELEASE_ASSERT(observerService); + + nsresult rv = observerService->RemoveObserver(this, aTopic); + MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); + + if (!strcmp(aTopic, "profile-after-change")) { + if (mozilla::Preferences::GetBool("pbackground.testing", false)) { + rv = observerService->AddObserver(this, "xpcom-shutdown", + false); + MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); + + // Callback 1. + bool ok = BackgroundChild::GetOrCreateForCurrentThread(this); + MOZ_RELEASE_ASSERT(ok); + + BackgroundChildImpl::ThreadLocal* threadLocal = + BackgroundChildImpl::GetThreadLocalForCurrentThread(); + MOZ_RELEASE_ASSERT(threadLocal); + + // Callback 2. + ok = BackgroundChild::GetOrCreateForCurrentThread(this); + MOZ_RELEASE_ASSERT(ok); + } + + return NS_OK; + } + + if (!strcmp(aTopic, "xpcom-shutdown")) { + MOZ_RELEASE_ASSERT(sCallbackCount == 3); + + return NS_OK; + } + + MOZ_CRASH("Unknown observer topic!"); + } + +public: + NS_DECL_ISUPPORTS +}; + +uint32_t BackgroundTester::sCallbackCount = 0; + +NS_IMPL_ISUPPORTS(BackgroundTester, nsIIPCBackgroundChildCreateCallback, + nsIObserver) + +#endif // ENABLE_TESTS + +void +MaybeTestPBackground() +{ +#ifdef ENABLE_TESTS + // This test relies on running the event loop and XPCShell does not always + // do so. Bail out here if we detect that we're running in XPCShell. + if (PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) { + return; + } + + // This is called too early at startup to test preferences directly. We have + // to install an observer to be notified when preferences are available. + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + MOZ_RELEASE_ASSERT(observerService); + + nsCOMPtr observer = new BackgroundTester(); + nsresult rv = observerService->AddObserver(observer, "profile-after-change", + false); + MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); +#endif +} + +namespace mozilla { +namespace dom { + +#define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline" + +class MemoryReportRequestParent : public PMemoryReportRequestParent +{ +public: + MemoryReportRequestParent(); + virtual ~MemoryReportRequestParent(); + + virtual bool Recv__delete__(const uint32_t& generation, const InfallibleTArray& report); +private: + ContentParent* Owner() + { + return static_cast(Manager()); + } +}; + +MemoryReportRequestParent::MemoryReportRequestParent() +{ + MOZ_COUNT_CTOR(MemoryReportRequestParent); +} + +bool +MemoryReportRequestParent::Recv__delete__(const uint32_t& generation, const InfallibleTArray& childReports) +{ + nsRefPtr mgr = + nsMemoryReporterManager::GetOrCreate(); + if (mgr) { + mgr->HandleChildReports(generation, childReports); + } + return true; +} + +MemoryReportRequestParent::~MemoryReportRequestParent() +{ + MOZ_COUNT_DTOR(MemoryReportRequestParent); +} + +// A memory reporter for ContentParent objects themselves. +class ContentParentsMemoryReporter MOZ_FINAL : public nsIMemoryReporter +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIMEMORYREPORTER +}; + +NS_IMPL_ISUPPORTS(ContentParentsMemoryReporter, nsIMemoryReporter) + +NS_IMETHODIMP +ContentParentsMemoryReporter::CollectReports(nsIMemoryReporterCallback* cb, + nsISupports* aClosure) +{ + nsAutoTArray 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); + + 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, 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."); + + nsresult rv = cb->Callback(/* process */ EmptyCString(), + path, + KIND_OTHER, + UNITS_COUNT, + numQueuedMessages, + desc, + aClosure); + + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +nsDataHashtable* ContentParent::sAppContentParents; +nsTArray* ContentParent::sNonAppContentParents; +nsTArray* ContentParent::sPrivateContent; +StaticAutoPtr > ContentParent::sContentParents; + +// This is true when subprocess launching is enabled. This is the +// case between StartUp() and ShutDown() or JoinAllSubprocesses(). +static bool sCanLaunchSubprocesses; + +// 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", + NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC, + "child-memory-reporter-request", + "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, + "phone-state-changed", +#endif +#ifdef ACCESSIBILITY + "a11y-init-or-shutdown", +#endif +}; + +/* static */ already_AddRefed +ContentParent::RunNuwaProcess() +{ + MOZ_ASSERT(NS_IsMainThread()); + nsRefPtr nuwaProcess = + new ContentParent(/* aApp = */ nullptr, + /* aIsForBrowser = */ false, + /* aIsForPreallocated = */ true, + PROCESS_PRIORITY_BACKGROUND, + /* aIsNuwaProcess = */ true); + nuwaProcess->Init(); + return nuwaProcess.forget(); +} + +// PreallocateAppProcess is called by the PreallocatedProcessManager. +// ContentParent then takes this process back within +// MaybeTakePreallocatedAppProcess. +/*static*/ already_AddRefed +ContentParent::PreallocateAppProcess() +{ + nsRefPtr process = + new ContentParent(/* app = */ nullptr, + /* isForBrowserElement = */ false, + /* isForPreallocated = */ true, + PROCESS_PRIORITY_PREALLOC); + process->Init(); + return process.forget(); +} + +/*static*/ already_AddRefed +ContentParent::MaybeTakePreallocatedAppProcess(const nsAString& aAppManifestURL, + ProcessPriority aInitialPriority) +{ + nsRefPtr process = PreallocatedProcessManager::Take(); + if (!process) { + return nullptr; + } + + 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(); + return nullptr; + } + process->TransformPreallocatedIntoApp(aAppManifestURL); + + return process.forget(); +} + +/*static*/ void +ContentParent::StartUp() +{ + if (XRE_GetProcessType() != GeckoProcessType_Default) { + return; + } + + // Note: This reporter measures all ContentParents. + RegisterStrongMemoryReporter(new ContentParentsMemoryReporter()); + + mozilla::dom::time::InitializeDateCacheCleaner(); + + BackgroundChild::Startup(); + + sCanLaunchSubprocesses = true; + + // Try to preallocate a process that we can transform into an app later. + PreallocatedProcessManager::AllocateAfterDelay(); + + // Test the PBackground infrastructure on ENABLE_TESTS builds when a special + // testing preference is set. + MaybeTestPBackground(); +} + +/*static*/ void +ContentParent::ShutDown() +{ + // No-op for now. We rely on normal process shutdown and + // ClearOnShutdown() to clean up our state. + sCanLaunchSubprocesses = false; +} + +/*static*/ void +ContentParent::JoinProcessesIOThread(const nsTArray* aProcesses, + Monitor* aMonitor, bool* aDone) +{ + const nsTArray& 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()); + + nsAutoTArray 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(FROM_HERE, + NewRunnableFunction( + &ContentParent::JoinProcessesIOThread, + &processes, &monitor, &done)); + { + MonitorAutoLock lock(monitor); + while (!done) { + lock.Wait(); + } + } + + sCanLaunchSubprocesses = false; +} + +/*static*/ already_AddRefed +ContentParent::GetNewOrUsed(bool aForBrowserElement) +{ + if (!sNonAppContentParents) + sNonAppContentParents = new nsTArray(); + + int32_t maxContentProcesses = Preferences::GetInt("dom.ipc.processCount", 1); + if (maxContentProcesses < 1) + maxContentProcesses = 1; + + if (sNonAppContentParents->Length() >= uint32_t(maxContentProcesses)) { + uint32_t idx = rand() % sNonAppContentParents->Length(); + nsRefPtr p = (*sNonAppContentParents)[idx]; + NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sNonAppContentParents?"); + return p.forget(); + } + + // Try to take and transform the preallocated process into browser. + nsRefPtr p = PreallocatedProcessManager::Take(); + if (p) { + p->TransformPreallocatedIntoBrowser(); + } else { + // Failed in using the preallocated process: fork from the chrome process. +#ifdef MOZ_NUWA_PROCESS + if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled", false)) { + // Wait until the Nuwa process forks a new process. + return nullptr; + } +#endif + p = new ContentParent(/* app = */ nullptr, + aForBrowserElement, + /* isForPreallocated = */ false, + PROCESS_PRIORITY_FOREGROUND); + } + + p->Init(); + sNonAppContentParents->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("keyboard"), eCaseMatters)) { + return PROCESS_PRIORITY_FOREGROUND_KEYBOARD; + } else if (!aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype, + NS_LITERAL_STRING("critical"), eCaseMatters)) { + return PROCESS_PRIORITY_FOREGROUND; + } + + nsCOMPtr browserFrame = + do_QueryInterface(aFrameElement); + if (!browserFrame) { + return PROCESS_PRIORITY_FOREGROUND; + } + + return browserFrame->GetIsExpectingSystemMessage() ? + PROCESS_PRIORITY_FOREGROUND_HIGH : + PROCESS_PRIORITY_FOREGROUND; +} + +bool +ContentParent::PreallocatedProcessReady() +{ +#ifdef MOZ_NUWA_PROCESS + return PreallocatedProcessManager::PreallocatedProcessReady(); +#else + return true; +#endif +} + +void +ContentParent::RunAfterPreallocatedProcessReady(nsIRunnable* aRequest) +{ +#ifdef MOZ_NUWA_PROCESS + PreallocatedProcessManager::RunAfterPreallocatedProcessReady(aRequest); +#endif +} + +/*static*/ TabParent* +ContentParent::CreateBrowserOrApp(const TabContext& aContext, + Element* aFrameElement) +{ + if (!sCanLaunchSubprocesses) { + return nullptr; + } + + if (aContext.IsBrowserElement() || !aContext.HasOwnApp()) { + if (nsRefPtr cp = GetNewOrUsed(aContext.IsBrowserElement())) { + uint32_t chromeFlags = 0; + + // Propagate the private-browsing status of the element's parent + // docshell to the remote docshell, via the chrome flags. + nsCOMPtr frameElement = do_QueryInterface(aFrameElement); + MOZ_ASSERT(frameElement); + nsIDocShell* docShell = + frameElement->OwnerDoc()->GetWindow()->GetDocShell(); + MOZ_ASSERT(docShell); + nsCOMPtr loadContext = do_QueryInterface(docShell); + if (loadContext && loadContext->UsePrivateBrowsing()) { + chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW; + } + bool affectLifetime; + docShell->GetAffectPrivateSessionLifetime(&affectLifetime); + if (affectLifetime) { + chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME; + } + + nsRefPtr tp(new TabParent(cp, aContext, chromeFlags)); + tp->SetOwnerElement(aFrameElement); + + PBrowserParent* browser = cp->SendPBrowserConstructor( + // DeallocPBrowserParent() releases this ref. + tp.forget().take(), + aContext.AsIPCTabContext(), + chromeFlags); + return static_cast(browser); + } + 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. + nsCOMPtr ownApp = aContext.GetOwnApp(); + + if (!sAppContentParents) { + sAppContentParents = + new nsDataHashtable(); + } + + // Each app gets its own ContentParent instance unless it shares it with + // a parent app. + nsAutoString manifestURL; + if (NS_FAILED(ownApp->GetManifestURL(manifestURL))) { + NS_ERROR("Failed to get manifest URL"); + return nullptr; + } + + ProcessPriority initialPriority = GetInitialProcessPriority(aFrameElement); + nsRefPtr p = sAppContentParents->Get(manifestURL); + + if (!p && Preferences::GetBool("dom.ipc.reuse_parent_app")) { + nsAutoString parentAppURL; + aFrameElement->GetAttr(kNameSpaceID_None, + nsGkAtoms::parentapp, parentAppURL); + nsAdoptingString systemAppURL = + Preferences::GetString("browser.homescreenURL"); + nsCOMPtr appsService = + do_GetService(APPS_SERVICE_CONTRACTID); + if (!parentAppURL.IsEmpty() && + !parentAppURL.Equals(systemAppURL) && + appsService) { + nsCOMPtr parentApp; + nsCOMPtr app; + appsService->GetAppByManifestURL(parentAppURL, + 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(parentAppURL); + } + } + } + + 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; + } + } + + if (!p) { + p = MaybeTakePreallocatedAppProcess(manifestURL, + initialPriority); + if (!p) { +#ifdef MOZ_NUWA_PROCESS + if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled", + false)) { + // Returning nullptr from here so the frame loader will retry + // later when we have a spare process. + return nullptr; + } +#endif + NS_WARNING("Unable to use pre-allocated app process"); + p = new ContentParent(ownApp, + /* isForBrowserElement = */ false, + /* isForPreallocated = */ false, + initialPriority); + p->Init(); + } + sAppContentParents->Put(manifestURL, p); + } + + uint32_t chromeFlags = 0; + + nsRefPtr tp = new TabParent(p, aContext, chromeFlags); + tp->SetOwnerElement(aFrameElement); + PBrowserParent* browser = p->SendPBrowserConstructor( + // DeallocPBrowserParent() releases this ref. + nsRefPtr(tp).forget().take(), + aContext.AsIPCTabContext(), + chromeFlags); + + p->MaybeTakeCPUWakeLock(aFrameElement); + + return static_cast(browser); +} + +void +ContentParent::GetAll(nsTArray& aArray) +{ + aArray.Clear(); + + if (!sContentParents) { + return; + } + + for (ContentParent* cp = sContentParents->getFirst(); cp; + cp = cp->LinkedListElement::getNext()) { + if (cp->mIsAlive) { + aArray.AppendElement(cp); + } + } +} + +void +ContentParent::GetAllEvenIfDead(nsTArray& aArray) +{ + aArray.Clear(); + + if (!sContentParents) { + return; + } + + for (ContentParent* cp = sContentParents->getFirst(); cp; + cp = cp->LinkedListElement::getNext()) { + aArray.AppendElement(cp); + } +} + +void +ContentParent::Init() +{ + nsCOMPtr 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) { + obs->NotifyObservers(static_cast(this), "ipc:content-created", nullptr); + } + +#ifdef ACCESSIBILITY + // If accessibility is running in chrome process then start it in content + // process. + if (nsIPresShell::IsAccessibilityActive()) { + unused << SendActivateA11y(); + } +#endif + + DebugOnly observer = FileUpdateDispatcher::GetSingleton(); + NS_ASSERTION(observer, "FileUpdateDispatcher is null"); +} + +namespace { + +class SystemMessageHandledListener MOZ_FINAL + : public nsITimerCallback + , public LinkedListElement +{ +public: + NS_DECL_ISUPPORTS + + SystemMessageHandledListener() {} + + static void OnSystemMessageHandled() + { + if (!sListeners) { + return; + } + + SystemMessageHandledListener* listener = sListeners->popFirst(); + if (!listener) { + return; + } + + // Careful: ShutDown() may delete |this|. + listener->ShutDown(); + } + + void Init(WakeLock* aWakeLock) + { + MOZ_ASSERT(!mWakeLock); + MOZ_ASSERT(!mTimer); + + // mTimer keeps a strong reference to |this|. When this object's + // destructor runs, it will remove itself from the LinkedList. + + if (!sListeners) { + sListeners = new LinkedList(); + ClearOnShutdown(&sListeners); + } + sListeners->insertBack(this); + + mWakeLock = aWakeLock; + + mTimer = do_CreateInstance("@mozilla.org/timer;1"); + + uint32_t timeoutSec = + Preferences::GetInt("dom.ipc.systemMessageCPULockTimeoutSec", 30); + mTimer->InitWithCallback(this, timeoutSec * 1000, + nsITimer::TYPE_ONE_SHOT); + } + + NS_IMETHOD Notify(nsITimer* aTimer) + { + // Careful: ShutDown() may delete |this|. + ShutDown(); + return NS_OK; + } + +private: + static StaticAutoPtr > sListeners; + + void ShutDown() + { + nsRefPtr kungFuDeathGrip = this; + + ErrorResult rv; + mWakeLock->Unlock(rv); + + if (mTimer) { + mTimer->Cancel(); + mTimer = nullptr; + } + } + + nsRefPtr mWakeLock; + nsCOMPtr mTimer; +}; + +StaticAutoPtr > + SystemMessageHandledListener::sListeners; + +NS_IMPL_ISUPPORTS(SystemMessageHandledListener, + nsITimerCallback) + +} // anonymous namespace + +void +ContentParent::MaybeTakeCPUWakeLock(Element* aFrameElement) +{ + // Take the CPU wake lock on behalf of this processs if it's expecting a + // system message. We'll release the CPU lock once the message is + // delivered, or after some period of time, which ever comes first. + + nsCOMPtr browserFrame = + do_QueryInterface(aFrameElement); + if (!browserFrame || + !browserFrame->GetIsExpectingSystemMessage()) { + return; + } + + nsRefPtr pms = PowerManagerService::GetInstance(); + nsRefPtr lock = + pms->NewWakeLockOnBehalfOfProcess(NS_LITERAL_STRING("cpu"), this); + + // This object's Init() function keeps it alive. + nsRefPtr listener = + new SystemMessageHandledListener(); + listener->Init(lock); +} + +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. Also, if the process isn't a + // direct child because of Nuwa this will fail with ECHILD, and we + // need to assume the child is alive in that case rather than + // assuming it's dead (as is otherwise a reasonable fallback). +#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 appsService = do_GetService(APPS_SERVICE_CONTRACTID); + NS_ENSURE_TRUE_VOID(appsService); + + nsCOMPtr app; + appsService->GetAppByManifestURL(aManifestURL, getter_AddRefs(app)); + + if (!app) { + return; + } + + app->GetName(aName); +} + +void +ContentParent::TransformPreallocatedIntoApp(const nsAString& aAppManifestURL) +{ + MOZ_ASSERT(IsPreallocated()); + mAppManifestURL = aAppManifestURL; + TryGetNameFromManifestURL(aAppManifestURL, mAppName); +} + +void +ContentParent::TransformPreallocatedIntoBrowser() +{ + // Reset mAppManifestURL, mIsForBrowser and mOSPrivileges for browser. + mAppManifestURL.Truncate(); + mIsForBrowser = true; +} + +void +ContentParent::ShutDownProcess(bool aCloseWithError) +{ + const InfallibleTArray& idbParents = + ManagedPIndexedDBParent(); + for (uint32_t i = 0; i < idbParents.Length(); ++i) { + static_cast(idbParents[i])->Disconnect(); + } + + // If Close() fails with an error, we'll end up back in this function, but + // with aCloseWithError = true. It's important that we call + // CloseWithError() in this case; see bug 895204. + + if (!aCloseWithError && !mCalledClose) { + // Close() can only be called once: It kicks off the destruction + // sequence. + mCalledClose = true; + Close(); + } + + if (aCloseWithError && !mCalledCloseWithError) { + MessageChannel* channel = GetIPCChannel(); + if (channel) { + mCalledCloseWithError = true; + channel->CloseWithError(); + } + } + + // 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. + if (mMessageManager) { + 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 (sPrivateContent) { + sPrivateContent->RemoveElement(this); + if (!sPrivateContent->Length()) { + delete sPrivateContent; + sPrivateContent = nullptr; + } + } + + mIsAlive = false; +} + +void +ContentParent::OnChannelError() +{ + nsRefPtr content(this); +#ifdef MOZ_NUWA_PROCESS + // Handle app or Nuwa process exit before normal channel error handling. + PreallocatedProcessManager::MaybeForgetSpare(this); +#endif + PContentParent::OnChannelError(); +} + +void +ContentParent::OnChannelConnected(int32_t pid) +{ + ProcessHandle handle; + if (!base::OpenPrivilegedProcessHandle(pid, &handle)) { + NS_WARNING("Can't open handle to child process."); + } + else { + // we need to close the existing handle before setting a new one. + base::CloseProcessHandle(OtherProcess()); + SetOtherProcess(handle); + +#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 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 + } + + // Set a reply timeout. The only time the parent process will actually + // timeout is through urgent messages (which are used by CPOWs). + SetReplyTimeoutMs(Preferences::GetInt("dom.ipc.cpow.timeout", 3000)); +} + +void +ContentParent::ProcessingError(Result what) +{ + if (MsgDropped == what) { + // Messages sent after crashes etc. are not a big deal. + return; + } + // Other errors are big deals. + KillHard(); +} + +namespace { + +void +DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess) +{ + XRE_GetIOMessageLoop() + ->PostTask(FROM_HERE, + new DeleteTask(aSubprocess)); +} + +// This runnable only exists to delegate ownership of the +// ContentParent to this runnable, until it's deleted by the event +// system. +struct DelayedDeleteContentParentTask : public nsRunnable +{ + DelayedDeleteContentParentTask(ContentParent* aObj) : mObj(aObj) { } + + // No-op + NS_IMETHODIMP Run() { return NS_OK; } + + nsRefPtr mObj; +}; + +} + +void +ContentParent::ActorDestroy(ActorDestroyReason why) +{ + if (mForceKillTask) { + mForceKillTask->Cancel(); + mForceKillTask = nullptr; + } + + nsRefPtr ppm = mMessageManager; + if (ppm) { + ppm->ReceiveMessage(static_cast(ppm.get()), + CHILD_PROCESS_SHUTDOWN_MESSAGE, false, + nullptr, nullptr, nullptr, nullptr); + } + nsRefPtr kungFuDeathGrip(this); + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + size_t length = ArrayLength(sObserverTopics); + for (size_t i = 0; i < length; ++i) { + obs->RemoveObserver(static_cast(this), + sObserverTopics[i]); + } + } + + if (ppm) { + ppm->Disconnect(); + } + + // Tell the memory reporter manager that this ContentParent is going away. + nsRefPtr mgr = + nsMemoryReporterManager::GetOrCreate(); +#ifdef MOZ_NUWA_PROCESS + bool isMemoryChild = !IsNuwaProcess(); +#else + bool isMemoryChild = true; +#endif + if (mgr && isMemoryChild) { + mgr->DecrementNumChildProcesses(); + } + + // remove the global remote preferences observers + Preferences::RemoveObserver(this, ""); + + RecvRemoveGeolocationListener(); + + mConsoleService = nullptr; + + MarkAsDead(); + + if (obs) { + nsRefPtr props = new nsHashPropertyBag(); + + props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), mChildID); + + if (AbnormalShutdown == why) { + 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 (ManagedPCrashReporterParent().Length() > 0) { + CrashReporterParent* crashReporter = + static_cast(ManagedPCrashReporterParent()[0]); + + // 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)); + } + + crashReporter->GenerateCrashReport(this, nullptr); + + nsAutoString dumpID(crashReporter->ChildDumpID()); + props->SetPropertyAsAString(NS_LITERAL_STRING("dumpID"), dumpID); + } +#endif + } + obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", nullptr); + } + + mIdleListeners.Clear(); + + // If the child process was terminated due to a SIGKIL, ShutDownProcess + // might not have been called yet. We must call it to ensure that our + // channel is closed, etc. + ShutDownProcess(/* closeWithError */ true); + + MessageLoop::current()-> + PostTask(FROM_HERE, + 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)); +} + +void +ContentParent::NotifyTabDestroying(PBrowserParent* aTab) +{ + // 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. + int32_t numLiveTabs = ManagedPBrowserParent().Length(); + ++mNumDestroyingTabs; + if (mNumDestroyingTabs != numLiveTabs) { + return; + } + + // We're dying now, so prevent this content process from being + // recycled during its shutdown procedure. + MarkAsDead(); + + MOZ_ASSERT(!mForceKillTask); + int32_t timeoutSecs = + Preferences::GetInt("dom.ipc.tabs.shutdownTimeoutSecs", 5); + if (timeoutSecs > 0) { + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + mForceKillTask = NewRunnableMethod(this, &ContentParent::KillHard), + timeoutSecs * 1000); + } +} + +void +ContentParent::NotifyTabDestroyed(PBrowserParent* aTab, + bool aNotifiedDestroying) +{ + if (aNotifiedDestroying) { + --mNumDestroyingTabs; + } + + // There can be more than one PBrowser for a given app process + // because of popup windows. When the last one closes, shut + // us down. + if (ManagedPBrowserParent().Length() == 1) { + MessageLoop::current()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &ContentParent::ShutDownProcess, + /* force */ false)); + } +} + +jsipc::JavaScriptParent* +ContentParent::GetCPOWManager() +{ + if (ManagedPJavaScriptParent().Length()) { + return static_cast(ManagedPJavaScriptParent()[0]); + } + JavaScriptParent* actor = static_cast(SendPJavaScriptConstructor()); + return actor; +} + +TestShellParent* +ContentParent::CreateTestShell() +{ + return static_cast(SendPTestShellConstructor()); +} + +bool +ContentParent::DestroyTestShell(TestShellParent* aTestShell) +{ + return PTestShellParent::Send__delete__(aTestShell); +} + +TestShellParent* +ContentParent::GetTestShellSingleton() +{ + if (!ManagedPTestShellParent().Length()) + return nullptr; + return static_cast(ManagedPTestShellParent()[0]); +} + +void +ContentParent::InitializeMembers() +{ + mSubprocess = nullptr; + mChildID = gContentChildID++; + mGeolocationWatchID = -1; + mForceKillTask = nullptr; + mNumDestroyingTabs = 0; + mIsAlive = true; + mSendPermissionUpdates = false; + mCalledClose = false; + mCalledCloseWithError = false; + mCalledKillHard = false; +} + +ContentParent::ContentParent(mozIApplication* aApp, + bool aIsForBrowser, + bool aIsForPreallocated, + ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */, + bool aIsNuwaProcess /* = false */) + : mIsForBrowser(aIsForBrowser) + , mIsNuwaProcess(aIsNuwaProcess) +{ + InitializeMembers(); // Perform common initialization. + + // No more than one of !!aApp, aIsForBrowser, aIsForPreallocated should be + // true. + MOZ_ASSERT(!!aApp + aIsForBrowser + aIsForPreallocated <= 1); + + // Only the preallocated process uses Nuwa. + MOZ_ASSERT_IF(aIsNuwaProcess, aIsForPreallocated); + + // Insert ourselves into the global linked list of ContentParent objects. + if (!sContentParents) { + sContentParents = new LinkedList(); + } + if (!aIsNuwaProcess) { + 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"); + + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + ChildPrivileges privs = aIsNuwaProcess + ? base::PRIVILEGES_INHERIT + : base::PRIVILEGES_DEFAULT; + mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, privs); + mSubprocess->SetSandboxEnabled(ShouldSandboxContentProcesses()); + + IToplevelProtocol::SetTransport(mSubprocess->GetChannel()); + + if (!aIsNuwaProcess) { + // Tell the memory reporter manager that this ContentParent exists. + nsRefPtr mgr = + nsMemoryReporterManager::GetOrCreate(); + if (mgr) { + mgr->IncrementNumChildProcesses(); + } + } + + std::vector extraArgs; + if (aIsNuwaProcess) { + extraArgs.push_back("-nuwa"); + } + mSubprocess->LaunchAndWaitForProcessHandle(extraArgs); + + Open(mSubprocess->GetChannel(), mSubprocess->GetOwnedChildProcessHandle()); + + InitInternal(aInitialPriority, + true, /* Setup off-main thread compositing */ + true /* Send registered chrome */); +} + +#ifdef MOZ_NUWA_PROCESS +static const mozilla::ipc::FileDescriptor* +FindFdProtocolFdMapping(const nsTArray& aFds, + ProtocolId aProtoId) +{ + for (unsigned int i = 0; i < aFds.Length(); i++) { + if (aFds[i].protocolId() == aProtoId) { + return &aFds[i].fd(); + } + } + return nullptr; +} + +/** + * This constructor is used for new content process cloned from a template. + * + * For Nuwa. + */ +ContentParent::ContentParent(ContentParent* aTemplate, + const nsAString& aAppManifestURL, + base::ProcessHandle aPid, + const nsTArray& aFds) + : mAppManifestURL(aAppManifestURL) + , mIsForBrowser(false) + , mIsNuwaProcess(false) +{ + InitializeMembers(); // Perform common initialization. + + sContentParents->insertBack(this); + + // From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the + // PID along with the warning. + nsDebugImpl::SetMultiprocessMode("Parent"); + + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + const FileDescriptor* fd = FindFdProtocolFdMapping(aFds, GetProtocolId()); + + NS_ASSERTION(fd != nullptr, "IPC Channel for PContent is necessary!"); + mSubprocess = new GeckoExistingProcessHost(GeckoProcessType_Content, + aPid, + *fd); + + // Tell the memory reporter manager that this ContentParent exists. + nsRefPtr mgr = + nsMemoryReporterManager::GetOrCreate(); + if (mgr) { + mgr->IncrementNumChildProcesses(); + } + + mSubprocess->LaunchAndWaitForProcessHandle(); + + // Clone actors routed by aTemplate for this instance. + IToplevelProtocol::SetTransport(mSubprocess->GetChannel()); + ProtocolCloneContext cloneContext; + cloneContext.SetContentParent(this); + CloneManagees(aTemplate, &cloneContext); + CloneOpenedToplevels(aTemplate, aFds, aPid, &cloneContext); + + Open(mSubprocess->GetChannel(), + mSubprocess->GetChildProcessHandle()); + + // Set the subprocess's priority (bg if we're a preallocated process, fg + // otherwise). We do this first because we're likely /lowering/ its CPU and + // memory priority, which it has inherited from this process. + ProcessPriority priority; + if (IsPreallocated()) { + priority = PROCESS_PRIORITY_PREALLOC; + } else { + priority = PROCESS_PRIORITY_FOREGROUND; + } + + InitInternal(priority, + false, /* Setup Off-main thread compositing */ + false /* Send registered chrome */); +} +#endif // MOZ_NUWA_PROCESS + +ContentParent::~ContentParent() +{ + if (mForceKillTask) { + mForceKillTask->Cancel(); + } + + if (OtherProcess()) + base::CloseProcessHandle(OtherProcess()); + + 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)); + } 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) +{ + // 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 CompositorChild. This + // message goes through the regular IPC queue for this + // channel, so delivery will happen-before any other messages + // we send. The CompositorChild 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 = !!CompositorParent::CompositorLoop(); + if (useOffMainThreadCompositing) { + DebugOnly opened = PCompositor::Open(this); + MOZ_ASSERT(opened); + + if (gfxPrefs::AsyncVideoEnabled()) { + opened = PImageBridge::Open(this); + MOZ_ASSERT(opened); + } + } + } + + if (aSendRegisteredChrome) { + nsCOMPtr registrySvc = nsChromeRegistry::GetService(); + nsChromeRegistryChrome* chromeRegistry = + static_cast(registrySvc.get()); + chromeRegistry->SendRegisteredChrome(this); + } + + mMessageManager = nsFrameMessageManager::NewProcessMessageManager(this); + + if (gAppData) { + nsCString version(gAppData->version); + nsCString buildID(gAppData->buildID); + nsCString name(gAppData->name); + nsCString UAName(gAppData->UAName); + + // Sending all information to content process. + unused << SendAppInfo(version, buildID, name, UAName); + } + + nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance(); + if (sheetService) { + // This looks like a lot of work, but in a normal browser session we just + // send two loads. + + nsCOMArray& agentSheets = *sheetService->AgentStyleSheets(); + for (uint32_t i = 0; i < agentSheets.Length(); i++) { + URIParams uri; + SerializeURI(agentSheets[i]->GetSheetURI(), uri); + unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AGENT_SHEET); + } + + nsCOMArray& userSheets = *sheetService->UserStyleSheets(); + for (uint32_t i = 0; i < userSheets.Length(); i++) { + URIParams uri; + SerializeURI(userSheets[i]->GetSheetURI(), uri); + unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::USER_SHEET); + } + + nsCOMArray& authorSheets = *sheetService->AuthorStyleSheets(); + for (uint32_t i = 0; i < authorSheets.Length(); i++) { + URIParams uri; + SerializeURI(authorSheets[i]->GetSheetURI(), uri); + unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AUTHOR_SHEET); + } + } + +#ifdef MOZ_CONTENT_SANDBOX + bool shouldSandbox = true; +#ifdef MOZ_NUWA_PROCESS + if (IsNuwaProcess()) { + shouldSandbox = false; + } +#endif + if (shouldSandbox && !SendSetProcessSandbox()) { + KillHard(); + } +#endif +} + +bool +ContentParent::IsAlive() +{ + return mIsAlive; +} + +bool +ContentParent::IsForApp() +{ + return !mAppManifestURL.IsEmpty(); +} + +#ifdef MOZ_NUWA_PROCESS +bool +ContentParent::IsNuwaProcess() +{ + return mIsNuwaProcess; +} +#endif + +int32_t +ContentParent::Pid() +{ + if (!mSubprocess || !mSubprocess->GetChildProcessHandle()) { + return -1; + } + return base::GetProcId(mSubprocess->GetChildProcessHandle()); +} + +bool +ContentParent::RecvReadPrefsArray(InfallibleTArray* aPrefs) +{ + Preferences::GetPreferences(aPrefs); + return true; +} + +bool +ContentParent::RecvReadFontList(InfallibleTArray* retValue) +{ +#ifdef ANDROID + gfxAndroidPlatform::GetPlatform()->GetFontList(retValue); +#endif + return true; +} + +bool +ContentParent::RecvReadPermissions(InfallibleTArray* aPermissions) +{ +#ifdef MOZ_PERMISSIONS + nsCOMPtr permissionManagerIface = + do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); + nsPermissionManager* permissionManager = + static_cast(permissionManagerIface.get()); + NS_ABORT_IF_FALSE(permissionManager, + "We have no permissionManager in the Chrome process !"); + + nsCOMPtr enumerator; + DebugOnly rv = permissionManager->GetEnumerator(getter_AddRefs(enumerator)); + NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "Could not get enumerator!"); + while(1) { + bool hasMore; + enumerator->HasMoreElements(&hasMore); + if (!hasMore) + break; + + nsCOMPtr supp; + enumerator->GetNext(getter_AddRefs(supp)); + nsCOMPtr perm = do_QueryInterface(supp); + + nsCString host; + perm->GetHost(host); + uint32_t appId; + perm->GetAppId(&appId); + bool isInBrowserElement; + perm->GetIsInBrowserElement(&isInBrowserElement); + 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(host, appId, + isInBrowserElement, type, + capability, expireType, + expireTime)); + } + + // Ask for future changes + mSendPermissionUpdates = true; +#endif + + return true; +} + +bool +ContentParent::RecvSetClipboardText(const nsString& text, + const bool& isPrivateData, + const int32_t& whichClipboard) +{ + nsresult rv; + nsCOMPtr clipboard(do_GetService(kCClipboardCID, &rv)); + NS_ENSURE_SUCCESS(rv, true); + + nsCOMPtr dataWrapper = + do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, true); + + rv = dataWrapper->SetData(text); + NS_ENSURE_SUCCESS(rv, true); + + nsCOMPtr trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv); + NS_ENSURE_SUCCESS(rv, true); + trans->Init(nullptr); + + // If our data flavor has already been added, this will fail. But we don't care + trans->AddDataFlavor(kUnicodeMime); + trans->SetIsPrivateData(isPrivateData); + + nsCOMPtr nsisupportsDataWrapper = + do_QueryInterface(dataWrapper); + + rv = trans->SetTransferData(kUnicodeMime, nsisupportsDataWrapper, + text.Length() * sizeof(char16_t)); + NS_ENSURE_SUCCESS(rv, true); + + clipboard->SetData(trans, nullptr, whichClipboard); + return true; +} + +bool +ContentParent::RecvGetClipboardText(const int32_t& whichClipboard, nsString* text) +{ + nsresult rv; + nsCOMPtr clipboard(do_GetService(kCClipboardCID, &rv)); + NS_ENSURE_SUCCESS(rv, true); + + nsCOMPtr trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv); + NS_ENSURE_SUCCESS(rv, true); + trans->Init(nullptr); + trans->AddDataFlavor(kUnicodeMime); + + clipboard->GetData(trans, whichClipboard); + nsCOMPtr tmp; + uint32_t len; + rv = trans->GetTransferData(kUnicodeMime, getter_AddRefs(tmp), &len); + if (NS_FAILED(rv)) + return true; + + nsCOMPtr supportsString = do_QueryInterface(tmp); + // No support for non-text data + if (!supportsString) + return true; + supportsString->GetData(*text); + return true; +} + +bool +ContentParent::RecvEmptyClipboard(const int32_t& whichClipboard) +{ + nsresult rv; + nsCOMPtr clipboard(do_GetService(kCClipboardCID, &rv)); + NS_ENSURE_SUCCESS(rv, true); + + clipboard->EmptyClipboard(whichClipboard); + + return true; +} + +bool +ContentParent::RecvClipboardHasText(const int32_t& whichClipboard, bool* hasText) +{ + nsresult rv; + nsCOMPtr clipboard(do_GetService(kCClipboardCID, &rv)); + NS_ENSURE_SUCCESS(rv, true); + + clipboard->HasDataMatchingFlavors(sClipboardTextFlavors, 1, + whichClipboard, hasText); + return true; +} + +bool +ContentParent::RecvGetSystemColors(const uint32_t& colorsCount, InfallibleTArray* 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* 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 = mozilla::widget::android::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::RecvAudioChannelGetState(const AudioChannel& aChannel, + const bool& aElementHidden, + const bool& aElementWasHidden, + AudioChannelState* aState) +{ + nsRefPtr service = + AudioChannelService::GetAudioChannelService(); + *aState = AUDIO_CHANNEL_STATE_NORMAL; + if (service) { + *aState = service->GetStateInternal(aChannel, mChildID, + aElementHidden, aElementWasHidden); + } + return true; +} + +bool +ContentParent::RecvAudioChannelRegisterType(const AudioChannel& aChannel, + const bool& aWithVideo) +{ + nsRefPtr service = + AudioChannelService::GetAudioChannelService(); + if (service) { + service->RegisterType(aChannel, mChildID, aWithVideo); + } + return true; +} + +bool +ContentParent::RecvAudioChannelUnregisterType(const AudioChannel& aChannel, + const bool& aElementHidden, + const bool& aWithVideo) +{ + nsRefPtr service = + AudioChannelService::GetAudioChannelService(); + if (service) { + service->UnregisterType(aChannel, aElementHidden, mChildID, aWithVideo); + } + return true; +} + +bool +ContentParent::RecvAudioChannelChangedNotification() +{ + nsRefPtr service = + AudioChannelService::GetAudioChannelService(); + if (service) { + service->SendAudioChannelChangedNotification(ChildID()); + } + return true; +} + +bool +ContentParent::RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel, + const bool& aHidden) +{ + nsRefPtr service = + AudioChannelService::GetAudioChannelService(); + if (service) { + service->SetDefaultVolumeControlChannelInternal(aChannel, + aHidden, mChildID); + } + return true; +} + +bool +ContentParent::RecvBroadcastVolume(const nsString& aVolumeName) +{ +#ifdef MOZ_WIDGET_GONK + nsresult rv; + nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv); + if (vs) { + vs->BroadcastVolume(aVolumeName); + } + return true; +#else + NS_WARNING("ContentParent::RecvBroadcastVolume shouldn't be called when MOZ_WIDGET_GONK is not defined"); + return false; +#endif +} + +bool +ContentParent::RecvNuwaReady() +{ +#ifdef MOZ_NUWA_PROCESS + if (!IsNuwaProcess()) { + NS_ERROR( + nsPrintfCString( + "Terminating child process %d for unauthorized IPC message: NuwaReady", + Pid()).get()); + + KillHard(); + return false; + } + PreallocatedProcessManager::OnNuwaReady(); + return true; +#else + NS_ERROR("ContentParent::RecvNuwaReady() not implemented!"); + return false; +#endif +} + +bool +ContentParent::RecvAddNewProcess(const uint32_t& aPid, + const InfallibleTArray& aFds) +{ +#ifdef MOZ_NUWA_PROCESS + if (!IsNuwaProcess()) { + NS_ERROR( + nsPrintfCString( + "Terminating child process %d for unauthorized IPC message: " + "AddNewProcess(%d)", Pid(), aPid).get()); + + KillHard(); + return false; + } + nsRefPtr content; + content = new ContentParent(this, + MAGIC_PREALLOCATED_APP_MANIFEST_URL, + aPid, + aFds); + content->Init(); + PreallocatedProcessManager::PublishSpareProcess(content); + return true; +#else + NS_ERROR("ContentParent::RecvAddNewProcess() not implemented!"); + return false; +#endif +} + +// 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(nsIObserver) + NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionCallback) + 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 (!strcmp(aTopic, "xpcom-shutdown") && mSubprocess) { + ShutDownProcess(/* closeWithError */ false); + NS_ASSERTION(!mSubprocess, "Close should have nulled mSubprocess"); + } + + 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; + } + // listening for alert notifications + else if (!strcmp(aTopic, "alertfinished") || + !strcmp(aTopic, "alertclickcallback") || + !strcmp(aTopic, "alertshow") ) { + if (!SendNotifyAlertsObserver(nsDependentCString(aTopic), + nsDependentString(aData))) + return NS_ERROR_NOT_AVAILABLE; + } + else if (!strcmp(aTopic, "child-memory-reporter-request")) { + bool isNuwa = false; +#ifdef MOZ_NUWA_PROCESS + isNuwa = IsNuwaProcess(); +#endif + if (!isNuwa) { + unsigned generation; + int minimize, identOffset = -1; + nsDependentString msg(aData); + NS_ConvertUTF16toUTF8 cmsg(msg); + + if (sscanf(cmsg.get(), + "generation=%x minimize=%d DMDident=%n", + &generation, &minimize, &identOffset) < 2 + || identOffset < 0) { + return NS_ERROR_INVALID_ARG; + } + // The pre-%n part of the string should be all ASCII, so the byte + // offset in identOffset should be correct as a char offset. + MOZ_ASSERT(cmsg[identOffset - 1] == '='); + unused << SendPMemoryReportRequestConstructor( + generation, minimize, nsString(Substring(msg, identOffset))); + } + } + 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(); + } + else if (!strcmp(aTopic, "file-watcher-update")) { + nsCString creason; + CopyUTF16toUTF8(aData, creason); + DeviceStorageFile* file = static_cast(aSubject); + + unused << SendFilePathUpdate(file->mStorageType, file->mStorageName, file->mPath, creason); + } +#ifdef MOZ_WIDGET_GONK + else if(!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) { + nsCOMPtr 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; + + vol->GetName(volName); + vol->GetMountPoint(mountPoint); + vol->GetState(&state); + vol->GetMountGeneration(&mountGeneration); + vol->GetIsMediaPresent(&isMediaPresent); + vol->GetIsSharing(&isSharing); + vol->GetIsFormatting(&isFormatting); + + unused << SendFileSystemUpdate(volName, mountPoint, state, + mountGeneration, isMediaPresent, + isSharing, isFormatting); + } else if (!strcmp(aTopic, "phone-state-changed")) { + nsString state(aData); + unused << SendNotifyPhoneStateChange(state); + } +#endif +#ifdef ACCESSIBILITY + // Make sure accessibility is running in content process when accessibility + // gets initiated in chrome process. + else if (aData && (*aData == '1') && + !strcmp(aTopic, "a11y-init-or-shutdown")) { + unused << SendActivateA11y(); + } +#endif + + return NS_OK; +} + +PCompositorParent* +ContentParent::AllocPCompositorParent(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess) +{ + return CompositorParent::Create(aTransport, aOtherProcess); +} + +PImageBridgeParent* +ContentParent::AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess) +{ + return ImageBridgeParent::Create(aTransport, aOtherProcess); +} + +PBackgroundParent* +ContentParent::AllocPBackgroundParent(Transport* aTransport, + ProcessId aOtherProcess) +{ + return BackgroundParent::Alloc(this, aTransport, aOtherProcess); +} + +bool +ContentParent::RecvGetProcessAttributes(uint64_t* aId, + bool* aIsForApp, bool* aIsForBrowser) +{ + *aId = mChildID; + *aIsForApp = IsForApp(); + *aIsForBrowser = mIsForBrowser; + + return true; +} + +bool +ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline) +{ + nsCOMPtr io(do_GetIOService()); + NS_ASSERTION(io, "No IO service?"); + DebugOnly rv = io->GetOffline(aIsOffline); + NS_ASSERTION(NS_SUCCEEDED(rv), "Failed getting offline?"); + + return true; +} + +mozilla::jsipc::PJavaScriptParent * +ContentParent::AllocPJavaScriptParent() +{ + mozilla::jsipc::JavaScriptParent *parent = new mozilla::jsipc::JavaScriptParent(); + if (!parent->init()) { + delete parent; + return nullptr; + } + return parent; +} + +bool +ContentParent::DeallocPJavaScriptParent(PJavaScriptParent *parent) +{ + static_cast(parent)->decref(); + return true; +} + +PBrowserParent* +ContentParent::AllocPBrowserParent(const IPCTabContext& aContext, + const uint32_t &aChromeFlags) +{ + unused << aChromeFlags; + + const IPCTabAppBrowserContext& appBrowser = aContext.appBrowserContext(); + + // We don't trust the IPCTabContext we receive from the child, so we'll bail + // if we receive an IPCTabContext that's not a PopupIPCTabContext. + // (PopupIPCTabContext lets the child process prove that it has access to + // the app it's trying to open.) + if (appBrowser.type() != IPCTabAppBrowserContext::TPopupIPCTabContext) { + NS_ERROR("Unexpected IPCTabContext type. Aborting AllocPBrowserParent."); + return nullptr; + } + + const PopupIPCTabContext& popupContext = appBrowser.get_PopupIPCTabContext(); + TabParent* opener = static_cast(popupContext.openerParent()); + if (!opener) { + NS_ERROR("Got null opener from child; aborting AllocPBrowserParent."); + return nullptr; + } + + // Popup windows of isBrowser frames must be isBrowser if the parent + // isBrowser. Allocating a !isBrowser frame with same app ID would allow + // the content to access data it's not supposed to. + if (!popupContext.isBrowserElement() && opener->IsBrowserElement()) { + NS_ERROR("Child trying to escalate privileges! Aborting AllocPBrowserParent."); + return nullptr; + } + + MaybeInvalidTabContext tc(aContext); + if (!tc.IsValid()) { + NS_ERROR(nsPrintfCString("Child passed us an invalid TabContext. (%s) " + "Aborting AllocPBrowserParent.", + tc.GetInvalidReason()).get()); + return nullptr; + } + + TabParent* parent = new TabParent(this, tc.GetTabContext(), aChromeFlags); + + // We release this ref in DeallocPBrowserParent() + NS_ADDREF(parent); + return parent; +} + +bool +ContentParent::DeallocPBrowserParent(PBrowserParent* frame) +{ + TabParent* parent = static_cast(frame); + NS_RELEASE(parent); + return true; +} + +PDeviceStorageRequestParent* +ContentParent::AllocPDeviceStorageRequestParent(const DeviceStorageParams& aParams) +{ + nsRefPtr result = new DeviceStorageRequestParent(aParams); + if (!result->EnsureRequiredPermissions(this)) { + return nullptr; + } + result->Dispatch(); + return result.forget().take(); +} + +bool +ContentParent::DeallocPDeviceStorageRequestParent(PDeviceStorageRequestParent* doomed) +{ + DeviceStorageRequestParent *parent = static_cast(doomed); + NS_RELEASE(parent); + return true; +} + +PFileSystemRequestParent* +ContentParent::AllocPFileSystemRequestParent(const FileSystemParams& aParams) +{ + nsRefPtr result = new FileSystemRequestParent(); + if (!result->Dispatch(this, aParams)) { + return nullptr; + } + return result.forget().take(); +} + +bool +ContentParent::DeallocPFileSystemRequestParent(PFileSystemRequestParent* doomed) +{ + FileSystemRequestParent* parent = static_cast(doomed); + NS_RELEASE(parent); + return true; +} + +PBlobParent* +ContentParent::AllocPBlobParent(const BlobConstructorParams& aParams) +{ + return BlobParent::Create(this, aParams); +} + +bool +ContentParent::DeallocPBlobParent(PBlobParent* aActor) +{ + delete aActor; + return true; +} + +BlobParent* +ContentParent::GetOrCreateActorForBlob(nsIDOMBlob* aBlob) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aBlob); + + // If the blob represents a remote blob for this ContentParent then we can + // simply pass its actor back here. + if (nsCOMPtr remoteBlob = do_QueryInterface(aBlob)) { + if (BlobParent* actor = static_cast( + static_cast(remoteBlob->GetPBlob()))) { + if (static_cast(actor->Manager()) == this) { + return actor; + } + } + } + + // XXX This is only safe so long as all blob implementations in our tree + // inherit nsDOMFileBase. If that ever changes then this will need to grow + // a real interface or something. + const nsDOMFileBase* blob = static_cast(aBlob); + + // We often pass blobs that are multipart but that only contain one sub-blob + // (WebActivities does this a bunch). Unwrap to reduce the number of actors + // that we have to maintain. + const nsTArray >* subBlobs = blob->GetSubBlobs(); + if (subBlobs && subBlobs->Length() == 1) { + const nsCOMPtr& subBlob = subBlobs->ElementAt(0); + MOZ_ASSERT(subBlob); + + // We can only take this shortcut if the multipart and the sub-blob are both + // Blob objects or both File objects. + nsCOMPtr multipartBlobAsFile = do_QueryInterface(aBlob); + nsCOMPtr subBlobAsFile = do_QueryInterface(subBlob); + if (!multipartBlobAsFile == !subBlobAsFile) { + // The wrapping might have been unnecessary, see if we can simply pass an + // existing remote blob for this ContentParent. + if (nsCOMPtr remoteSubBlob = do_QueryInterface(subBlob)) { + BlobParent* actor = + static_cast( + static_cast(remoteSubBlob->GetPBlob())); + MOZ_ASSERT(actor); + + if (static_cast(actor->Manager()) == this) { + return actor; + } + } + + // No need to add a reference here since the original blob must have a + // strong reference in the caller and it must also have a strong reference + // to this sub-blob. + aBlob = subBlob; + blob = static_cast(aBlob); + subBlobs = blob->GetSubBlobs(); + } + } + + // All blobs shared between processes must be immutable. + nsCOMPtr mutableBlob = do_QueryInterface(aBlob); + if (!mutableBlob || NS_FAILED(mutableBlob->SetMutable(false))) { + NS_WARNING("Failed to make blob immutable!"); + return nullptr; + } + + ChildBlobConstructorParams params; + + if (blob->IsSizeUnknown() || blob->IsDateUnknown()) { + // We don't want to call GetSize or GetLastModifiedDate + // yet since that may stat a file on the main thread + // here. Instead we'll learn the size lazily from the + // other process. + params = MysteryBlobConstructorParams(); + } + else { + nsString contentType; + nsresult rv = aBlob->GetType(contentType); + NS_ENSURE_SUCCESS(rv, nullptr); + + uint64_t length; + rv = aBlob->GetSize(&length); + NS_ENSURE_SUCCESS(rv, nullptr); + + nsCOMPtr file = do_QueryInterface(aBlob); + if (file) { + FileBlobConstructorParams fileParams; + + rv = file->GetMozLastModifiedDate(&fileParams.modDate()); + NS_ENSURE_SUCCESS(rv, nullptr); + + rv = file->GetName(fileParams.name()); + NS_ENSURE_SUCCESS(rv, nullptr); + + fileParams.contentType() = contentType; + fileParams.length() = length; + + params = fileParams; + } else { + NormalBlobConstructorParams blobParams; + blobParams.contentType() = contentType; + blobParams.length() = length; + params = blobParams; + } + } + + BlobParent* actor = BlobParent::Create(this, aBlob); + NS_ENSURE_TRUE(actor, nullptr); + + return SendPBlobConstructor(actor, params) ? actor : nullptr; +} + +void +ContentParent::KillHard() +{ + // 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; + mForceKillTask = nullptr; + // This ensures the process is eventually killed, but doesn't + // immediately KILLITWITHFIRE because we want to get a minidump if + // possible. After a timeout though, the process is forceably + // killed. + if (!KillProcess(OtherProcess(), 1, false)) { + NS_WARNING("failed to kill subprocess!"); + } + mSubprocess->SetAlreadyDead(); + XRE_GetIOMessageLoop()->PostTask( + FROM_HERE, + NewRunnableFunction(&ProcessWatcher::EnsureProcessTerminated, + OtherProcess(), /*force=*/true)); + //We do clean-up here + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + NewRunnableMethod(this, &ContentParent::ShutDownProcess, + /* closeWithError */ true), + 3000); + // We've now closed the OtherProcess() handle, so must set it to null to + // prevent our dtor closing it twice. + SetOtherProcess(0); +} + +bool +ContentParent::IsPreallocated() +{ + return mAppManifestURL == MAGIC_PREALLOCATED_APP_MANIFEST_URL; +} + +void +ContentParent::FriendlyName(nsAString& aName) +{ + aName.Truncate(); +#ifdef MOZ_NUWA_PROCESS + if (IsNuwaProcess()) { + aName.AssignLiteral("(Nuwa)"); + } else +#endif + if (IsPreallocated()) { + aName.AssignLiteral("(Preallocated)"); + } else if (mIsForBrowser) { + aName.AssignLiteral("Browser"); + } 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(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; +} + +PIndexedDBParent* +ContentParent::AllocPIndexedDBParent() +{ + return new IndexedDBParent(this); +} + +bool +ContentParent::DeallocPIndexedDBParent(PIndexedDBParent* aActor) +{ + delete aActor; + return true; +} + +bool +ContentParent::RecvPIndexedDBConstructor(PIndexedDBParent* aActor) +{ + nsRefPtr mgr = IndexedDatabaseManager::GetOrCreate(); + NS_ENSURE_TRUE(mgr, false); + + if (!IndexedDatabaseManager::IsMainProcess()) { + NS_RUNTIMEABORT("Not supported yet!"); + } + + nsRefPtr factory; + nsresult rv = IDBFactory::Create(this, getter_AddRefs(factory)); + NS_ENSURE_SUCCESS(rv, false); + + NS_ASSERTION(factory, "This should never be null!"); + + IndexedDBParent* actor = static_cast(aActor); + actor->mFactory = factory; + actor->mASCIIOrigin = factory->GetASCIIOrigin(); + + return true; +} + +PMemoryReportRequestParent* +ContentParent::AllocPMemoryReportRequestParent(const uint32_t& generation, + const bool &minimizeMemoryUsage, + const nsString &aDMDDumpIdent) +{ + MemoryReportRequestParent* parent = new MemoryReportRequestParent(); + return parent; +} + +bool +ContentParent::DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor) +{ + delete actor; + return true; +} + +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; +} + +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 OptionalURIParams& aReferrer, + PBrowserParent* aBrowser) +{ + ExternalHelperAppParent *parent = new ExternalHelperAppParent(uri, aContentLength); + parent->AddRef(); + parent->Init(this, + aMimeContentType, + aContentDisposition, + aContentDispositionHint, + aContentDispositionFilename, + aForceSave, + aReferrer, + aBrowser); + return parent; +} + +bool +ContentParent::DeallocPExternalHelperAppParent(PExternalHelperAppParent* aService) +{ + ExternalHelperAppParent *parent = static_cast(aService); + parent->Release(); + return true; +} + +PSmsParent* +ContentParent::AllocPSmsParent() +{ + if (!AssertAppProcessPermission(this, "sms")) { + return nullptr; + } + + SmsParent* parent = new SmsParent(); + parent->AddRef(); + return parent; +} + +bool +ContentParent::DeallocPSmsParent(PSmsParent* aSms) +{ + static_cast(aSms)->Release(); + return true; +} + +PTelephonyParent* +ContentParent::AllocPTelephonyParent() +{ + if (!AssertAppProcessPermission(this, "telephony")) { + return nullptr; + } + + TelephonyParent* actor = new TelephonyParent(); + NS_ADDREF(actor); + return actor; +} + +bool +ContentParent::DeallocPTelephonyParent(PTelephonyParent* aActor) +{ + static_cast(aActor)->Release(); + return true; +} + +PStorageParent* +ContentParent::AllocPStorageParent() +{ + return new DOMStorageDBParent(); +} + +bool +ContentParent::DeallocPStorageParent(PStorageParent* aActor) +{ + DOMStorageDBParent* child = static_cast(aActor); + child->ReleaseIPDLReference(); + return true; +} + +PBluetoothParent* +ContentParent::AllocPBluetoothParent() +{ +#ifdef MOZ_B2G_BT + if (!AssertAppProcessPermission(this, "bluetooth")) { + return nullptr; + } + return new mozilla::dom::bluetooth::BluetoothParent(); +#else + MOZ_CRASH("No support for bluetooth on this platform!"); +#endif +} + +bool +ContentParent::DeallocPBluetoothParent(PBluetoothParent* aActor) +{ +#ifdef MOZ_B2G_BT + delete aActor; + return true; +#else + MOZ_CRASH("No support for bluetooth on this platform!"); +#endif +} + +bool +ContentParent::RecvPBluetoothConstructor(PBluetoothParent* aActor) +{ +#ifdef MOZ_B2G_BT + nsRefPtr btService = BluetoothService::Get(); + NS_ENSURE_TRUE(btService, false); + + return static_cast(aActor)->InitWithService(btService); +#else + MOZ_CRASH("No support for bluetooth on this platform!"); +#endif +} + +PFMRadioParent* +ContentParent::AllocPFMRadioParent() +{ +#ifdef MOZ_B2G_FM + if (!AssertAppProcessPermission(this, "fmradio")) { + return nullptr; + } + return new FMRadioParent(); +#else + NS_WARNING("No support for FMRadio on this platform!"); + return nullptr; +#endif +} + +bool +ContentParent::DeallocPFMRadioParent(PFMRadioParent* aActor) +{ +#ifdef MOZ_B2G_FM + delete aActor; + return true; +#else + NS_WARNING("No support for FMRadio on this platform!"); + return false; +#endif +} + +asmjscache::PAsmJSCacheEntryParent* +ContentParent::AllocPAsmJSCacheEntryParent( + const asmjscache::OpenMode& aOpenMode, + const asmjscache::WriteParams& aWriteParams, + const IPC::Principal& aPrincipal) +{ + return asmjscache::AllocEntryParent(aOpenMode, aWriteParams, aPrincipal); +} + +bool +ContentParent::DeallocPAsmJSCacheEntryParent(PAsmJSCacheEntryParent* aActor) +{ + asmjscache::DeallocEntryParent(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; + nsRefPtr service = + SpeakerManagerService::GetSpeakerManagerService(); + if (service) { + *aValue = service->GetSpeakerStatus(); + } + return true; +#endif + return false; +} + +bool +ContentParent::RecvSpeakerManagerForceSpeaker(const bool& aEnable) +{ +#ifdef MOZ_WIDGET_GONK + nsRefPtr service = + SpeakerManagerService::GetSpeakerManagerService(); + if (service) { + service->ForceSpeaker(aEnable, mChildID); + } + return true; +#endif + return false; +} + +bool +ContentParent::RecvStartVisitedQuery(const URIParams& aURI) +{ + nsCOMPtr newURI = DeserializeURI(aURI); + if (!newURI) { + return false; + } + nsCOMPtr 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 ourURI = DeserializeURI(uri); + if (!ourURI) { + return false; + } + nsCOMPtr ourReferrer = DeserializeURI(referrer); + nsCOMPtr history = services::GetHistoryService(); + if (history) { + history->VisitURI(ourURI, ourReferrer, flags); + } + return true; +} + + +bool +ContentParent::RecvSetURITitle(const URIParams& uri, + const nsString& title) +{ + nsCOMPtr ourURI = DeserializeURI(uri); + if (!ourURI) { + return false; + } + nsCOMPtr history = services::GetHistoryService(); + if (history) { + history->SetURITitle(ourURI, title); + } + return true; +} + +bool +ContentParent::RecvGetRandomValues(const uint32_t& length, + InfallibleTArray* randomValues) +{ + uint8_t* buf = Crypto::GetRandomValues(length); + if (!buf) { + return true; + } + + randomValues->SetCapacity(length); + randomValues->SetLength(length); + + memcpy(randomValues->Elements(), buf, length); + + NS_Free(buf); + + return true; +} + +bool +ContentParent::RecvLoadURIExternal(const URIParams& uri) +{ + nsCOMPtr extProtService(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID)); + if (!extProtService) { + return true; + } + nsCOMPtr ourURI = DeserializeURI(uri); + if (!ourURI) { + return false; + } + extProtService->LoadURI(ourURI, nullptr); + return true; +} + +bool +ContentParent::RecvShowAlertNotification(const nsString& aImageUrl, const nsString& aTitle, + const nsString& aText, const bool& aTextClickable, + const nsString& aCookie, const nsString& aName, + const nsString& aBidi, const nsString& aLang, + const IPC::Principal& aPrincipal) +{ +#ifdef MOZ_CHILD_PERMISSIONS + uint32_t permission = mozilla::CheckPermission(this, aPrincipal, + "desktop-notification"); + if (permission != nsIPermissionManager::ALLOW_ACTION) { + return true; + } +#endif /* MOZ_CHILD_PERMISSIONS */ + + nsCOMPtr sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID)); + if (sysAlerts) { + sysAlerts->ShowAlertNotification(aImageUrl, aTitle, aText, aTextClickable, + aCookie, this, aName, aBidi, aLang, aPrincipal); + } + return true; +} + +bool +ContentParent::RecvCloseAlert(const nsString& aName, + const IPC::Principal& aPrincipal) +{ +#ifdef MOZ_CHILD_PERMISSIONS + uint32_t permission = mozilla::CheckPermission(this, aPrincipal, + "desktop-notification"); + if (permission != nsIPermissionManager::ALLOW_ACTION) { + return true; + } +#endif + + nsCOMPtr sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID)); + if (sysAlerts) { + sysAlerts->CloseAlert(aName, aPrincipal); + } + + return true; +} + +bool +ContentParent::RecvSyncMessage(const nsString& aMsg, + const ClonedMessageData& aData, + const InfallibleTArray& aCpows, + const IPC::Principal& aPrincipal, + InfallibleTArray* aRetvals) +{ + nsIPrincipal* principal = aPrincipal; + if (!Preferences::GetBool("dom.testing.ignore_ipc_principal", false) && + principal && !AssertAppPrincipal(this, principal)) { + return false; + } + + nsRefPtr ppm = mMessageManager; + if (ppm) { + StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData); + CpowIdHolder cpows(GetCPOWManager(), aCpows); + + ppm->ReceiveMessage(static_cast(ppm.get()), + aMsg, true, &cloneData, &cpows, aPrincipal, aRetvals); + } + return true; +} + +bool +ContentParent::AnswerRpcMessage(const nsString& aMsg, + const ClonedMessageData& aData, + const InfallibleTArray& aCpows, + const IPC::Principal& aPrincipal, + InfallibleTArray* aRetvals) +{ + nsIPrincipal* principal = aPrincipal; + if (!Preferences::GetBool("dom.testing.ignore_ipc_principal", false) && + principal && !AssertAppPrincipal(this, principal)) { + return false; + } + + nsRefPtr ppm = mMessageManager; + if (ppm) { + StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData); + CpowIdHolder cpows(GetCPOWManager(), aCpows); + ppm->ReceiveMessage(static_cast(ppm.get()), + aMsg, true, &cloneData, &cpows, aPrincipal, aRetvals); + } + return true; +} + +bool +ContentParent::RecvAsyncMessage(const nsString& aMsg, + const ClonedMessageData& aData, + const InfallibleTArray& aCpows, + const IPC::Principal& aPrincipal) +{ + nsIPrincipal* principal = aPrincipal; + if (!Preferences::GetBool("dom.testing.ignore_ipc_principal", false) && + principal && !AssertAppPrincipal(this, principal)) { + return false; + } + + nsRefPtr ppm = mMessageManager; + if (ppm) { + StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData); + CpowIdHolder cpows(GetCPOWManager(), aCpows); + ppm->ReceiveMessage(static_cast(ppm.get()), + aMsg, false, &cloneData, &cpows, aPrincipal, nullptr); + } + return true; +} + +bool +ContentParent::RecvFilePathUpdateNotify(const nsString& aType, + const nsString& aStorageName, + const nsString& aFilePath, + const nsCString& aReason) +{ + nsRefPtr dsf = new DeviceStorageFile(aType, + aStorageName, + aFilePath); + + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (!obs) { + return false; + } + obs->NotifyObservers(dsf, "file-watcher-update", + NS_ConvertASCIItoUTF16(aReason).get()); + return true; +} + +static int32_t +AddGeolocationListener(nsIDOMGeoPositionCallback* watcher, bool highAccuracy) +{ + nsCOMPtr geo = do_GetService("@mozilla.org/geolocation;1"); + if (!geo) { + return -1; + } + + PositionOptions* options = new PositionOptions(); + options->mTimeout = 0; + options->mMaximumAge = 0; + options->mEnableHighAccuracy = highAccuracy; + int32_t retval = 1; + geo->WatchPosition(watcher, nullptr, options, &retval); + return retval; +} + +bool +ContentParent::RecvAddGeolocationListener(const IPC::Principal& aPrincipal, + const bool& aHighAccuracy) +{ +#ifdef MOZ_CHILD_PERMISSIONS + if (!Preferences::GetBool("dom.testing.ignore_ipc_principal", false)) { + uint32_t permission = mozilla::CheckPermission(this, aPrincipal, + "geolocation"); + if (permission != nsIPermissionManager::ALLOW_ACTION) { + return true; + } + } +#endif /* MOZ_CHILD_PERMISSIONS */ + + // To ensure no geolocation updates are skipped, we always force the + // creation of a new listener. + RecvRemoveGeolocationListener(); + mGeolocationWatchID = AddGeolocationListener(this, aHighAccuracy); + return true; +} + +bool +ContentParent::RecvRemoveGeolocationListener() +{ + if (mGeolocationWatchID != -1) { + nsCOMPtr 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, aEnable); + } + return true; +} + +NS_IMETHODIMP +ContentParent::HandleEvent(nsIDOMGeoPosition* postion) +{ + unused << SendGeolocationUpdate(GeoPosition(postion)); + return NS_OK; +} + +nsConsoleService * +ContentParent::GetConsoleService() +{ + if (mConsoleService) { + return mConsoleService.get(); + } + + // 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 consoleService(do_GetService(consoleServiceCID)); + mConsoleService = consoleService; + return mConsoleService.get(); +} + +bool +ContentParent::RecvConsoleMessage(const nsString& aMessage) +{ + nsRefPtr consoleService = GetConsoleService(); + if (!consoleService) { + return true; + } + + nsRefPtr 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) +{ + nsRefPtr consoleService = GetConsoleService(); + if (!consoleService) { + return true; + } + + nsCOMPtr 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(); + if (aExist) { + sPrivateContent->AppendElement(this); + } else { + sPrivateContent->RemoveElement(this); + if (!sPrivateContent->Length()) { + nsCOMPtr obs = mozilla::services::GetObserverService(); + obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr); + delete sPrivateContent; + sPrivateContent = nullptr; + } + } + return true; +} + +bool +ContentParent::DoSendAsyncMessage(JSContext* aCx, + const nsAString& aMessage, + const mozilla::dom::StructuredCloneData& aData, + JS::Handle aCpows, + nsIPrincipal* aPrincipal) +{ + ClonedMessageData data; + if (!BuildClonedMessageDataForParent(this, aData, data)) { + return false; + } + InfallibleTArray cpows; + if (!GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { + return false; + } + return SendAsyncMessage(nsString(aMessage), data, cpows, aPrincipal); +} + +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::RecvSystemMessageHandled() +{ + SystemMessageHandledListener::OnSystemMessageHandled(); + return true; +} + +bool +ContentParent::RecvCreateFakeVolume(const nsString& fsName, const nsString& mountPoint) +{ +#ifdef MOZ_WIDGET_GONK + nsresult rv; + nsCOMPtr 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 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::RecvKeywordToURI(const nsCString& aKeyword, OptionalInputStreamParams* aPostData, + OptionalURIParams* aURI) +{ + nsCOMPtr fixup = do_GetService(NS_URIFIXUP_CONTRACTID); + if (!fixup) { + return true; + } + + nsCOMPtr postData; + nsCOMPtr uri; + if (NS_FAILED(fixup->KeywordToURI(aKeyword, getter_AddRefs(postData), + getter_AddRefs(uri)))) { + return true; + } + + nsTArray fds; + SerializeInputStream(postData, *aPostData, fds); + MOZ_ASSERT(fds.IsEmpty()); + + SerializeURI(uri, *aURI); + return true; +} + +bool +ContentParent::ShouldContinueFromReplyTimeout() +{ + // The only time ContentParent sends blocking messages is for CPOWs, so + // timeouts should only ever occur in electrolysis-enabled sessions. + MOZ_ASSERT(BrowserTabsRemote()); + return false; +} + +bool +ContentParent::ShouldSandboxContentProcesses() +{ +#ifdef MOZ_CONTENT_SANDBOX + return !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX"); +#else + return true; +#endif +} + +bool +ContentParent::RecvRecordingDeviceEvents(const nsString& aRecordingStatus, + const nsString& aPageURL, + const bool& aIsAudio, + const bool& aIsVideo) +{ + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + // recording-device-ipc-events needs to gather more information from content process + nsRefPtr 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, + bool* aSuccess) +{ + nsCOMPtr gfxInfo = do_GetService("@mozilla.org/gfx/info;1"); + if (!gfxInfo) { + *aSuccess = false; + return true; + } + + *aSuccess = NS_SUCCEEDED(gfxInfo->GetFeatureStatus(aFeature, aStatus)); + return true; +} + +bool +ContentParent::RecvAddIdleObserver(const uint64_t& aObserver, const uint32_t& aIdleTimeInS) +{ + nsresult rv; + nsCOMPtr idleService = + do_GetService("@mozilla.org/widget/idleservice;1", &rv); + NS_ENSURE_SUCCESS(rv, false); + + nsRefPtr listener = new ParentIdleListener(this, aObserver); + mIdleListeners.Put(aObserver, listener); + idleService->AddIdleObserver(listener, aIdleTimeInS); + return true; +} + +bool +ContentParent::RecvRemoveIdleObserver(const uint64_t& aObserver, const uint32_t& aIdleTimeInS) +{ + nsresult rv; + nsCOMPtr idleService = + do_GetService("@mozilla.org/widget/idleservice;1", &rv); + NS_ENSURE_SUCCESS(rv, false); + + nsRefPtr listener; + bool found = mIdleListeners.Get(aObserver, &listener); + if (found) { + mIdleListeners.Remove(aObserver); + idleService->RemoveIdleObserver(listener, aIdleTimeInS); + } + + return true; +} + +bool +ContentParent::RecvBackUpXResources(const FileDescriptor& aXSocketFd) +{ +#ifndef MOZ_X11 + NS_RUNTIMEABORT("This message only makes sense on X11 platforms"); +#else + NS_ABORT_IF_FALSE(0 > mChildXSocketFdDup.get(), + "Already backed up X resources??"); + mChildXSocketFdDup.forget(); + if (aXSocketFd.IsValid()) { + mChildXSocketFdDup.reset(aXSocketFd.PlatformHandle()); + } +#endif + return true; +} + +PFileDescriptorSetParent* +ContentParent::AllocPFileDescriptorSetParent(const FileDescriptor& aFD) +{ + return new FileDescriptorSetParent(aFD); +} + +bool +ContentParent::DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor) +{ + delete static_cast(aActor); + return true; +} + +} // 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; +}