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