mirror of https://github.com/roytam1/UXP
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5061 lines
151 KiB
5061 lines
151 KiB
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
|
/* This Source Code Form is subject to the terms of the Mozilla Public |
|
* License, v. 2.0. If a copy of the MPL was not distributed with this |
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
|
|
#include "mozilla/DebugOnly.h" |
|
|
|
#include "base/basictypes.h" |
|
|
|
#include "ContentParent.h" |
|
#include "TabParent.h" |
|
|
|
#if defined(ANDROID) || defined(LINUX) |
|
# include <sys/time.h> |
|
# include <sys/resource.h> |
|
#endif |
|
|
|
#ifdef MOZ_WIDGET_GONK |
|
#include <sys/types.h> |
|
#include <sys/wait.h> |
|
#endif |
|
|
|
#include "chrome/common/process_watcher.h" |
|
|
|
#include "mozilla/a11y/PDocAccessible.h" |
|
#include "AppProcessChecker.h" |
|
#include "AudioChannelService.h" |
|
#include "BlobParent.h" |
|
#include "CrashReporterParent.h" |
|
#include "GMPServiceParent.h" |
|
#include "HandlerServiceParent.h" |
|
#include "IHistory.h" |
|
#include "imgIContainer.h" |
|
#include "mozIApplication.h" |
|
#if defined(XP_WIN) && defined(ACCESSIBILITY) |
|
#include "mozilla/a11y/AccessibleWrap.h" |
|
#endif |
|
#include "mozilla/ClearOnShutdown.h" |
|
#include "mozilla/StyleSheetInlines.h" |
|
#include "mozilla/DataStorage.h" |
|
#include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h" |
|
#include "mozilla/docshell/OfflineCacheUpdateParent.h" |
|
#include "mozilla/dom/DataTransfer.h" |
|
#include "mozilla/dom/DOMStorageIPC.h" |
|
#include "mozilla/dom/Element.h" |
|
#include "mozilla/dom/File.h" |
|
#include "mozilla/dom/FileSystemSecurity.h" |
|
#include "mozilla/dom/ExternalHelperAppParent.h" |
|
#include "mozilla/dom/GetFilesHelper.h" |
|
#include "mozilla/dom/GeolocationBinding.h" |
|
#include "mozilla/dom/Notification.h" |
|
#include "mozilla/dom/PContentBridgeParent.h" |
|
#include "mozilla/dom/PContentPermissionRequestParent.h" |
|
#include "mozilla/dom/PCycleCollectWithLogsParent.h" |
|
#include "mozilla/dom/PMemoryReportRequestParent.h" |
|
#include "mozilla/dom/ServiceWorkerRegistrar.h" |
|
#include "mozilla/dom/power/PowerManagerService.h" |
|
#include "mozilla/dom/Permissions.h" |
|
#include "mozilla/dom/PresentationParent.h" |
|
#include "mozilla/dom/PPresentationParent.h" |
|
#include "mozilla/dom/PushNotifier.h" |
|
#include "mozilla/dom/FlyWebPublishedServerIPC.h" |
|
#include "mozilla/dom/quota/QuotaManagerService.h" |
|
#include "mozilla/dom/time/DateCacheCleaner.h" |
|
#include "mozilla/embedding/printingui/PrintingParent.h" |
|
#include "mozilla/gfx/gfxVars.h" |
|
#include "mozilla/gfx/GPUProcessManager.h" |
|
#include "mozilla/hal_sandbox/PHalParent.h" |
|
#include "mozilla/ipc/BackgroundChild.h" |
|
#include "mozilla/ipc/BackgroundParent.h" |
|
#include "mozilla/ipc/FileDescriptorUtils.h" |
|
#include "mozilla/ipc/PSendStreamParent.h" |
|
#include "mozilla/ipc/TestShellParent.h" |
|
#include "mozilla/ipc/InputStreamUtils.h" |
|
#include "mozilla/jsipc/CrossProcessObjectWrappers.h" |
|
#include "mozilla/layers/PAPZParent.h" |
|
#include "mozilla/layers/CompositorThread.h" |
|
#include "mozilla/layers/ImageBridgeParent.h" |
|
#include "mozilla/layers/LayerTreeOwnerTracker.h" |
|
#include "mozilla/layout/RenderFrameParent.h" |
|
#include "mozilla/LookAndFeel.h" |
|
#include "mozilla/media/MediaParent.h" |
|
#include "mozilla/Move.h" |
|
#include "mozilla/net/NeckoParent.h" |
|
#include "mozilla/plugins/PluginBridge.h" |
|
#include "mozilla/Preferences.h" |
|
#include "mozilla/ProcessHangMonitor.h" |
|
#include "mozilla/ProcessHangMonitorIPC.h" |
|
#ifdef MOZ_ENABLE_PROFILER_SPS |
|
#include "mozilla/ProfileGatherer.h" |
|
#endif |
|
#include "mozilla/ScopeExit.h" |
|
#include "mozilla/Services.h" |
|
#include "mozilla/StaticPtr.h" |
|
#include "mozilla/Telemetry.h" |
|
#include "mozilla/WebBrowserPersistDocumentParent.h" |
|
#include "mozilla/Unused.h" |
|
#include "nsAnonymousTemporaryFile.h" |
|
#include "nsAppRunner.h" |
|
#include "nsCDefaultURIFixup.h" |
|
#include "nsCExternalHandlerService.h" |
|
#include "nsCOMPtr.h" |
|
#include "nsChromeRegistryChrome.h" |
|
#include "nsConsoleMessage.h" |
|
#include "nsConsoleService.h" |
|
#include "nsContentUtils.h" |
|
#include "nsDebugImpl.h" |
|
#include "nsFrameMessageManager.h" |
|
#include "nsHashPropertyBag.h" |
|
#include "nsIAlertsService.h" |
|
#include "nsIAppsService.h" |
|
#include "nsIClipboard.h" |
|
#include "nsContentPermissionHelper.h" |
|
#include "nsICycleCollectorListener.h" |
|
#include "nsIDocShellTreeOwner.h" |
|
#include "nsIDocument.h" |
|
#include "nsIDOMGeoGeolocation.h" |
|
#include "nsIDOMGeoPositionError.h" |
|
#include "nsIDragService.h" |
|
#include "mozilla/dom/WakeLock.h" |
|
#include "nsIDOMWindow.h" |
|
#include "nsIExternalProtocolService.h" |
|
#include "nsIFormProcessor.h" |
|
#include "nsIGfxInfo.h" |
|
#include "nsIIdleService.h" |
|
#include "nsIInterfaceRequestorUtils.h" |
|
#include "nsIMemoryInfoDumper.h" |
|
#include "nsIMemoryReporter.h" |
|
#include "nsIMozBrowserFrame.h" |
|
#include "nsIMutable.h" |
|
#include "nsINSSU2FToken.h" |
|
#include "nsIObserverService.h" |
|
#include "nsIParentChannel.h" |
|
#include "nsIPresShell.h" |
|
#include "nsIRemoteWindowContext.h" |
|
#include "nsIScriptError.h" |
|
#include "nsIScriptSecurityManager.h" |
|
#include "nsISiteSecurityService.h" |
|
#include "nsISpellChecker.h" |
|
#include "nsISupportsPrimitives.h" |
|
#include "nsITimer.h" |
|
#include "nsIURIFixup.h" |
|
#include "nsIDocShellTreeOwner.h" |
|
#include "nsIXULWindow.h" |
|
#include "nsIDOMChromeWindow.h" |
|
#include "nsIWindowWatcher.h" |
|
#include "nsPIWindowWatcher.h" |
|
#include "nsWindowWatcher.h" |
|
#include "nsIXULRuntime.h" |
|
#include "mozilla/dom/nsMixedContentBlocker.h" |
|
#include "nsMemoryInfoDumper.h" |
|
#include "nsMemoryReporterManager.h" |
|
#include "nsServiceManagerUtils.h" |
|
#include "nsStyleSheetService.h" |
|
#include "nsThreadUtils.h" |
|
#include "nsToolkitCompsCID.h" |
|
#include "nsWidgetsCID.h" |
|
#include "PreallocatedProcessManager.h" |
|
#include "ProcessPriorityManager.h" |
|
#include "SandboxHal.h" |
|
#include "ScreenManagerParent.h" |
|
#include "SourceSurfaceRawData.h" |
|
#include "TabParent.h" |
|
#include "URIUtils.h" |
|
#include "nsIWebBrowserChrome.h" |
|
#include "nsIDocShell.h" |
|
#include "nsDocShell.h" |
|
#include "nsOpenURIInFrameParams.h" |
|
#include "mozilla/net/NeckoMessageUtils.h" |
|
#include "gfxPrefs.h" |
|
#include "prio.h" |
|
#include "private/pprio.h" |
|
#include "ContentProcessManager.h" |
|
#include "mozilla/dom/ipc/StructuredCloneData.h" |
|
#include "mozilla/psm/PSMContentListener.h" |
|
#include "nsPluginHost.h" |
|
#include "nsPluginTags.h" |
|
#include "nsIBlocklistService.h" |
|
#include "mozilla/StyleSheet.h" |
|
#include "mozilla/StyleSheetInlines.h" |
|
#include "nsHostObjectProtocolHandler.h" |
|
#include "nsICaptivePortalService.h" |
|
|
|
#include "nsIBidiKeyboard.h" |
|
|
|
#include "nsLayoutStylesheetCache.h" |
|
|
|
#ifdef MOZ_WEBRTC |
|
#include "signaling/src/peerconnection/WebrtcGlobalParent.h" |
|
#endif |
|
|
|
#if defined(ANDROID) || defined(LINUX) |
|
#include "nsSystemInfo.h" |
|
#endif |
|
|
|
#if defined(XP_LINUX) |
|
#include "mozilla/Hal.h" |
|
#endif |
|
|
|
#ifdef ANDROID |
|
# include "gfxAndroidPlatform.h" |
|
#endif |
|
|
|
#ifdef MOZ_PERMISSIONS |
|
# include "nsPermissionManager.h" |
|
#endif |
|
|
|
#ifdef MOZ_WIDGET_ANDROID |
|
# include "AndroidBridge.h" |
|
#endif |
|
|
|
#ifdef MOZ_WIDGET_GONK |
|
#include "nsIVolume.h" |
|
#include "nsVolumeService.h" |
|
#include "nsIVolumeService.h" |
|
#include "SpeakerManagerService.h" |
|
using namespace mozilla::system; |
|
#endif |
|
|
|
#ifdef MOZ_WIDGET_GTK |
|
#include <gdk/gdk.h> |
|
#endif |
|
|
|
#include "mozilla/RemoteSpellCheckEngineParent.h" |
|
|
|
#include "Crypto.h" |
|
|
|
#ifdef MOZ_WEBSPEECH |
|
#include "mozilla/dom/SpeechSynthesisParent.h" |
|
#endif |
|
|
|
#ifdef MOZ_TOOLKIT_SEARCH |
|
#include "nsIBrowserSearchService.h" |
|
#endif |
|
|
|
#ifdef MOZ_ENABLE_PROFILER_SPS |
|
#include "nsIProfiler.h" |
|
#include "nsIProfileSaveEvent.h" |
|
#endif |
|
|
|
#ifdef XP_WIN |
|
#include "mozilla/widget/AudioSession.h" |
|
#endif |
|
|
|
#ifdef ACCESSIBILITY |
|
#include "nsAccessibilityService.h" |
|
#endif |
|
|
|
// For VP9Benchmark::sBenchmarkFpsPref |
|
#include "Benchmark.h" |
|
|
|
static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); |
|
|
|
#if defined(XP_WIN) |
|
// e10s forced enable pref, defined in nsAppRunner.cpp |
|
extern const char* kForceEnableE10sPref; |
|
#endif |
|
|
|
using base::ChildPrivileges; |
|
using base::KillProcess; |
|
#ifdef MOZ_ENABLE_PROFILER_SPS |
|
using mozilla::ProfileGatherer; |
|
#endif |
|
|
|
using namespace mozilla::dom::power; |
|
using namespace mozilla::media; |
|
using namespace mozilla::embedding; |
|
using namespace mozilla::gfx; |
|
using namespace mozilla::gmp; |
|
using namespace mozilla::hal; |
|
using namespace mozilla::ipc; |
|
using namespace mozilla::layers; |
|
using namespace mozilla::layout; |
|
using namespace mozilla::net; |
|
using namespace mozilla::jsipc; |
|
using namespace mozilla::psm; |
|
using namespace mozilla::widget; |
|
|
|
// XXX Workaround for bug 986973 to maintain the existing broken semantics |
|
template<> |
|
struct nsIConsoleService::COMTypeInfo<nsConsoleService, void> { |
|
static const nsIID kIID; |
|
}; |
|
const nsIID nsIConsoleService::COMTypeInfo<nsConsoleService, void>::kIID = NS_ICONSOLESERVICE_IID; |
|
|
|
namespace mozilla { |
|
namespace dom { |
|
|
|
#define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline" |
|
#define NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC "ipc:network:set-connectivity" |
|
|
|
class MemoryReportRequestParent : public PMemoryReportRequestParent |
|
{ |
|
public: |
|
explicit MemoryReportRequestParent(uint32_t aGeneration); |
|
|
|
virtual ~MemoryReportRequestParent(); |
|
|
|
virtual void ActorDestroy(ActorDestroyReason aWhy) override; |
|
|
|
virtual bool RecvReport(const MemoryReport& aReport) override; |
|
virtual bool Recv__delete__() override; |
|
|
|
private: |
|
const uint32_t mGeneration; |
|
// Non-null if we haven't yet called EndProcessReport() on it. |
|
RefPtr<nsMemoryReporterManager> mReporterManager; |
|
|
|
ContentParent* Owner() |
|
{ |
|
return static_cast<ContentParent*>(Manager()); |
|
} |
|
}; |
|
|
|
MemoryReportRequestParent::MemoryReportRequestParent(uint32_t aGeneration) |
|
: mGeneration(aGeneration) |
|
{ |
|
MOZ_COUNT_CTOR(MemoryReportRequestParent); |
|
mReporterManager = nsMemoryReporterManager::GetOrCreate(); |
|
NS_WARNING_ASSERTION(mReporterManager, "GetOrCreate failed"); |
|
} |
|
|
|
bool |
|
MemoryReportRequestParent::RecvReport(const MemoryReport& aReport) |
|
{ |
|
if (mReporterManager) { |
|
mReporterManager->HandleChildReport(mGeneration, aReport); |
|
} |
|
return true; |
|
} |
|
|
|
bool |
|
MemoryReportRequestParent::Recv__delete__() |
|
{ |
|
// Notifying the reporter manager is done in ActorDestroy, because |
|
// it needs to happen even if the child process exits mid-report. |
|
// (The reporter manager will time out eventually, but let's avoid |
|
// that if possible.) |
|
return true; |
|
} |
|
|
|
void |
|
MemoryReportRequestParent::ActorDestroy(ActorDestroyReason aWhy) |
|
{ |
|
if (mReporterManager) { |
|
mReporterManager->EndProcessReport(mGeneration, aWhy == Deletion); |
|
mReporterManager = nullptr; |
|
} |
|
} |
|
|
|
MemoryReportRequestParent::~MemoryReportRequestParent() |
|
{ |
|
MOZ_ASSERT(!mReporterManager); |
|
MOZ_COUNT_DTOR(MemoryReportRequestParent); |
|
} |
|
|
|
// IPC receiver for remote GC/CC logging. |
|
class CycleCollectWithLogsParent final : public PCycleCollectWithLogsParent |
|
{ |
|
public: |
|
~CycleCollectWithLogsParent() |
|
{ |
|
MOZ_COUNT_DTOR(CycleCollectWithLogsParent); |
|
} |
|
|
|
static bool AllocAndSendConstructor(ContentParent* aManager, |
|
bool aDumpAllTraces, |
|
nsICycleCollectorLogSink* aSink, |
|
nsIDumpGCAndCCLogsCallback* aCallback) |
|
{ |
|
CycleCollectWithLogsParent *actor; |
|
FILE* gcLog; |
|
FILE* ccLog; |
|
nsresult rv; |
|
|
|
actor = new CycleCollectWithLogsParent(aSink, aCallback); |
|
rv = actor->mSink->Open(&gcLog, &ccLog); |
|
if (NS_WARN_IF(NS_FAILED(rv))) { |
|
delete actor; |
|
return false; |
|
} |
|
|
|
return aManager-> |
|
SendPCycleCollectWithLogsConstructor(actor, |
|
aDumpAllTraces, |
|
FILEToFileDescriptor(gcLog), |
|
FILEToFileDescriptor(ccLog)); |
|
} |
|
|
|
private: |
|
virtual bool RecvCloseGCLog() override |
|
{ |
|
Unused << mSink->CloseGCLog(); |
|
return true; |
|
} |
|
|
|
virtual bool RecvCloseCCLog() override |
|
{ |
|
Unused << mSink->CloseCCLog(); |
|
return true; |
|
} |
|
|
|
virtual bool Recv__delete__() override |
|
{ |
|
// Report completion to mCallback only on successful |
|
// completion of the protocol. |
|
nsCOMPtr<nsIFile> gcLog, ccLog; |
|
mSink->GetGcLog(getter_AddRefs(gcLog)); |
|
mSink->GetCcLog(getter_AddRefs(ccLog)); |
|
Unused << mCallback->OnDump(gcLog, ccLog, /* parent = */ false); |
|
return true; |
|
} |
|
|
|
virtual void ActorDestroy(ActorDestroyReason aReason) override |
|
{ |
|
// If the actor is unexpectedly destroyed, we deliberately |
|
// don't call Close[GC]CLog on the sink, because the logs may |
|
// be incomplete. See also the nsCycleCollectorLogSinkToFile |
|
// implementaiton of those methods, and its destructor. |
|
} |
|
|
|
CycleCollectWithLogsParent(nsICycleCollectorLogSink *aSink, |
|
nsIDumpGCAndCCLogsCallback *aCallback) |
|
: mSink(aSink), mCallback(aCallback) |
|
{ |
|
MOZ_COUNT_CTOR(CycleCollectWithLogsParent); |
|
} |
|
|
|
nsCOMPtr<nsICycleCollectorLogSink> mSink; |
|
nsCOMPtr<nsIDumpGCAndCCLogsCallback> mCallback; |
|
}; |
|
|
|
// A memory reporter for ContentParent objects themselves. |
|
class ContentParentsMemoryReporter final : public nsIMemoryReporter |
|
{ |
|
~ContentParentsMemoryReporter() {} |
|
public: |
|
NS_DECL_ISUPPORTS |
|
NS_DECL_NSIMEMORYREPORTER |
|
}; |
|
|
|
NS_IMPL_ISUPPORTS(ContentParentsMemoryReporter, nsIMemoryReporter) |
|
|
|
NS_IMETHODIMP |
|
ContentParentsMemoryReporter::CollectReports( |
|
nsIHandleReportCallback* aHandleReport, |
|
nsISupports* aData, |
|
bool aAnonymize) |
|
{ |
|
AutoTArray<ContentParent*, 16> cps; |
|
ContentParent::GetAllEvenIfDead(cps); |
|
|
|
for (uint32_t i = 0; i < cps.Length(); i++) { |
|
ContentParent* cp = cps[i]; |
|
MessageChannel* channel = cp->GetIPCChannel(); |
|
|
|
nsString friendlyName; |
|
cp->FriendlyName(friendlyName, aAnonymize); |
|
|
|
cp->AddRef(); |
|
nsrefcnt refcnt = cp->Release(); |
|
|
|
const char* channelStr = "no channel"; |
|
uint32_t numQueuedMessages = 0; |
|
if (channel) { |
|
if (channel->Unsound_IsClosed()) { |
|
channelStr = "closed channel"; |
|
} else { |
|
channelStr = "open channel"; |
|
} |
|
numQueuedMessages = channel->Unsound_NumQueuedMessages(); |
|
} |
|
|
|
nsPrintfCString path("queued-ipc-messages/content-parent" |
|
"(%s, pid=%d, %s, 0x%p, refcnt=%d)", |
|
NS_ConvertUTF16toUTF8(friendlyName).get(), |
|
cp->Pid(), channelStr, |
|
static_cast<nsIContentParent*>(cp), refcnt); |
|
|
|
NS_NAMED_LITERAL_CSTRING(desc, |
|
"The number of unset IPC messages held in this ContentParent's " |
|
"channel. A large value here might indicate that we're leaking " |
|
"messages. Similarly, a ContentParent object for a process that's no " |
|
"longer running could indicate that we're leaking ContentParents."); |
|
|
|
aHandleReport->Callback(/* process */ EmptyCString(), path, |
|
KIND_OTHER, UNITS_COUNT, |
|
numQueuedMessages, desc, aData); |
|
} |
|
|
|
return NS_OK; |
|
} |
|
|
|
nsDataHashtable<nsStringHashKey, ContentParent*>* ContentParent::sAppContentParents; |
|
nsTArray<ContentParent*>* ContentParent::sNonAppContentParents; |
|
nsTArray<ContentParent*>* ContentParent::sLargeAllocationContentParents; |
|
nsTArray<ContentParent*>* ContentParent::sPrivateContent; |
|
StaticAutoPtr<LinkedList<ContentParent> > ContentParent::sContentParents; |
|
|
|
// This is true when subprocess launching is enabled. This is the |
|
// case between StartUp() and ShutDown() or JoinAllSubprocesses(). |
|
static bool sCanLaunchSubprocesses; |
|
|
|
// Set to true if the DISABLE_UNSAFE_CPOW_WARNINGS environment variable is |
|
// set. |
|
static bool sDisableUnsafeCPOWWarnings = false; |
|
|
|
// The first content child has ID 1, so the chrome process can have ID 0. |
|
static uint64_t gContentChildID = 1; |
|
|
|
// We want the prelaunched process to know that it's for apps, but not |
|
// actually for any app in particular. Use a magic manifest URL. |
|
// Can't be a static constant. |
|
#define MAGIC_PREALLOCATED_APP_MANIFEST_URL NS_LITERAL_STRING("{{template}}") |
|
|
|
static const char* sObserverTopics[] = { |
|
"xpcom-shutdown", |
|
"profile-before-change", |
|
NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC, |
|
NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC, |
|
NS_IPC_CAPTIVE_PORTAL_SET_STATE, |
|
"memory-pressure", |
|
"child-gc-request", |
|
"child-cc-request", |
|
"child-mmu-request", |
|
"last-pb-context-exited", |
|
"file-watcher-update", |
|
#ifdef MOZ_WIDGET_GONK |
|
NS_VOLUME_STATE_CHANGED, |
|
NS_VOLUME_REMOVED, |
|
"phone-state-changed", |
|
#endif |
|
#ifdef ACCESSIBILITY |
|
"a11y-init-or-shutdown", |
|
#endif |
|
#ifdef MOZ_ENABLE_PROFILER_SPS |
|
"profiler-started", |
|
"profiler-stopped", |
|
"profiler-paused", |
|
"profiler-resumed", |
|
"profiler-subprocess-gather", |
|
"profiler-subprocess", |
|
#endif |
|
"cacheservice:empty-cache", |
|
}; |
|
|
|
// PreallocateAppProcess is called by the PreallocatedProcessManager. |
|
// ContentParent then takes this process back within |
|
// GetNewOrPreallocatedAppProcess. |
|
/*static*/ already_AddRefed<ContentParent> |
|
ContentParent::PreallocateAppProcess() |
|
{ |
|
RefPtr<ContentParent> process = |
|
new ContentParent(/* app = */ nullptr, |
|
/* aOpener = */ nullptr, |
|
/* isForBrowserElement = */ false, |
|
/* isForPreallocated = */ true); |
|
|
|
if (!process->LaunchSubprocess(PROCESS_PRIORITY_PREALLOC)) { |
|
return nullptr; |
|
} |
|
|
|
process->Init(); |
|
return process.forget(); |
|
} |
|
|
|
/*static*/ already_AddRefed<ContentParent> |
|
ContentParent::GetNewOrPreallocatedAppProcess(mozIApplication* aApp, |
|
ProcessPriority aInitialPriority, |
|
ContentParent* aOpener, |
|
/*out*/ bool* aTookPreAllocated) |
|
{ |
|
MOZ_ASSERT(aApp); |
|
RefPtr<ContentParent> process = PreallocatedProcessManager::Take(); |
|
|
|
if (process) { |
|
if (!process->SetPriorityAndCheckIsAlive(aInitialPriority)) { |
|
// Kill the process just in case it's not actually dead; we don't want |
|
// to "leak" this process! |
|
process->KillHard("GetNewOrPreallocatedAppProcess"); |
|
} |
|
else { |
|
nsAutoString manifestURL; |
|
if (NS_FAILED(aApp->GetManifestURL(manifestURL))) { |
|
NS_ERROR("Failed to get manifest URL"); |
|
return nullptr; |
|
} |
|
process->TransformPreallocatedIntoApp(aOpener, manifestURL); |
|
process->ForwardKnownInfo(); |
|
|
|
if (aTookPreAllocated) { |
|
*aTookPreAllocated = true; |
|
} |
|
return process.forget(); |
|
} |
|
} |
|
|
|
NS_WARNING("Unable to use pre-allocated app process"); |
|
process = new ContentParent(aApp, |
|
/* aOpener = */ aOpener, |
|
/* isForBrowserElement = */ false, |
|
/* isForPreallocated = */ false); |
|
|
|
if (!process->LaunchSubprocess(aInitialPriority)) { |
|
return nullptr; |
|
} |
|
|
|
process->Init(); |
|
process->ForwardKnownInfo(); |
|
|
|
if (aTookPreAllocated) { |
|
*aTookPreAllocated = false; |
|
} |
|
|
|
return process.forget(); |
|
} |
|
|
|
/*static*/ void |
|
ContentParent::StartUp() |
|
{ |
|
// We could launch sub processes from content process |
|
// FIXME Bug 1023701 - Stop using ContentParent static methods in |
|
// child process |
|
sCanLaunchSubprocesses = true; |
|
|
|
if (!XRE_IsParentProcess()) { |
|
return; |
|
} |
|
|
|
// Note: This reporter measures all ContentParents. |
|
RegisterStrongMemoryReporter(new ContentParentsMemoryReporter()); |
|
|
|
mozilla::dom::time::InitializeDateCacheCleaner(); |
|
|
|
BlobParent::Startup(BlobParent::FriendKey()); |
|
|
|
BackgroundChild::Startup(); |
|
|
|
// Try to preallocate a process that we can transform into an app later. |
|
PreallocatedProcessManager::AllocateAfterDelay(); |
|
|
|
sDisableUnsafeCPOWWarnings = PR_GetEnv("DISABLE_UNSAFE_CPOW_WARNINGS"); |
|
} |
|
|
|
/*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()); |
|
|
|
AutoTArray<ContentParent*, 8> processes; |
|
GetAll(processes); |
|
if (processes.IsEmpty()) { |
|
printf_stderr("There are no live subprocesses."); |
|
return; |
|
} |
|
|
|
printf_stderr("Subprocesses are still alive. Doing emergency join.\n"); |
|
|
|
bool done = false; |
|
Monitor monitor("mozilla.dom.ContentParent.JoinAllSubprocesses"); |
|
XRE_GetIOMessageLoop()->PostTask(NewRunnableFunction( |
|
&ContentParent::JoinProcessesIOThread, |
|
&processes, &monitor, &done)); |
|
{ |
|
MonitorAutoLock lock(monitor); |
|
while (!done) { |
|
lock.Wait(); |
|
} |
|
} |
|
|
|
sCanLaunchSubprocesses = false; |
|
} |
|
|
|
/*static*/ already_AddRefed<ContentParent> |
|
ContentParent::GetNewOrUsedBrowserProcess(bool aForBrowserElement, |
|
ProcessPriority aPriority, |
|
ContentParent* aOpener, |
|
bool aLargeAllocationProcess) |
|
{ |
|
nsTArray<ContentParent*>* contentParents; |
|
int32_t maxContentParents; |
|
|
|
// Decide which pool of content parents we are going to be pulling from based |
|
// on the aLargeAllocationProcess flag. |
|
if (aLargeAllocationProcess) { |
|
if (!sLargeAllocationContentParents) { |
|
sLargeAllocationContentParents = new nsTArray<ContentParent*>(); |
|
} |
|
contentParents = sLargeAllocationContentParents; |
|
|
|
maxContentParents = Preferences::GetInt("dom.ipc.dedicatedProcessCount", 2); |
|
} else { |
|
if (!sNonAppContentParents) { |
|
sNonAppContentParents = new nsTArray<ContentParent*>(); |
|
} |
|
contentParents = sNonAppContentParents; |
|
|
|
maxContentParents = Preferences::GetInt("dom.ipc.processCount", 1); |
|
} |
|
|
|
if (maxContentParents < 1) { |
|
maxContentParents = 1; |
|
} |
|
|
|
if (contentParents->Length() >= uint32_t(maxContentParents)) { |
|
uint32_t maxSelectable = std::min(static_cast<uint32_t>(contentParents->Length()), |
|
static_cast<uint32_t>(maxContentParents)); |
|
uint32_t startIdx = rand() % maxSelectable; |
|
uint32_t currIdx = startIdx; |
|
do { |
|
RefPtr<ContentParent> p = (*contentParents)[currIdx]; |
|
NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sNonAppContntParents?"); |
|
if (p->mOpener == aOpener) { |
|
return p.forget(); |
|
} |
|
currIdx = (currIdx + 1) % maxSelectable; |
|
} while (currIdx != startIdx); |
|
} |
|
|
|
// Try to take and transform the preallocated process into browser. |
|
RefPtr<ContentParent> p = PreallocatedProcessManager::Take(); |
|
if (p) { |
|
p->TransformPreallocatedIntoBrowser(aOpener); |
|
} else { |
|
// Failed in using the preallocated process: fork from the chrome process. |
|
p = new ContentParent(/* app = */ nullptr, |
|
aOpener, |
|
aForBrowserElement, |
|
/* isForPreallocated = */ false); |
|
|
|
if (!p->LaunchSubprocess(aPriority)) { |
|
return nullptr; |
|
} |
|
|
|
p->Init(); |
|
} |
|
|
|
p->mLargeAllocationProcess = aLargeAllocationProcess; |
|
|
|
p->ForwardKnownInfo(); |
|
|
|
contentParents->AppendElement(p); |
|
return p.forget(); |
|
} |
|
|
|
/*static*/ ProcessPriority |
|
ContentParent::GetInitialProcessPriority(Element* aFrameElement) |
|
{ |
|
// Frames with mozapptype == critical which are expecting a system message |
|
// get FOREGROUND_HIGH priority. |
|
|
|
if (!aFrameElement) { |
|
return PROCESS_PRIORITY_FOREGROUND; |
|
} |
|
|
|
if (aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype, |
|
NS_LITERAL_STRING("inputmethod"), eCaseMatters)) { |
|
return PROCESS_PRIORITY_FOREGROUND_KEYBOARD; |
|
} else if (!aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype, |
|
NS_LITERAL_STRING("critical"), eCaseMatters)) { |
|
return PROCESS_PRIORITY_FOREGROUND; |
|
} |
|
|
|
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(aFrameElement); |
|
if (!browserFrame) { |
|
return PROCESS_PRIORITY_FOREGROUND; |
|
} |
|
|
|
return PROCESS_PRIORITY_FOREGROUND; |
|
} |
|
|
|
#if defined(XP_WIN) |
|
extern const wchar_t* kPluginWidgetContentParentProperty; |
|
|
|
/*static*/ void |
|
ContentParent::SendAsyncUpdate(nsIWidget* aWidget) |
|
{ |
|
if (!aWidget || aWidget->Destroyed()) { |
|
return; |
|
} |
|
// Fire off an async request to the plugin to paint its window |
|
HWND hwnd = (HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW); |
|
NS_ASSERTION(hwnd, "Expected valid hwnd value."); |
|
ContentParent* cp = reinterpret_cast<ContentParent*>( |
|
::GetPropW(hwnd, kPluginWidgetContentParentProperty)); |
|
if (cp && !cp->IsDestroyed()) { |
|
Unused << cp->SendUpdateWindow((uintptr_t)hwnd); |
|
} |
|
} |
|
#endif // defined(XP_WIN) |
|
|
|
bool |
|
ContentParent::PreallocatedProcessReady() |
|
{ |
|
return true; |
|
} |
|
|
|
bool |
|
ContentParent::RecvCreateChildProcess(const IPCTabContext& aContext, |
|
const hal::ProcessPriority& aPriority, |
|
const TabId& aOpenerTabId, |
|
ContentParentId* aCpId, |
|
bool* aIsForApp, |
|
bool* aIsForBrowser, |
|
TabId* aTabId) |
|
{ |
|
#if 0 |
|
if (!CanOpenBrowser(aContext)) { |
|
return false; |
|
} |
|
#endif |
|
RefPtr<ContentParent> cp; |
|
MaybeInvalidTabContext tc(aContext); |
|
if (!tc.IsValid()) { |
|
NS_ERROR(nsPrintfCString("Received an invalid TabContext from " |
|
"the child process. (%s)", |
|
tc.GetInvalidReason()).get()); |
|
return false; |
|
} |
|
|
|
nsCOMPtr<mozIApplication> ownApp = tc.GetTabContext().GetOwnApp(); |
|
if (ownApp) { |
|
cp = GetNewOrPreallocatedAppProcess(ownApp, aPriority, this); |
|
} |
|
else { |
|
cp = GetNewOrUsedBrowserProcess(/* isBrowserElement = */ true, |
|
aPriority, this); |
|
} |
|
|
|
if (!cp) { |
|
*aCpId = 0; |
|
*aIsForApp = false; |
|
*aIsForBrowser = false; |
|
return true; |
|
} |
|
|
|
*aCpId = cp->ChildID(); |
|
*aIsForApp = cp->IsForApp(); |
|
*aIsForBrowser = cp->IsForBrowser(); |
|
|
|
ContentProcessManager *cpm = ContentProcessManager::GetSingleton(); |
|
cpm->AddContentProcess(cp, this->ChildID()); |
|
|
|
if (cpm->AddGrandchildProcess(this->ChildID(), cp->ChildID())) { |
|
// Pre-allocate a TabId here to save one time IPC call at app startup. |
|
*aTabId = AllocateTabId(aOpenerTabId, aContext, cp->ChildID()); |
|
return (*aTabId != 0); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
bool |
|
ContentParent::RecvBridgeToChildProcess(const ContentParentId& aCpId) |
|
{ |
|
ContentProcessManager *cpm = ContentProcessManager::GetSingleton(); |
|
ContentParent* cp = cpm->GetContentProcessById(aCpId); |
|
|
|
if (cp) { |
|
ContentParentId parentId; |
|
if (cpm->GetParentProcessId(cp->ChildID(), &parentId) && |
|
parentId == this->ChildID()) { |
|
return NS_SUCCEEDED(PContentBridge::Bridge(this, cp)); |
|
} |
|
} |
|
|
|
// You can't bridge to a process you didn't open! |
|
KillHard("BridgeToChildProcess"); |
|
return false; |
|
} |
|
|
|
static nsIDocShell* GetOpenerDocShellHelper(Element* aFrameElement) |
|
{ |
|
// Propagate the private-browsing status of the element's parent |
|
// docshell to the remote docshell, via the chrome flags. |
|
nsCOMPtr<Element> frameElement = do_QueryInterface(aFrameElement); |
|
MOZ_ASSERT(frameElement); |
|
nsPIDOMWindowOuter* win = frameElement->OwnerDoc()->GetWindow(); |
|
if (!win) { |
|
NS_WARNING("Remote frame has no window"); |
|
return nullptr; |
|
} |
|
nsIDocShell* docShell = win->GetDocShell(); |
|
if (!docShell) { |
|
NS_WARNING("Remote frame has no docshell"); |
|
return nullptr; |
|
} |
|
|
|
return docShell; |
|
} |
|
|
|
bool |
|
ContentParent::RecvCreateGMPService() |
|
{ |
|
return PGMPService::Open(this); |
|
} |
|
|
|
bool |
|
ContentParent::RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv, uint32_t* aRunID) |
|
{ |
|
*aRv = NS_OK; |
|
return mozilla::plugins::SetupBridge(aPluginId, this, false, aRv, aRunID); |
|
} |
|
|
|
bool |
|
ContentParent::RecvUngrabPointer(const uint32_t& aTime) |
|
{ |
|
#if !defined(MOZ_WIDGET_GTK) |
|
NS_RUNTIMEABORT("This message only makes sense on GTK platforms"); |
|
return false; |
|
#else |
|
gdk_pointer_ungrab(aTime); |
|
return true; |
|
#endif |
|
} |
|
|
|
bool |
|
ContentParent::RecvRemovePermission(const IPC::Principal& aPrincipal, |
|
const nsCString& aPermissionType, |
|
nsresult* aRv) { |
|
*aRv = Permissions::RemovePermission(aPrincipal, aPermissionType.get()); |
|
return true; |
|
} |
|
|
|
bool |
|
ContentParent::RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv) |
|
{ |
|
*aRv = NS_OK; |
|
// We don't need to get the run ID for the plugin, since we already got it |
|
// in the first call to SetupBridge in RecvLoadPlugin, so we pass in a dummy |
|
// pointer and just throw it away. |
|
uint32_t dummy = 0; |
|
return mozilla::plugins::SetupBridge(aPluginId, this, true, aRv, &dummy); |
|
} |
|
|
|
bool |
|
ContentParent::RecvGetBlocklistState(const uint32_t& aPluginId, |
|
uint32_t* aState) |
|
{ |
|
*aState = nsIBlocklistService::STATE_BLOCKED; |
|
|
|
RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst(); |
|
if (!pluginHost) { |
|
NS_WARNING("Plugin host not found"); |
|
return false; |
|
} |
|
nsPluginTag* tag = pluginHost->PluginWithId(aPluginId); |
|
|
|
if (!tag) { |
|
// Default state is blocked anyway |
|
NS_WARNING("Plugin tag not found. This should never happen, but to avoid a crash we're forcibly blocking it"); |
|
return true; |
|
} |
|
|
|
return NS_SUCCEEDED(tag->GetBlocklistState(aState)); |
|
} |
|
|
|
bool |
|
ContentParent::RecvFindPlugins(const uint32_t& aPluginEpoch, |
|
nsresult* aRv, |
|
nsTArray<PluginTag>* aPlugins, |
|
uint32_t* aNewPluginEpoch) |
|
{ |
|
*aRv = mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch); |
|
return true; |
|
} |
|
|
|
/*static*/ TabParent* |
|
ContentParent::CreateBrowserOrApp(const TabContext& aContext, |
|
Element* aFrameElement, |
|
ContentParent* aOpenerContentParent, |
|
bool aFreshProcess) |
|
{ |
|
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); |
|
|
|
if (!sCanLaunchSubprocesses) { |
|
return nullptr; |
|
} |
|
|
|
if (TabParent* parent = TabParent::GetNextTabParent()) { |
|
parent->SetOwnerElement(aFrameElement); |
|
return parent; |
|
} |
|
|
|
ProcessPriority initialPriority = GetInitialProcessPriority(aFrameElement); |
|
bool isInContentProcess = !XRE_IsParentProcess(); |
|
TabId tabId; |
|
|
|
nsIDocShell* docShell = GetOpenerDocShellHelper(aFrameElement); |
|
TabId openerTabId; |
|
if (docShell) { |
|
openerTabId = TabParent::GetTabIdFrom(docShell); |
|
} |
|
|
|
if (aContext.IsMozBrowserElement() || !aContext.HasOwnApp()) { |
|
RefPtr<nsIContentParent> constructorSender; |
|
if (isInContentProcess) { |
|
MOZ_ASSERT(aContext.IsMozBrowserElement()); |
|
constructorSender = CreateContentBridgeParent(aContext, initialPriority, |
|
openerTabId, &tabId); |
|
} else { |
|
if (aOpenerContentParent) { |
|
constructorSender = aOpenerContentParent; |
|
} else { |
|
constructorSender = |
|
GetNewOrUsedBrowserProcess(aContext.IsMozBrowserElement(), |
|
initialPriority, |
|
nullptr, |
|
aFreshProcess); |
|
if (!constructorSender) { |
|
return nullptr; |
|
} |
|
} |
|
tabId = AllocateTabId(openerTabId, |
|
aContext.AsIPCTabContext(), |
|
constructorSender->ChildID()); |
|
} |
|
if (constructorSender) { |
|
nsCOMPtr<nsIDocShellTreeOwner> treeOwner; |
|
docShell->GetTreeOwner(getter_AddRefs(treeOwner)); |
|
if (!treeOwner) { |
|
return nullptr; |
|
} |
|
|
|
nsCOMPtr<nsIWebBrowserChrome> wbc = do_GetInterface(treeOwner); |
|
if (!wbc) { |
|
return nullptr; |
|
} |
|
uint32_t chromeFlags = 0; |
|
wbc->GetChromeFlags(&chromeFlags); |
|
|
|
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell); |
|
if (loadContext && loadContext->UsePrivateBrowsing()) { |
|
chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW; |
|
} |
|
if (docShell->GetAffectPrivateSessionLifetime()) { |
|
chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME; |
|
} |
|
|
|
if (tabId == 0) { |
|
return nullptr; |
|
} |
|
RefPtr<TabParent> tp(new TabParent(constructorSender, tabId, |
|
aContext, chromeFlags)); |
|
tp->SetInitedByParent(); |
|
|
|
PBrowserParent* browser = |
|
constructorSender->SendPBrowserConstructor( |
|
// DeallocPBrowserParent() releases this ref. |
|
tp.forget().take(), tabId, |
|
aContext.AsIPCTabContext(), |
|
chromeFlags, |
|
constructorSender->ChildID(), |
|
constructorSender->IsForApp(), |
|
constructorSender->IsForBrowser()); |
|
|
|
if (aFreshProcess) { |
|
Unused << browser->SendSetFreshProcess(); |
|
} |
|
|
|
if (browser) { |
|
RefPtr<TabParent> constructedTabParent = TabParent::GetFrom(browser); |
|
constructedTabParent->SetOwnerElement(aFrameElement); |
|
return constructedTabParent; |
|
} |
|
} |
|
return nullptr; |
|
} |
|
|
|
// If we got here, we have an app and we're not a browser element. ownApp |
|
// shouldn't be null, because we otherwise would have gone into the |
|
// !HasOwnApp() branch above. |
|
RefPtr<nsIContentParent> parent; |
|
bool reused = false; |
|
bool tookPreallocated = false; |
|
nsAutoString manifestURL; |
|
|
|
if (isInContentProcess) { |
|
parent = CreateContentBridgeParent(aContext, |
|
initialPriority, |
|
openerTabId, |
|
&tabId); |
|
} |
|
else { |
|
nsCOMPtr<mozIApplication> ownApp = aContext.GetOwnApp(); |
|
|
|
if (!sAppContentParents) { |
|
sAppContentParents = |
|
new nsDataHashtable<nsStringHashKey, ContentParent*>(); |
|
} |
|
|
|
// Each app gets its own ContentParent instance unless it shares it with |
|
// a parent app. |
|
if (NS_FAILED(ownApp->GetManifestURL(manifestURL))) { |
|
NS_ERROR("Failed to get manifest URL"); |
|
return nullptr; |
|
} |
|
|
|
RefPtr<ContentParent> p = sAppContentParents->Get(manifestURL); |
|
|
|
if (!p && Preferences::GetBool("dom.ipc.reuse_parent_app")) { |
|
nsAutoString parentAppManifestURL; |
|
aFrameElement->GetAttr(kNameSpaceID_None, |
|
nsGkAtoms::parentapp, parentAppManifestURL); |
|
nsAdoptingString systemAppManifestURL = |
|
Preferences::GetString("b2g.system_manifest_url"); |
|
nsCOMPtr<nsIAppsService> appsService = |
|
do_GetService(APPS_SERVICE_CONTRACTID); |
|
if (!parentAppManifestURL.IsEmpty() && |
|
!parentAppManifestURL.Equals(systemAppManifestURL) && |
|
appsService) { |
|
nsCOMPtr<mozIApplication> parentApp; |
|
nsCOMPtr<mozIApplication> app; |
|
appsService->GetAppByManifestURL(parentAppManifestURL, |
|
getter_AddRefs(parentApp)); |
|
appsService->GetAppByManifestURL(manifestURL, |
|
getter_AddRefs(app)); |
|
|
|
// Only let certified apps re-use the same process. |
|
unsigned short parentAppStatus = 0; |
|
unsigned short appStatus = 0; |
|
if (app && |
|
NS_SUCCEEDED(app->GetAppStatus(&appStatus)) && |
|
appStatus == nsIPrincipal::APP_STATUS_CERTIFIED && |
|
parentApp && |
|
NS_SUCCEEDED(parentApp->GetAppStatus(&parentAppStatus)) && |
|
parentAppStatus == nsIPrincipal::APP_STATUS_CERTIFIED) { |
|
// Check if we can re-use the process of the parent app. |
|
p = sAppContentParents->Get(parentAppManifestURL); |
|
} |
|
} |
|
} |
|
|
|
if (p) { |
|
// Check that the process is still alive and set its priority. |
|
// Hopefully the process won't die after this point, if this call |
|
// succeeds. |
|
if (!p->SetPriorityAndCheckIsAlive(initialPriority)) { |
|
p = nullptr; |
|
} |
|
} |
|
|
|
reused = !!p; |
|
if (!p) { |
|
p = GetNewOrPreallocatedAppProcess(ownApp, initialPriority, nullptr, |
|
&tookPreallocated); |
|
MOZ_ASSERT(p); |
|
sAppContentParents->Put(manifestURL, p); |
|
} |
|
tabId = AllocateTabId(openerTabId, aContext.AsIPCTabContext(), |
|
p->ChildID()); |
|
parent = static_cast<nsIContentParent*>(p); |
|
} |
|
|
|
if (!parent || (tabId == 0)) { |
|
return nullptr; |
|
} |
|
|
|
uint32_t chromeFlags = 0; |
|
|
|
RefPtr<TabParent> tp = new TabParent(parent, tabId, aContext, chromeFlags); |
|
tp->SetInitedByParent(); |
|
PBrowserParent* browser = parent->SendPBrowserConstructor( |
|
// DeallocPBrowserParent() releases this ref. |
|
RefPtr<TabParent>(tp).forget().take(), |
|
tabId, |
|
aContext.AsIPCTabContext(), |
|
chromeFlags, |
|
parent->ChildID(), |
|
parent->IsForApp(), |
|
parent->IsForBrowser()); |
|
|
|
if (aFreshProcess) { |
|
Unused << browser->SendSetFreshProcess(); |
|
} |
|
|
|
if (browser) { |
|
RefPtr<TabParent> constructedTabParent = TabParent::GetFrom(browser); |
|
constructedTabParent->SetOwnerElement(aFrameElement); |
|
} |
|
|
|
if (isInContentProcess) { |
|
// Just return directly without the following check in content process. |
|
return TabParent::GetFrom(browser); |
|
} |
|
|
|
if (!browser) { |
|
// We failed to actually start the PBrowser. This can happen if the |
|
// other process has already died. |
|
if (!reused) { |
|
// Don't leave a broken ContentParent in the hashtable. |
|
parent->AsContentParent()->KillHard("CreateBrowserOrApp"); |
|
sAppContentParents->Remove(manifestURL); |
|
parent = nullptr; |
|
} |
|
|
|
// If we took the preallocated process and it was already dead, try |
|
// again with a non-preallocated process. We can be sure this won't |
|
// loop forever, because the next time through there will be no |
|
// preallocated process to take. |
|
if (tookPreallocated) { |
|
return ContentParent::CreateBrowserOrApp(aContext, aFrameElement, |
|
aOpenerContentParent); |
|
} |
|
|
|
// Otherwise just give up. |
|
return nullptr; |
|
} |
|
|
|
return TabParent::GetFrom(browser); |
|
} |
|
|
|
/*static*/ ContentBridgeParent* |
|
ContentParent::CreateContentBridgeParent(const TabContext& aContext, |
|
const hal::ProcessPriority& aPriority, |
|
const TabId& aOpenerTabId, |
|
/*out*/ TabId* aTabId) |
|
{ |
|
MOZ_ASSERT(aTabId); |
|
|
|
ContentChild* child = ContentChild::GetSingleton(); |
|
ContentParentId cpId; |
|
bool isForApp; |
|
bool isForBrowser; |
|
if (!child->SendCreateChildProcess(aContext.AsIPCTabContext(), |
|
aPriority, |
|
aOpenerTabId, |
|
&cpId, |
|
&isForApp, |
|
&isForBrowser, |
|
aTabId)) { |
|
return nullptr; |
|
} |
|
if (cpId == 0) { |
|
return nullptr; |
|
} |
|
if (!child->SendBridgeToChildProcess(cpId)) { |
|
return nullptr; |
|
} |
|
ContentBridgeParent* parent = child->GetLastBridge(); |
|
parent->SetChildID(cpId); |
|
parent->SetIsForApp(isForApp); |
|
parent->SetIsForBrowser(isForBrowser); |
|
return parent; |
|
} |
|
|
|
void |
|
ContentParent::GetAll(nsTArray<ContentParent*>& aArray) |
|
{ |
|
aArray.Clear(); |
|
|
|
for (auto* cp : AllProcesses(eLive)) { |
|
aArray.AppendElement(cp); |
|
} |
|
} |
|
|
|
void |
|
ContentParent::GetAllEvenIfDead(nsTArray<ContentParent*>& aArray) |
|
{ |
|
aArray.Clear(); |
|
|
|
for (auto* cp : AllProcesses(eAll)) { |
|
aArray.AppendElement(cp); |
|
} |
|
} |
|
|
|
void |
|
ContentParent::Init() |
|
{ |
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); |
|
if (obs) { |
|
size_t length = ArrayLength(sObserverTopics); |
|
for (size_t i = 0; i < length; ++i) { |
|
obs->AddObserver(this, sObserverTopics[i], false); |
|
} |
|
} |
|
Preferences::AddStrongObserver(this, ""); |
|
if (obs) { |
|
nsAutoString cpId; |
|
cpId.AppendInt(static_cast<uint64_t>(this->ChildID())); |
|
obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-created", cpId.get()); |
|
} |
|
|
|
#ifdef ACCESSIBILITY |
|
// If accessibility is running in chrome process then start it in content |
|
// process. |
|
if (nsIPresShell::IsAccessibilityActive()) { |
|
#if !defined(XP_WIN) |
|
Unused << SendActivateA11y(0); |
|
#else |
|
// On Windows we currently only enable a11y in the content process |
|
// for testing purposes. |
|
if (Preferences::GetBool(kForceEnableE10sPref, false)) { |
|
Unused << SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID())); |
|
} |
|
#endif |
|
} |
|
#endif |
|
|
|
#ifdef MOZ_ENABLE_PROFILER_SPS |
|
nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1")); |
|
bool profilerActive = false; |
|
DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive); |
|
MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
|
|
if (profilerActive) { |
|
nsCOMPtr<nsIProfilerStartParams> currentProfilerParams; |
|
rv = profiler->GetStartParams(getter_AddRefs(currentProfilerParams)); |
|
MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
|
|
nsCOMPtr<nsISupports> gatherer; |
|
rv = profiler->GetProfileGatherer(getter_AddRefs(gatherer)); |
|
MOZ_ASSERT(NS_SUCCEEDED(rv)); |
|
mGatherer = static_cast<ProfileGatherer*>(gatherer.get()); |
|
|
|
StartProfiler(currentProfilerParams); |
|
} |
|
#endif |
|
|
|
RefPtr<GeckoMediaPluginServiceParent> gmps(GeckoMediaPluginServiceParent::GetSingleton()); |
|
gmps->UpdateContentProcessGMPCapabilities(); |
|
} |
|
|
|
void |
|
ContentParent::ForwardKnownInfo() |
|
{ |
|
MOZ_ASSERT(mMetamorphosed); |
|
if (!mMetamorphosed) { |
|
return; |
|
} |
|
#ifdef MOZ_WIDGET_GONK |
|
InfallibleTArray<VolumeInfo> volumeInfo; |
|
RefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton(); |
|
if (vs) { |
|
vs->GetVolumesForIPC(&volumeInfo); |
|
Unused << SendVolumes(volumeInfo); |
|
} |
|
#endif /* MOZ_WIDGET_GONK */ |
|
} |
|
|
|
namespace { |
|
|
|
class RemoteWindowContext final : public nsIRemoteWindowContext |
|
, public nsIInterfaceRequestor |
|
{ |
|
public: |
|
explicit RemoteWindowContext(TabParent* aTabParent) |
|
: mTabParent(aTabParent) |
|
{ |
|
} |
|
|
|
NS_DECL_ISUPPORTS |
|
NS_DECL_NSIINTERFACEREQUESTOR |
|
NS_DECL_NSIREMOTEWINDOWCONTEXT |
|
|
|
private: |
|
~RemoteWindowContext(); |
|
RefPtr<TabParent> mTabParent; |
|
}; |
|
|
|
NS_IMPL_ISUPPORTS(RemoteWindowContext, nsIRemoteWindowContext, nsIInterfaceRequestor) |
|
|
|
RemoteWindowContext::~RemoteWindowContext() |
|
{ |
|
} |
|
|
|
NS_IMETHODIMP |
|
RemoteWindowContext::GetInterface(const nsIID& aIID, void** aSink) |
|
{ |
|
return QueryInterface(aIID, aSink); |
|
} |
|
|
|
NS_IMETHODIMP |
|
RemoteWindowContext::OpenURI(nsIURI* aURI) |
|
{ |
|
mTabParent->LoadURL(aURI); |
|
return NS_OK; |
|
} |
|
|
|
} // namespace |
|
|
|
bool |
|
ContentParent::SetPriorityAndCheckIsAlive(ProcessPriority aPriority) |
|
{ |
|
ProcessPriorityManager::SetProcessPriority(this, aPriority); |
|
|
|
// Now that we've set this process's priority, check whether the process is |
|
// still alive. Hopefully we've set the priority to FOREGROUND*, so the |
|
// process won't unexpectedly crash after this point! |
|
// |
|
// Bug 943174: use waitid() with WNOWAIT so that, if the process |
|
// did exit, we won't consume its zombie and confuse the |
|
// GeckoChildProcessHost dtor. |
|
#ifdef MOZ_WIDGET_GONK |
|
siginfo_t info; |
|
info.si_pid = 0; |
|
if (waitid(P_PID, Pid(), &info, WNOWAIT | WNOHANG | WEXITED) == 0 |
|
&& info.si_pid != 0) { |
|
return false; |
|
} |
|
#endif |
|
|
|
return true; |
|
} |
|
|
|
// Helper for ContentParent::TransformPreallocatedIntoApp. |
|
static void |
|
TryGetNameFromManifestURL(const nsAString& aManifestURL, |
|
nsAString& aName) |
|
{ |
|
aName.Truncate(); |
|
if (aManifestURL.IsEmpty() || |
|
aManifestURL == MAGIC_PREALLOCATED_APP_MANIFEST_URL) { |
|
return; |
|
} |
|
|
|
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID); |
|
NS_ENSURE_TRUE_VOID(appsService); |
|
|
|
nsCOMPtr<mozIApplication> app; |
|
appsService->GetAppByManifestURL(aManifestURL, getter_AddRefs(app)); |
|
|
|
if (!app) { |
|
return; |
|
} |
|
|
|
app->GetName(aName); |
|
} |
|
|
|
void |
|
ContentParent::TransformPreallocatedIntoApp(ContentParent* aOpener, |
|
const nsAString& aAppManifestURL) |
|
{ |
|
MOZ_ASSERT(IsPreallocated()); |
|
mMetamorphosed = true; |
|
mOpener = aOpener; |
|
mAppManifestURL = aAppManifestURL; |
|
TryGetNameFromManifestURL(aAppManifestURL, mAppName); |
|
} |
|
|
|
void |
|
ContentParent::TransformPreallocatedIntoBrowser(ContentParent* aOpener) |
|
{ |
|
// Reset mAppManifestURL, mIsForBrowser and mOSPrivileges for browser. |
|
mMetamorphosed = true; |
|
mOpener = aOpener; |
|
mAppManifestURL.Truncate(); |
|
mIsForBrowser = true; |
|
} |
|
|
|
void |
|
ContentParent::ShutDownProcess(ShutDownMethod aMethod) |
|
{ |
|
// Shutting down by sending a shutdown message works differently than the |
|
// other methods. We first call Shutdown() in the child. After the child is |
|
// ready, it calls FinishShutdown() on us. Then we close the channel. |
|
if (aMethod == SEND_SHUTDOWN_MESSAGE) { |
|
if (mIPCOpen && !mShutdownPending && SendShutdown()) { |
|
mShutdownPending = true; |
|
// Start the force-kill timer if we haven't already. |
|
StartForceKillTimer(); |
|
} |
|
|
|
// If call was not successful, the channel must have been broken |
|
// somehow, and we will clean up the error in ActorDestroy. |
|
return; |
|
} |
|
|
|
using mozilla::dom::quota::QuotaManagerService; |
|
|
|
if (QuotaManagerService* quotaManagerService = QuotaManagerService::Get()) { |
|
quotaManagerService->AbortOperationsForProcess(mChildID); |
|
} |
|
|
|
// If Close() fails with an error, we'll end up back in this function, but |
|
// with aMethod = CLOSE_CHANNEL_WITH_ERROR. |
|
|
|
if (aMethod == CLOSE_CHANNEL && !mCalledClose) { |
|
// Close() can only be called once: It kicks off the destruction |
|
// sequence. |
|
mCalledClose = true; |
|
Close(); |
|
} |
|
|
|
const ManagedContainer<POfflineCacheUpdateParent>& ocuParents = |
|
ManagedPOfflineCacheUpdateParent(); |
|
for (auto iter = ocuParents.ConstIter(); !iter.Done(); iter.Next()) { |
|
RefPtr<mozilla::docshell::OfflineCacheUpdateParent> ocuParent = |
|
static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(iter.Get()->GetKey()); |
|
ocuParent->StopSendingMessagesToChild(); |
|
} |
|
|
|
// NB: must MarkAsDead() here so that this isn't accidentally |
|
// returned from Get*() while in the midst of shutdown. |
|
MarkAsDead(); |
|
|
|
// A ContentParent object might not get freed until after XPCOM shutdown has |
|
// shut down the cycle collector. But by then it's too late to release any |
|
// CC'ed objects, so we need to null them out here, while we still can. See |
|
// bug 899761. |
|
ShutDownMessageManager(); |
|
} |
|
|
|
bool |
|
ContentParent::RecvFinishShutdown() |
|
{ |
|
// At this point, we already called ShutDownProcess once with |
|
// SEND_SHUTDOWN_MESSAGE. To actually close the channel, we call |
|
// ShutDownProcess again with CLOSE_CHANNEL. |
|
MOZ_ASSERT(mShutdownPending); |
|
ShutDownProcess(CLOSE_CHANNEL); |
|
return true; |
|
} |
|
|
|
void |
|
ContentParent::ShutDownMessageManager() |
|
{ |
|
if (!mMessageManager) { |
|
return; |
|
} |
|
|
|
mMessageManager->ReceiveMessage( |
|
static_cast<nsIContentFrameMessageManager*>(mMessageManager.get()), nullptr, |
|
CHILD_PROCESS_SHUTDOWN_MESSAGE, false, |
|
nullptr, nullptr, nullptr, nullptr); |
|
|
|
mMessageManager->Disconnect(); |
|
mMessageManager = nullptr; |
|
} |
|
|
|
void |
|
ContentParent::MarkAsDead() |
|
{ |
|
if (!mAppManifestURL.IsEmpty()) { |
|
if (sAppContentParents) { |
|
sAppContentParents->Remove(mAppManifestURL); |
|
if (!sAppContentParents->Count()) { |
|
delete sAppContentParents; |
|
sAppContentParents = nullptr; |
|
} |
|
} |
|
} else { |
|
if (sNonAppContentParents) { |
|
sNonAppContentParents->RemoveElement(this); |
|
if (!sNonAppContentParents->Length()) { |
|
delete sNonAppContentParents; |
|
sNonAppContentParents = nullptr; |
|
} |
|
} |
|
|
|
if (sLargeAllocationContentParents) { |
|
sLargeAllocationContentParents->RemoveElement(this); |
|
if (!sLargeAllocationContentParents->Length()) { |
|
delete sLargeAllocationContentParents; |
|
sLargeAllocationContentParents = nullptr; |
|
} |
|
} |
|
} |
|
|
|
if (sPrivateContent) { |
|
sPrivateContent->RemoveElement(this); |
|
if (!sPrivateContent->Length()) { |
|
delete sPrivateContent; |
|
sPrivateContent = nullptr; |
|
} |
|
} |
|
|
|
mIsAlive = false; |
|
} |
|
|
|
void |
|
ContentParent::OnChannelError() |
|
{ |
|
RefPtr<ContentParent> content(this); |
|
PContentParent::OnChannelError(); |
|
} |
|
|
|
void |
|
ContentParent::OnChannelConnected(int32_t pid) |
|
{ |
|
SetOtherProcessId(pid); |
|
|
|
#if defined(ANDROID) || defined(LINUX) |
|
// Check nice preference |
|
int32_t nice = Preferences::GetInt("dom.ipc.content.nice", 0); |
|
|
|
// Environment variable overrides preference |
|
char* relativeNicenessStr = getenv("MOZ_CHILD_PROCESS_RELATIVE_NICENESS"); |
|
if (relativeNicenessStr) { |
|
nice = atoi(relativeNicenessStr); |
|
} |
|
|
|
/* make the GUI thread have higher priority on single-cpu devices */ |
|
nsCOMPtr<nsIPropertyBag2> infoService = do_GetService(NS_SYSTEMINFO_CONTRACTID); |
|
if (infoService) { |
|
int32_t cpus; |
|
nsresult rv = infoService->GetPropertyAsInt32(NS_LITERAL_STRING("cpucount"), &cpus); |
|
if (NS_FAILED(rv)) { |
|
cpus = 1; |
|
} |
|
if (nice != 0 && cpus == 1) { |
|
setpriority(PRIO_PROCESS, pid, getpriority(PRIO_PROCESS, pid) + nice); |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
void |
|
ContentParent::ProcessingError(Result aCode, const char* aReason) |
|
{ |
|
if (MsgDropped == aCode) { |
|
return; |
|
} |
|
// Other errors are big deals. |
|
KillHard(aReason); |
|
} |
|
|
|
/* static */ |
|
bool |
|
ContentParent::AllocateLayerTreeId(TabParent* aTabParent, uint64_t* aId) |
|
{ |
|
return AllocateLayerTreeId(aTabParent->Manager()->AsContentParent(), |
|
aTabParent, aTabParent->GetTabId(), aId); |
|
} |
|
|
|
/* static */ |
|
bool |
|
ContentParent::AllocateLayerTreeId(ContentParent* aContent, |
|
TabParent* aTopLevel, const TabId& aTabId, |
|
uint64_t* aId) |
|
{ |
|
GPUProcessManager* gpu = GPUProcessManager::Get(); |
|
|
|
*aId = gpu->AllocateLayerTreeId(); |
|
|
|
if (!aContent || !aTopLevel) { |
|
return false; |
|
} |
|
|
|
gpu->MapLayerTreeId(*aId, aContent->OtherPid()); |
|
|
|
if (!gfxPlatform::AsyncPanZoomEnabled()) { |
|
return true; |
|
} |
|
|
|
return aContent->SendNotifyLayerAllocated(aTabId, *aId); |
|
} |
|
|
|
bool |
|
ContentParent::RecvAllocateLayerTreeId(const ContentParentId& aCpId, |
|
const TabId& aTabId, uint64_t* aId) |
|
{ |
|
// Protect against spoofing by a compromised child. aCpId must either |
|
// correspond to the process that this ContentParent represents or be a |
|
// child of it. |
|
ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); |
|
if (ChildID() != aCpId) { |
|
ContentParentId parent; |
|
if (!cpm->GetParentProcessId(aCpId, &parent) || |
|
ChildID() != parent) { |
|
return false; |
|
} |
|
} |
|
|
|
// GetTopLevelTabParentByProcessAndTabId will make sure that aTabId |
|
// lives in the process for aCpId. |
|
RefPtr<ContentParent> contentParent = cpm->GetContentProcessById(aCpId); |
|
RefPtr<TabParent> browserParent = |
|
cpm->GetTopLevelTabParentByProcessAndTabId(aCpId, aTabId); |
|
MOZ_ASSERT(contentParent && browserParent); |
|
|
|
return AllocateLayerTreeId(contentParent, browserParent, aTabId, aId); |
|
} |
|
|
|
bool |
|
ContentParent::RecvDeallocateLayerTreeId(const uint64_t& aId) |
|
{ |
|
GPUProcessManager* gpu = GPUProcessManager::Get(); |
|
|
|
if (!gpu->IsLayerTreeIdMapped(aId, OtherPid())) |
|
{ |
|
// You can't deallocate layer tree ids that you didn't allocate |
|
KillHard("DeallocateLayerTreeId"); |
|
} |
|
|
|
gpu->UnmapLayerTreeId(aId, OtherPid()); |
|
|
|
return true; |
|
} |
|
|
|
namespace { |
|
|
|
void |
|
DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess) |
|
{ |
|
RefPtr<DeleteTask<GeckoChildProcessHost>> task = new DeleteTask<GeckoChildProcessHost>(aSubprocess); |
|
XRE_GetIOMessageLoop()->PostTask(task.forget()); |
|
} |
|
|
|
// This runnable only exists to delegate ownership of the |
|
// ContentParent to this runnable, until it's deleted by the event |
|
// system. |
|
struct DelayedDeleteContentParentTask : public Runnable |
|
{ |
|
explicit DelayedDeleteContentParentTask(ContentParent* aObj) : mObj(aObj) { } |
|
|
|
// No-op |
|
NS_IMETHOD Run() override { return NS_OK; } |
|
|
|
RefPtr<ContentParent> mObj; |
|
}; |
|
|
|
} // namespace |
|
|
|
void |
|
ContentParent::ActorDestroy(ActorDestroyReason why) |
|
{ |
|
if (mForceKillTimer) { |
|
mForceKillTimer->Cancel(); |
|
mForceKillTimer = nullptr; |
|
} |
|
|
|
// Signal shutdown completion regardless of error state, so we can |
|
// finish waiting in the xpcom-shutdown/profile-before-change observer. |
|
mIPCOpen = false; |
|
|
|
if (mHangMonitorActor) { |
|
ProcessHangMonitor::RemoveProcess(mHangMonitorActor); |
|
mHangMonitorActor = nullptr; |
|
} |
|
|
|
RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get(); |
|
if (fss) { |
|
fss->Forget(ChildID()); |
|
} |
|
|
|
if (why == NormalShutdown && !mCalledClose) { |
|
// If we shut down normally but haven't called Close, assume somebody |
|
// else called Close on us. In that case, we still need to call |
|
// ShutDownProcess below to perform other necessary clean up. |
|
mCalledClose = true; |
|
} |
|
|
|
// Make sure we always clean up. |
|
ShutDownProcess(why == NormalShutdown ? CLOSE_CHANNEL |
|
: CLOSE_CHANNEL_WITH_ERROR); |
|
|
|
RefPtr<ContentParent> kungFuDeathGrip(this); |
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); |
|
if (obs) { |
|
size_t length = ArrayLength(sObserverTopics); |
|
for (size_t i = 0; i < length; ++i) { |
|
obs->RemoveObserver(static_cast<nsIObserver*>(this), |
|
sObserverTopics[i]); |
|
} |
|
} |
|
|
|
// remove the global remote preferences observers |
|
Preferences::RemoveObserver(this, ""); |
|
gfxVars::RemoveReceiver(this); |
|
|
|
if (GPUProcessManager* gpu = GPUProcessManager::Get()) { |
|
// Note: the manager could have shutdown already. |
|
gpu->RemoveListener(this); |
|
} |
|
|
|
RecvRemoveGeolocationListener(); |
|
|
|
mConsoleService = nullptr; |
|
|
|
#ifdef MOZ_ENABLE_PROFILER_SPS |
|
if (mGatherer && !mProfile.IsEmpty()) { |
|
mGatherer->OOPExitProfile(mProfile); |
|
} |
|
#endif |
|
|
|
if (obs) { |
|
RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag(); |
|
|
|
props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), mChildID); |
|
|
|
if (AbnormalShutdown == why) { |
|
Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT, |
|
NS_LITERAL_CSTRING("content"), 1); |
|
|
|
props->SetPropertyAsBool(NS_LITERAL_STRING("abnormal"), true); |
|
} |
|
nsAutoString cpId; |
|
cpId.AppendInt(static_cast<uint64_t>(this->ChildID())); |
|
obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", cpId.get()); |
|
} |
|
|
|
// Remove any and all idle listeners. |
|
nsCOMPtr<nsIIdleService> idleService = |
|
do_GetService("@mozilla.org/widget/idleservice;1"); |
|
MOZ_ASSERT(idleService); |
|
RefPtr<ParentIdleListener> listener; |
|
for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) { |
|
listener = static_cast<ParentIdleListener*>(mIdleListeners[i].get()); |
|
idleService->RemoveIdleObserver(listener, listener->mTime); |
|
} |
|
mIdleListeners.Clear(); |
|
|
|
MessageLoop::current()-> |
|
PostTask(NewRunnableFunction(DelayedDeleteSubprocess, mSubprocess)); |
|
mSubprocess = nullptr; |
|
|
|
// IPDL rules require actors to live on past ActorDestroy, but it |
|
// may be that the kungFuDeathGrip above is the last reference to |
|
// |this|. If so, when we go out of scope here, we're deleted and |
|
// all hell breaks loose. |
|
// |
|
// This runnable ensures that a reference to |this| lives on at |
|
// least until after the current task finishes running. |
|
NS_DispatchToCurrentThread(new DelayedDeleteContentParentTask(this)); |
|
|
|
ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); |
|
nsTArray<ContentParentId> childIDArray = |
|
cpm->GetAllChildProcessById(this->ChildID()); |
|
|
|
// Destroy any processes created by this ContentParent |
|
for(uint32_t i = 0; i < childIDArray.Length(); i++) { |
|
ContentParent* cp = cpm->GetContentProcessById(childIDArray[i]); |
|
MessageLoop::current()->PostTask(NewRunnableMethod |
|
<ShutDownMethod>(cp, |
|
&ContentParent::ShutDownProcess, |
|
SEND_SHUTDOWN_MESSAGE)); |
|
} |
|
cpm->RemoveContentProcess(this->ChildID()); |
|
|
|
if (mDriverCrashGuard) { |
|
mDriverCrashGuard->NotifyCrashed(); |
|
} |
|
|
|
// Unregister all the BlobURLs registered by the ContentChild. |
|
for (uint32_t i = 0; i < mBlobURLs.Length(); ++i) { |
|
nsHostObjectProtocolHandler::RemoveDataEntry(mBlobURLs[i]); |
|
} |
|
|
|
mBlobURLs.Clear(); |
|
|
|
#if defined(XP_WIN32) && defined(ACCESSIBILITY) |
|
a11y::AccessibleWrap::ReleaseContentProcessIdFor(ChildID()); |
|
#endif |
|
} |
|
|
|
void |
|
ContentParent::NotifyTabDestroying(const TabId& aTabId, |
|
const ContentParentId& aCpId) |
|
{ |
|
if (XRE_IsParentProcess()) { |
|
// There can be more than one PBrowser for a given app process |
|
// because of popup windows. PBrowsers can also destroy |
|
// concurrently. When all the PBrowsers are destroying, kick off |
|
// another task to ensure the child process *really* shuts down, |
|
// even if the PBrowsers themselves never finish destroying. |
|
ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); |
|
ContentParent* cp = cpm->GetContentProcessById(aCpId); |
|
if (!cp) { |
|
return; |
|
} |
|
++cp->mNumDestroyingTabs; |
|
nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(aCpId); |
|
if (static_cast<size_t>(cp->mNumDestroyingTabs) != tabIds.Length()) { |
|
return; |
|
} |
|
|
|
uint32_t numberOfParents = sNonAppContentParents ? sNonAppContentParents->Length() : 0; |
|
int32_t processesToKeepAlive = Preferences::GetInt("dom.ipc.keepProcessesAlive", 0); |
|
if (!cp->mLargeAllocationProcess && static_cast<int32_t>(numberOfParents) <= processesToKeepAlive) { |
|
return; |
|
} |
|
|
|
// We're dying now, so prevent this content process from being |
|
// recycled during its shutdown procedure. |
|
cp->MarkAsDead(); |
|
cp->StartForceKillTimer(); |
|
} else { |
|
ContentChild::GetSingleton()->SendNotifyTabDestroying(aTabId, aCpId); |
|
} |
|
} |
|
|
|
void |
|
ContentParent::StartForceKillTimer() |
|
{ |
|
if (mForceKillTimer || !mIPCOpen) { |
|
return; |
|
} |
|
|
|
int32_t timeoutSecs = Preferences::GetInt("dom.ipc.tabs.shutdownTimeoutSecs", 5); |
|
if (timeoutSecs > 0) { |
|
mForceKillTimer = do_CreateInstance("@mozilla.org/timer;1"); |
|
MOZ_ASSERT(mForceKillTimer); |
|
mForceKillTimer->InitWithFuncCallback(ContentParent::ForceKillTimerCallback, |
|
this, |
|
timeoutSecs * 1000, |
|
nsITimer::TYPE_ONE_SHOT); |
|
} |
|
} |
|
|
|
void |
|
ContentParent::NotifyTabDestroyed(const TabId& aTabId, |
|
bool aNotifiedDestroying) |
|
{ |
|
if (aNotifiedDestroying) { |
|
--mNumDestroyingTabs; |
|
} |
|
|
|
nsTArray<PContentPermissionRequestParent*> parentArray = |
|
nsContentPermissionUtils::GetContentPermissionRequestParentById(aTabId); |
|
|
|
// Need to close undeleted ContentPermissionRequestParents before tab is closed. |
|
for (auto& permissionRequestParent : parentArray) { |
|
Unused << PContentPermissionRequestParent::Send__delete__(permissionRequestParent); |
|
} |
|
|
|
// There can be more than one PBrowser for a given app process |
|
// because of popup windows. When the last one closes, shut |
|
// us down. |
|
ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); |
|
nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(this->ChildID()); |
|
|
|
// We might want to keep alive some content processes for testing, because of performance |
|
// reasons, but we don't want to alter behavior if the pref is not set. |
|
uint32_t numberOfParents = sNonAppContentParents ? sNonAppContentParents->Length() : 0; |
|
int32_t processesToKeepAlive = Preferences::GetInt("dom.ipc.keepProcessesAlive", 0); |
|
bool shouldKeepAliveAny = !mLargeAllocationProcess && processesToKeepAlive > 0; |
|
bool shouldKeepAliveThis = shouldKeepAliveAny && static_cast<int32_t>(numberOfParents) <= processesToKeepAlive; |
|
|
|
if (tabIds.Length() == 1 && !shouldKeepAliveThis) { |
|
// In the case of normal shutdown, send a shutdown message to child to |
|
// allow it to perform shutdown tasks. |
|
MessageLoop::current()->PostTask(NewRunnableMethod |
|
<ShutDownMethod>(this, |
|
&ContentParent::ShutDownProcess, |
|
SEND_SHUTDOWN_MESSAGE)); |
|
} |
|
} |
|
|
|
jsipc::CPOWManager* |
|
ContentParent::GetCPOWManager() |
|
{ |
|
if (PJavaScriptParent* p = LoneManagedOrNullAsserts(ManagedPJavaScriptParent())) { |
|
return CPOWManagerFor(p); |
|
} |
|
return nullptr; |
|
} |
|
|
|
TestShellParent* |
|
ContentParent::CreateTestShell() |
|
{ |
|
return static_cast<TestShellParent*>(SendPTestShellConstructor()); |
|
} |
|
|
|
bool |
|
ContentParent::DestroyTestShell(TestShellParent* aTestShell) |
|
{ |
|
return PTestShellParent::Send__delete__(aTestShell); |
|
} |
|
|
|
TestShellParent* |
|
ContentParent::GetTestShellSingleton() |
|
{ |
|
PTestShellParent* p = LoneManagedOrNullAsserts(ManagedPTestShellParent()); |
|
return static_cast<TestShellParent*>(p); |
|
} |
|
|
|
void |
|
ContentParent::InitializeMembers() |
|
{ |
|
mSubprocess = nullptr; |
|
mChildID = gContentChildID++; |
|
mGeolocationWatchID = -1; |
|
mNumDestroyingTabs = 0; |
|
mIsAlive = true; |
|
mMetamorphosed = false; |
|
mSendPermissionUpdates = false; |
|
mCalledClose = false; |
|
mCalledKillHard = false; |
|
mCreatedPairedMinidumps = false; |
|
mShutdownPending = false; |
|
mIPCOpen = true; |
|
mHangMonitorActor = nullptr; |
|
} |
|
|
|
bool |
|
ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */) |
|
{ |
|
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); |
|
|
|
std::vector<std::string> extraArgs; |
|
|
|
if (gSafeMode) { |
|
extraArgs.push_back("-safeMode"); |
|
} |
|
|
|
if (!mSubprocess->LaunchAndWaitForProcessHandle(extraArgs)) { |
|
MarkAsDead(); |
|
return false; |
|
} |
|
|
|
Open(mSubprocess->GetChannel(), |
|
base::GetProcId(mSubprocess->GetChildProcessHandle())); |
|
|
|
InitInternal(aInitialPriority, |
|
true, /* Setup off-main thread compositing */ |
|
true /* Send registered chrome */); |
|
|
|
ContentProcessManager::GetSingleton()->AddContentProcess(this); |
|
|
|
ProcessHangMonitor::AddProcess(this); |
|
|
|
// Set a reply timeout for CPOWs. |
|
SetReplyTimeoutMs(Preferences::GetInt("dom.ipc.cpow.timeout", 0)); |
|
|
|
return true; |
|
} |
|
|
|
ContentParent::ContentParent(mozIApplication* aApp, |
|
ContentParent* aOpener, |
|
bool aIsForBrowser, |
|
bool aIsForPreallocated) |
|
: nsIContentParent() |
|
, mOpener(aOpener) |
|
, mIsForBrowser(aIsForBrowser) |
|
, mLargeAllocationProcess(false) |
|
{ |
|
InitializeMembers(); // Perform common initialization. |
|
|
|
// No more than one of !!aApp, aIsForBrowser, aIsForPreallocated should be |
|
// true. |
|
MOZ_ASSERT(!!aApp + aIsForBrowser + aIsForPreallocated <= 1); |
|
|
|
mMetamorphosed = true; |
|
|
|
// Insert ourselves into the global linked list of ContentParent objects. |
|
if (!sContentParents) { |
|
sContentParents = new LinkedList<ContentParent>(); |
|
} |
|
sContentParents->insertBack(this); |
|
|
|
if (aApp) { |
|
aApp->GetManifestURL(mAppManifestURL); |
|
aApp->GetName(mAppName); |
|
} else if (aIsForPreallocated) { |
|
mAppManifestURL = MAGIC_PREALLOCATED_APP_MANIFEST_URL; |
|
} |
|
|
|
// From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the |
|
// PID along with the warning. |
|
nsDebugImpl::SetMultiprocessMode("Parent"); |
|
|
|
#if defined(XP_WIN) && !defined(MOZ_B2G) |
|
// Request Windows message deferral behavior on our side of the PContent |
|
// channel. Generally only applies to the situation where we get caught in |
|
// a deadlock with the plugin process when sending CPOWs. |
|
GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION); |
|
#endif |
|
|
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
ChildPrivileges privs = base::PRIVILEGES_DEFAULT; |
|
mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, privs); |
|
} |
|
|
|
ContentParent::~ContentParent() |
|
{ |
|
if (mForceKillTimer) { |
|
mForceKillTimer->Cancel(); |
|
} |
|
|
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
|
|
// We should be removed from all these lists in ActorDestroy. |
|
MOZ_ASSERT(!sPrivateContent || !sPrivateContent->Contains(this)); |
|
if (mAppManifestURL.IsEmpty()) { |
|
MOZ_ASSERT((!sNonAppContentParents || |
|
!sNonAppContentParents->Contains(this)) && |
|
(!sLargeAllocationContentParents || |
|
!sLargeAllocationContentParents->Contains(this))); |
|
} else { |
|
// In general, we expect sAppContentParents->Get(mAppManifestURL) to be |
|
// nullptr. But it could be that we created another ContentParent for |
|
// this app after we did this->ActorDestroy(), so the right check is |
|
// that sAppContentParents->Get(mAppManifestURL) != this. |
|
MOZ_ASSERT(!sAppContentParents || |
|
sAppContentParents->Get(mAppManifestURL) != this); |
|
} |
|
} |
|
|
|
void |
|
ContentParent::InitInternal(ProcessPriority aInitialPriority, |
|
bool aSetupOffMainThreadCompositing, |
|
bool aSendRegisteredChrome) |
|
{ |
|
if (aSendRegisteredChrome) { |
|
nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService(); |
|
nsChromeRegistryChrome* chromeRegistry = |
|
static_cast<nsChromeRegistryChrome*>(registrySvc.get()); |
|
chromeRegistry->SendRegisteredChrome(this); |
|
} |
|
|
|
if (gAppData) { |
|
nsCString version(gAppData->version); |
|
nsCString buildID(gAppData->buildID); |
|
nsCString name(gAppData->name); |
|
nsCString UAName(gAppData->UAName); |
|
nsCString ID(gAppData->ID); |
|
nsCString vendor(gAppData->vendor); |
|
|
|
// Sending all information to content process. |
|
Unused << SendAppInfo(version, buildID, name, UAName, ID, vendor); |
|
} |
|
|
|
// Initialize the message manager (and load delayed scripts) now that we |
|
// have established communications with the child. |
|
mMessageManager->InitWithCallback(this); |
|
|
|
// Set the subprocess's priority. We do this early on because we're likely |
|
// /lowering/ the process's CPU and memory priority, which it has inherited |
|
// from this process. |
|
// |
|
// This call can cause us to send IPC messages to the child process, so it |
|
// must come after the Open() call above. |
|
ProcessPriorityManager::SetProcessPriority(this, aInitialPriority); |
|
|
|
if (aSetupOffMainThreadCompositing) { |
|
// NB: internally, this will send an IPC message to the child |
|
// process to get it to create the CompositorBridgeChild. This |
|
// message goes through the regular IPC queue for this |
|
// channel, so delivery will happen-before any other messages |
|
// we send. The CompositorBridgeChild must be created before any |
|
// PBrowsers are created, because they rely on the Compositor |
|
// already being around. (Creation is async, so can't happen |
|
// on demand.) |
|
bool useOffMainThreadCompositing = !!CompositorThreadHolder::Loop(); |
|
if (useOffMainThreadCompositing) { |
|
GPUProcessManager* gpm = GPUProcessManager::Get(); |
|
|
|
Endpoint<PCompositorBridgeChild> compositor; |
|
Endpoint<PImageBridgeChild> imageBridge; |
|
Endpoint<PVRManagerChild> vrBridge; |
|
Endpoint<PVideoDecoderManagerChild> videoManager; |
|
|
|
DebugOnly<bool> opened = gpm->CreateContentBridges( |
|
OtherPid(), |
|
&compositor, |
|
&imageBridge, |
|
&vrBridge, |
|
&videoManager); |
|
MOZ_ASSERT(opened); |
|
|
|
Unused << SendInitRendering( |
|
Move(compositor), |
|
Move(imageBridge), |
|
Move(vrBridge), |
|
Move(videoManager)); |
|
|
|
gpm->AddListener(this); |
|
} |
|
} |
|
|
|
if (gAppData) { |
|
// Sending all information to content process. |
|
Unused << SendAppInit(); |
|
} |
|
|
|
nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance(); |
|
if (sheetService) { |
|
// This looks like a lot of work, but in a normal browser session we just |
|
// send two loads. |
|
|
|
for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) { |
|
URIParams uri; |
|
SerializeURI(sheet->GetSheetURI(), uri); |
|
Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AGENT_SHEET); |
|
} |
|
|
|
for (StyleSheet* sheet : *sheetService->UserStyleSheets()) { |
|
URIParams uri; |
|
SerializeURI(sheet->GetSheetURI(), uri); |
|
Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::USER_SHEET); |
|
} |
|
|
|
for (StyleSheet* sheet : *sheetService->AuthorStyleSheets()) { |
|
URIParams uri; |
|
SerializeURI(sheet->GetSheetURI(), uri); |
|
Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AUTHOR_SHEET); |
|
} |
|
} |
|
|
|
#if defined(XP_WIN) |
|
// Send the info needed to join the browser process's audio session. |
|
nsID id; |
|
nsString sessionName; |
|
nsString iconPath; |
|
if (NS_SUCCEEDED(mozilla::widget::GetAudioSessionData(id, sessionName, |
|
iconPath))) { |
|
Unused << SendSetAudioSessionData(id, sessionName, iconPath); |
|
} |
|
#endif |
|
|
|
{ |
|
RefPtr<ServiceWorkerRegistrar> swr = ServiceWorkerRegistrar::Get(); |
|
MOZ_ASSERT(swr); |
|
|
|
nsTArray<ServiceWorkerRegistrationData> registrations; |
|
swr->GetRegistrations(registrations); |
|
Unused << SendInitServiceWorkers(ServiceWorkerConfiguration(registrations)); |
|
} |
|
|
|
{ |
|
nsTArray<BlobURLRegistrationData> registrations; |
|
if (nsHostObjectProtocolHandler::GetAllBlobURLEntries(registrations, |
|
this)) { |
|
Unused << SendInitBlobURLs(registrations); |
|
} |
|
} |
|
} |
|
|
|
bool |
|
ContentParent::IsAlive() const |
|
{ |
|
return mIsAlive; |
|
} |
|
|
|
bool |
|
ContentParent::IsForApp() const |
|
{ |
|
return !mAppManifestURL.IsEmpty(); |
|
} |
|
|
|
int32_t |
|
ContentParent::Pid() const |
|
{ |
|
if (!mSubprocess || !mSubprocess->GetChildProcessHandle()) { |
|
return -1; |
|
} |
|
return base::GetProcId(mSubprocess->GetChildProcessHandle()); |
|
} |
|
|
|
bool |
|
ContentParent::RecvReadPrefsArray(InfallibleTArray<PrefSetting>* aPrefs) |
|
{ |
|
Preferences::GetPreferences(aPrefs); |
|
return true; |
|
} |
|
|
|
bool |
|
ContentParent::RecvGetGfxVars(InfallibleTArray<GfxVarUpdate>* aVars) |
|
{ |
|
// Ensure gfxVars is initialized (for xpcshell tests). |
|
gfxVars::Initialize(); |
|
|
|
*aVars = gfxVars::FetchNonDefaultVars(); |
|
|
|
// Now that content has initialized gfxVars, we can start listening for |
|
// updates. |
|
gfxVars::AddReceiver(this); |
|
return true; |
|
} |
|
|
|
void |
|
ContentParent::OnCompositorUnexpectedShutdown() |
|
{ |
|
GPUProcessManager* gpm = GPUProcessManager::Get(); |
|
|
|
Endpoint<PCompositorBridgeChild> compositor; |
|
Endpoint<PImageBridgeChild> imageBridge; |
|
Endpoint<PVRManagerChild> vrBridge; |
|
Endpoint<PVideoDecoderManagerChild> videoManager; |
|
|
|
DebugOnly<bool> opened = gpm->CreateContentBridges( |
|
OtherPid(), |
|
&compositor, |
|
&imageBridge, |
|
&vrBridge, |
|
&videoManager); |
|
MOZ_ASSERT(opened); |
|
|
|
Unused << SendReinitRendering( |
|
Move(compositor), |
|
Move(imageBridge), |
|
Move(vrBridge), |
|
Move(videoManager)); |
|
} |
|
|
|
void |
|
ContentParent::OnVarChanged(const GfxVarUpdate& aVar) |
|
{ |
|
if (!mIPCOpen) { |
|
return; |
|
} |
|
Unused << SendVarUpdate(aVar); |
|
} |
|
|
|
bool |
|
ContentParent::RecvReadFontList(InfallibleTArray<FontListEntry>* retValue) |
|
{ |
|
#ifdef ANDROID |
|
gfxAndroidPlatform::GetPlatform()->GetSystemFontList(retValue); |
|
#endif |
|
return true; |
|
} |
|
|
|
bool |
|
ContentParent::RecvReadDataStorageArray(const nsString& aFilename, |
|
InfallibleTArray<DataStorageItem>* aValues) |
|
{ |
|
// If we're shutting down, the DataStorage object may have been cleared |
|
// already, and setting it up is pointless anyways since we're about to die. |
|
if (mShutdownPending) { |
|
return true; |
|
} |
|
|
|
// Ensure the SSS is initialized before we try to use its storage. |
|
nsCOMPtr<nsISiteSecurityService> sss = do_GetService("@mozilla.org/ssservice;1"); |
|
|
|
RefPtr<DataStorage> storage = DataStorage::Get(aFilename); |
|
storage->GetAll(aValues); |
|
return true; |
|
} |
|
|
|
bool |
|
ContentParent::RecvReadPermissions(InfallibleTArray<IPC::Permission>* aPermissions) |
|
{ |
|
#ifdef MOZ_PERMISSIONS |
|
nsCOMPtr<nsIPermissionManager> permissionManagerIface = |
|
services::GetPermissionManager(); |
|
nsPermissionManager* permissionManager = |
|
static_cast<nsPermissionManager*>(permissionManagerIface.get()); |
|
MOZ_ASSERT(permissionManager, |
|
"We have no permissionManager in the Chrome process !"); |
|
|
|
nsCOMPtr<nsISimpleEnumerator> enumerator; |
|
DebugOnly<nsresult> rv = permissionManager->GetEnumerator(getter_AddRefs(enumerator)); |
|
MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not get enumerator!"); |
|
while(1) { |
|
bool hasMore; |
|
enumerator->HasMoreElements(&hasMore); |
|
if (!hasMore) |
|
break; |
|
|
|
nsCOMPtr<nsISupports> supp; |
|
enumerator->GetNext(getter_AddRefs(supp)); |
|
nsCOMPtr<nsIPermission> perm = do_QueryInterface(supp); |
|
|
|
nsCOMPtr<nsIPrincipal> principal; |
|
perm->GetPrincipal(getter_AddRefs(principal)); |
|
nsCString origin; |
|
if (principal) { |
|
principal->GetOrigin(origin); |
|
} |
|
nsCString type; |
|
perm->GetType(type); |
|
uint32_t capability; |
|
perm->GetCapability(&capability); |
|
uint32_t expireType; |
|
perm->GetExpireType(&expireType); |
|
int64_t expireTime; |
|
perm->GetExpireTime(&expireTime); |
|
|
|
aPermissions->AppendElement(IPC::Permission(origin, type, |
|
capability, expireType, |
|
expireTime)); |
|
} |
|
|
|
// Ask for future changes |
|
mSendPermissionUpdates = true; |
|
#endif |
|
|
|
return true; |
|
} |
|
|
|
bool |
|
ContentParent::RecvSetClipboard(const IPCDataTransfer& aDataTransfer, |
|
const bool& aIsPrivateData, |
|
const IPC::Principal& aRequestingPrincipal, |
|
const int32_t& aWhichClipboard) |
|
{ |
|
nsresult rv; |
|
nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv)); |
|
NS_ENSURE_SUCCESS(rv, true); |
|
|
|
nsCOMPtr<nsITransferable> trans = |
|
do_CreateInstance("@mozilla.org/widget/transferable;1", &rv); |
|
NS_ENSURE_SUCCESS(rv, true); |
|
trans->Init(nullptr); |
|
|
|
rv = nsContentUtils::IPCTransferableToTransferable(aDataTransfer, |
|
aIsPrivateData, |
|
aRequestingPrincipal, |
|
trans, this, nullptr); |
|
NS_ENSURE_SUCCESS(rv, true); |
|
|
|
clipboard->SetData(trans, nullptr, aWhichClipboard); |
|
return true; |
|
} |
|
|
|
bool |
|
ContentParent::RecvGetClipboard(nsTArray<nsCString>&& aTypes, |
|
const int32_t& aWhichClipboard, |
|
IPCDataTransfer* aDataTransfer) |
|
{ |
|
nsresult rv; |
|
nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv)); |
|
NS_ENSURE_SUCCESS(rv, true); |
|
|
|
nsCOMPtr<nsITransferable> trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv); |
|
NS_ENSURE_SUCCESS(rv, true); |
|
trans->Init(nullptr); |
|
|
|
for (uint32_t t = 0; t < aTypes.Length(); t++) { |
|
trans->AddDataFlavor(aTypes[t].get()); |
|
} |
|
|
|
clipboard->GetData(trans, aWhichClipboard); |
|
nsContentUtils::TransferableToIPCTransferable(trans, aDataTransfer, |
|
true, nullptr, this); |
|
return true; |
|
} |
|
|
|
bool |
|
ContentParent::RecvEmptyClipboard(const int32_t& aWhichClipboard) |
|
{ |
|
nsresult rv; |
|
nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv)); |
|
NS_ENSURE_SUCCESS(rv, true); |
|
|
|
clipboard->EmptyClipboard(aWhichClipboard); |
|
|
|
return true; |
|
} |
|
|
|
bool |
|
ContentParent::RecvClipboardHasType(nsTArray<nsCString>&& aTypes, |
|
const int32_t& aWhichClipboard, |
|
bool* aHasType) |
|
{ |
|
nsresult rv; |
|
nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv)); |
|
NS_ENSURE_SUCCESS(rv, true); |
|
|
|
const char** typesChrs = new const char *[aTypes.Length()]; |
|
for (uint32_t t = 0; t < aTypes.Length(); t++) { |
|
typesChrs[t] = aTypes[t].get(); |
|
} |
|
|
|
clipboard->HasDataMatchingFlavors(typesChrs, aTypes.Length(), |
|
aWhichClipboard, aHasType); |
|
|
|
delete [] typesChrs; |
|
return true; |
|
} |
|
|
|
bool |
|
ContentParent::RecvGetSystemColors(const uint32_t& colorsCount, |
|
InfallibleTArray<uint32_t>* colors) |
|
{ |
|
#ifdef MOZ_WIDGET_ANDROID |
|
NS_ASSERTION(AndroidBridge::Bridge() != nullptr, "AndroidBridge is not available"); |
|
if (AndroidBridge::Bridge() == nullptr) { |
|
// Do not fail - the colors won't be right, but it's not critical |
|
return true; |
|
} |
|
|
|
colors->AppendElements(colorsCount); |
|
|
|
// The array elements correspond to the members of AndroidSystemColors structure, |
|
// so just pass the pointer to the elements buffer |
|
AndroidBridge::Bridge()->GetSystemColors((AndroidSystemColors*)colors->Elements()); |
|
#endif |
|
return true; |
|
} |
|
|
|
bool |
|
ContentParent::RecvGetIconForExtension(const nsCString& aFileExt, |
|
const uint32_t& aIconSize, |
|
InfallibleTArray<uint8_t>* bits) |
|
{ |
|
#ifdef MOZ_WIDGET_ANDROID |
|
NS_ASSERTION(AndroidBridge::Bridge() != nullptr, "AndroidBridge is not available"); |
|
if (AndroidBridge::Bridge() == nullptr) { |
|
// Do not fail - just no icon will be shown |
|
return true; |
|
} |
|
|
|
bits->AppendElements( |