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