diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/ipc | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/ipc')
103 files changed, 37730 insertions, 0 deletions
diff --git a/dom/ipc/AppProcessChecker.cpp b/dom/ipc/AppProcessChecker.cpp new file mode 100644 index 000000000..fa91c94ff --- /dev/null +++ b/dom/ipc/AppProcessChecker.cpp @@ -0,0 +1,95 @@ +/* -*- 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 "AppProcessChecker.h" +#include "nsIPermissionManager.h" + +namespace mozilla { +namespace dom { +class PContentParent; +} // namespace dom +} // namespace mozilla + +class nsIPrincipal; + +namespace mozilla { + +#if DEBUG + #define LOG(...) printf_stderr(__VA_ARGS__) +#else + #define LOG(...) +#endif + +bool +AssertAppProcess(mozilla::dom::PBrowserParent* aActor, + AssertAppProcessType aType, + const char* aCapability) +{ + return true; +} + +bool +AssertAppStatus(mozilla::dom::PBrowserParent* aActor, + unsigned short aStatus) +{ + return true; +} + +bool +AssertAppProcess(const mozilla::dom::TabContext& aContext, + AssertAppProcessType aType, + const char* aCapability) +{ + return true; +} + +bool +AssertAppStatus(const mozilla::dom::TabContext& aContext, + unsigned short aStatus) +{ + return true; +} + + +bool +AssertAppProcess(mozilla::dom::PContentParent* aActor, + AssertAppProcessType aType, + const char* aCapability) +{ + return true; +} + +bool +AssertAppStatus(mozilla::dom::PContentParent* aActor, + unsigned short aStatus) +{ + return true; +} + +bool +AssertAppProcess(mozilla::hal_sandbox::PHalParent* aActor, + AssertAppProcessType aType, + const char* aCapability) +{ + return true; +} + +bool +AssertAppPrincipal(mozilla::dom::PContentParent* aActor, + nsIPrincipal* aPrincipal) +{ + return true; +} + +uint32_t +CheckPermission(mozilla::dom::PContentParent* aActor, + nsIPrincipal* aPrincipal, + const char* aPermission) +{ + return nsIPermissionManager::ALLOW_ACTION; +} + +} // namespace mozilla diff --git a/dom/ipc/AppProcessChecker.h b/dom/ipc/AppProcessChecker.h new file mode 100644 index 000000000..e5e7a90ba --- /dev/null +++ b/dom/ipc/AppProcessChecker.h @@ -0,0 +1,160 @@ +/* -*- 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/. */ + +#ifndef mozilla_AppProcessChecker_h +#define mozilla_AppProcessChecker_h + +#include <stdint.h> + +class nsIPrincipal; + +namespace mozilla { + +namespace dom { +class TabContext; +class PBrowserParent; +class PContentParent; +} // namespace dom + +namespace hal_sandbox { +class PHalParent; +} // namespace hal_sandbox + +enum AssertAppProcessType { + ASSERT_APP_PROCESS_PERMISSION, + ASSERT_APP_PROCESS_MANIFEST_URL, + ASSERT_APP_HAS_PERMISSION +}; + +/** + * Return true if the specified browser has the specified capability. + * If this returns false, the browser didn't have the capability and + * will be killed. + */ +bool +AssertAppProcess(mozilla::dom::PBrowserParent* aActor, + AssertAppProcessType aType, + const char* aCapability); + +/** + * Return true if the specified app has the specified status. + * If this returns false, the browser will be killed. + */ +bool +AssertAppStatus(mozilla::dom::PBrowserParent* aActor, + unsigned short aStatus); + +/** + * Return true if the specified browser has the specified capability. + * If this returns false, the browser didn't have the capability and + * will be killed. + */ +bool +AssertAppProcess(const mozilla::dom::TabContext& aContext, + AssertAppProcessType aType, + const char* aCapability); + +/** + * Return true if the specified app has the specified status. + * If this returns false, the browser will be killed. + */ +bool +AssertAppStatus(const mozilla::dom::TabContext& aContext, + unsigned short aStatus); + +/** + * Return true if any of the PBrowsers loaded in this content process + * has the specified capability. If this returns false, the process + * didn't have the capability and will be killed. + */ +bool +AssertAppProcess(mozilla::dom::PContentParent* aActor, + AssertAppProcessType aType, + const char* aCapability); + +/** + * Return true if any of the PBrowsers loaded in this content process + * has an app with the specified status. If this returns false, the process + * didn't have the status and will be killed. + */ +bool +AssertAppStatus(mozilla::dom::PContentParent* aActor, + unsigned short aStatus); + +bool +AssertAppProcess(mozilla::hal_sandbox::PHalParent* aActor, + AssertAppProcessType aType, + const char* aCapability); + +// NB: when adding capability checks for other IPDL actors, please add +// them to this file and have them delegate to the two functions above +// as appropriate. For example, +// +// bool AppProcessHasCapability(PNeckoParent* aActor, AssertAppProcessType aType) { +// return AssertAppProcess(aActor->Manager(), aType); +// } + +bool +AssertAppPrincipal(mozilla::dom::PContentParent* aParent, + nsIPrincipal* aPrincipal); + +/** + * Check if the specified principal is valid, and return the saved permission + * value for permission `aPermission' on that principal. + * See nsIPermissionManager.idl for possible return values. + * + * nsIPermissionManager::UNKNOWN_ACTION is retuned if the principal is invalid. + */ +uint32_t +CheckPermission(mozilla::dom::PContentParent* aParent, + nsIPrincipal* aPrincipal, const char* aPermission); + +/** + * Inline function for asserting the process's permission. + */ +template<typename T> +inline bool +AssertAppProcessPermission(T* aActor, + const char* aPermission) { + return AssertAppProcess(aActor, + ASSERT_APP_PROCESS_PERMISSION, + aPermission); +} + +/** + * Inline function for asserting the process's manifest URL. + */ +template<typename T> +inline bool +AssertAppProcessManifestURL(T* aActor, + const char* aManifestURL) { + return AssertAppProcess(aActor, + ASSERT_APP_PROCESS_MANIFEST_URL, + aManifestURL); +} + +/** + * Inline function for asserting the process's manifest URL. + */ +template<typename T> +inline bool +AssertAppHasPermission(T* aActor, + const char* aPermission) { + return AssertAppProcess(aActor, + ASSERT_APP_HAS_PERMISSION, + aPermission); +} + +template<typename T> +inline bool +AssertAppHasStatus(T* aActor, + unsigned short aStatus) { + return AssertAppStatus(aActor, aStatus); +} + +} // namespace mozilla + +#endif // mozilla_AppProcessChecker_h diff --git a/dom/ipc/Blob.cpp b/dom/ipc/Blob.cpp new file mode 100644 index 000000000..95d2e0513 --- /dev/null +++ b/dom/ipc/Blob.cpp @@ -0,0 +1,4793 @@ +/* -*- 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 "BlobChild.h" +#include "BlobParent.h" + +#include "BackgroundParent.h" +#include "ContentChild.h" +#include "ContentParent.h" +#include "FileDescriptorSetChild.h" +#include "jsapi.h" +#include "mozilla/Assertions.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/Monitor.h" +#include "mozilla/Mutex.h" +#include "mozilla/Unused.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/nsIContentParent.h" +#include "mozilla/dom/nsIContentChild.h" +#include "mozilla/dom/PBlobStreamChild.h" +#include "mozilla/dom/PBlobStreamParent.h" +#include "mozilla/dom/indexedDB/FileSnapshot.h" +#include "mozilla/dom/IndexedDatabaseManager.h" +#include "mozilla/ipc/InputStreamUtils.h" +#include "mozilla/ipc/IPCStreamUtils.h" +#include "mozilla/ipc/PBackgroundChild.h" +#include "mozilla/ipc/PBackgroundParent.h" +#include "mozilla/ipc/PFileDescriptorSetParent.h" +#include "MultipartBlobImpl.h" +#include "nsDataHashtable.h" +#include "nsHashKeys.h" +#include "nsID.h" +#include "nsIFileStreams.h" +#include "nsIInputStream.h" +#include "nsIIPCSerializableInputStream.h" +#include "nsIMultiplexInputStream.h" +#include "nsIRemoteBlob.h" +#include "nsISeekableStream.h" +#include "nsIUUIDGenerator.h" +#include "nsNetCID.h" +#include "nsServiceManagerUtils.h" +#include "nsStringStream.h" +#include "nsThreadUtils.h" +#include "nsXULAppAPI.h" +#include "WorkerPrivate.h" +#include "WorkerRunnable.h" + +#ifdef DEBUG +#include "BackgroundChild.h" // BackgroundChild::GetForCurrentThread(). +#endif + +#ifdef OS_POSIX +#include "chrome/common/file_descriptor_set_posix.h" +#endif + +#define DISABLE_ASSERTS_FOR_FUZZING 0 + +#if DISABLE_ASSERTS_FOR_FUZZING +#define ASSERT_UNLESS_FUZZING(...) do { } while (0) +#else +#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__) +#endif + +#define PRIVATE_REMOTE_INPUT_STREAM_IID \ + {0x30c7699f, 0x51d2, 0x48c8, {0xad, 0x56, 0xc0, 0x16, 0xd7, 0x6f, 0x71, 0x27}} + +namespace mozilla { +namespace dom { + +using namespace mozilla::ipc; +using namespace mozilla::dom::indexedDB; +using namespace mozilla::dom::workers; + +namespace { + +const char kUUIDGeneratorContractId[] = "@mozilla.org/uuid-generator;1"; + +const uint32_t kMaxFileDescriptorsPerMessage = 250; + +#ifdef OS_POSIX +// Keep this in sync with other platforms. +static_assert(FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE == 250, + "MAX_DESCRIPTORS_PER_MESSAGE mismatch!"); +#endif + +StaticRefPtr<nsIUUIDGenerator> gUUIDGenerator; + +GeckoProcessType gProcessType = GeckoProcessType_Invalid; + +void +CommonStartup() +{ + MOZ_ASSERT(NS_IsMainThread()); + + gProcessType = XRE_GetProcessType(); + MOZ_ASSERT(gProcessType != GeckoProcessType_Invalid); + + nsCOMPtr<nsIUUIDGenerator> uuidGen = do_GetService(kUUIDGeneratorContractId); + MOZ_RELEASE_ASSERT(uuidGen); + + gUUIDGenerator = uuidGen; + ClearOnShutdown(&gUUIDGenerator); +} + +template <class ManagerType> +struct ConcreteManagerTypeTraits; + +template <> +struct ConcreteManagerTypeTraits<nsIContentChild> +{ + typedef ContentChild Type; +}; + +template <> +struct ConcreteManagerTypeTraits<PBackgroundChild> +{ + typedef PBackgroundChild Type; +}; + +template <> +struct ConcreteManagerTypeTraits<nsIContentParent> +{ + typedef ContentParent Type; +}; + +template <> +struct ConcreteManagerTypeTraits<PBackgroundParent> +{ + typedef PBackgroundParent Type; +}; + +void +AssertCorrectThreadForManager(nsIContentChild* aManager) +{ + MOZ_ASSERT(NS_IsMainThread()); +} + +void +AssertCorrectThreadForManager(nsIContentParent* aManager) +{ + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + MOZ_ASSERT(NS_IsMainThread()); +} + +void +AssertCorrectThreadForManager(PBackgroundChild* aManager) +{ +#ifdef DEBUG + if (aManager) { + PBackgroundChild* backgroundChild = BackgroundChild::GetForCurrentThread(); + MOZ_ASSERT(backgroundChild); + MOZ_ASSERT(backgroundChild == aManager); + } +#endif +} + +void +AssertCorrectThreadForManager(PBackgroundParent* aManager) +{ + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + AssertIsOnBackgroundThread(); +} + +intptr_t +ActorManagerProcessID(nsIContentParent* aManager) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + return reinterpret_cast<intptr_t>(aManager); +} + +intptr_t +ActorManagerProcessID(PBackgroundParent* aManager) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + return BackgroundParent::GetRawContentParentForComparison(aManager); +} + +bool +ActorManagerIsSameProcess(nsIContentParent* aManager) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + return false; +} + +bool +ActorManagerIsSameProcess(PBackgroundParent* aManager) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + return !BackgroundParent::IsOtherProcessActor(aManager); +} + +bool +EventTargetIsOnCurrentThread(nsIEventTarget* aEventTarget) +{ + if (!aEventTarget) { + return NS_IsMainThread(); + } + + bool current; + + // If this fails, we are probably shutting down. + if (NS_WARN_IF(NS_FAILED(aEventTarget->IsOnCurrentThread(¤t)))) { + return true; + } + + return current; +} + +class CancelableRunnableWrapper final + : public CancelableRunnable +{ + nsCOMPtr<nsIRunnable> mRunnable; +#ifdef DEBUG + nsCOMPtr<nsIEventTarget> mDEBUGEventTarget; +#endif + +public: + CancelableRunnableWrapper(nsIRunnable* aRunnable, + nsIEventTarget* aEventTarget) + : mRunnable(aRunnable) +#ifdef DEBUG + , mDEBUGEventTarget(aEventTarget) +#endif + { + MOZ_ASSERT(aRunnable); + MOZ_ASSERT(aEventTarget); + } + + NS_DECL_ISUPPORTS_INHERITED + +private: + ~CancelableRunnableWrapper() + { } + + NS_DECL_NSIRUNNABLE + nsresult Cancel() override; +}; + +NS_IMPL_ISUPPORTS_INHERITED0(CancelableRunnableWrapper, CancelableRunnable) + +NS_IMETHODIMP +CancelableRunnableWrapper::Run() +{ + DebugOnly<bool> onTarget; + MOZ_ASSERT(mDEBUGEventTarget); + MOZ_ASSERT(NS_SUCCEEDED(mDEBUGEventTarget->IsOnCurrentThread(&onTarget))); + MOZ_ASSERT(onTarget); + + nsCOMPtr<nsIRunnable> runnable; + mRunnable.swap(runnable); + + if (runnable) { + return runnable->Run(); + } + + return NS_OK; +} + +nsresult +CancelableRunnableWrapper::Cancel() +{ + DebugOnly<bool> onTarget; + MOZ_ASSERT(mDEBUGEventTarget); + MOZ_ASSERT(NS_SUCCEEDED(mDEBUGEventTarget->IsOnCurrentThread(&onTarget))); + MOZ_ASSERT(onTarget); + + if (NS_WARN_IF(!mRunnable)) { + return NS_ERROR_UNEXPECTED; + } + + Unused << Run(); + MOZ_ASSERT(!mRunnable); + + return NS_OK; +} + +// Ensure that a nsCOMPtr/nsRefPtr is released on the target thread. +template <template <class> class SmartPtr, class T> +void +ReleaseOnTarget(SmartPtr<T>& aDoomed, nsIEventTarget* aTarget) +{ + MOZ_ASSERT(aDoomed); + MOZ_ASSERT(!EventTargetIsOnCurrentThread(aTarget)); + + T* doomedRaw; + aDoomed.forget(&doomedRaw); + + auto* doomedSupports = static_cast<nsISupports*>(doomedRaw); + + nsCOMPtr<nsIRunnable> releaseRunnable = + NewNonOwningRunnableMethod(doomedSupports, &nsISupports::Release); + MOZ_ASSERT(releaseRunnable); + + if (aTarget) { + // If we're targeting a non-main thread then make sure the runnable is + // cancelable. + releaseRunnable = new CancelableRunnableWrapper(releaseRunnable, aTarget); + + MOZ_ALWAYS_SUCCEEDS(aTarget->Dispatch(releaseRunnable, + NS_DISPATCH_NORMAL)); + } else { + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(releaseRunnable)); + } +} + +template <class ManagerType> +void +ConstructFileDescriptorSet(ManagerType* aManager, + nsTArray<FileDescriptor>& aFDs, + OptionalFileDescriptorSet& aOptionalFDSet) +{ + typedef typename ConcreteManagerTypeTraits<ManagerType>::Type + ConcreteManagerType; + + MOZ_ASSERT(aManager); + + if (aFDs.IsEmpty()) { + aOptionalFDSet = void_t(); + return; + } + + if (aFDs.Length() <= kMaxFileDescriptorsPerMessage) { + aOptionalFDSet = nsTArray<FileDescriptor>(); + aOptionalFDSet.get_ArrayOfFileDescriptor().SwapElements(aFDs); + return; + } + + auto* concreteManager = static_cast<ConcreteManagerType*>(aManager); + + PFileDescriptorSetParent* fdSet = + concreteManager->SendPFileDescriptorSetConstructor(aFDs[0]); + if (!fdSet) { + aOptionalFDSet = void_t(); + return; + } + + for (uint32_t index = 1; index < aFDs.Length(); index++) { + if (!fdSet->SendAddFileDescriptor(aFDs[index])) { + aOptionalFDSet = void_t(); + return; + } + } + + aOptionalFDSet = fdSet; +} + +void +OptionalFileDescriptorSetToFDs(OptionalFileDescriptorSet& aOptionalSet, + nsTArray<FileDescriptor>& aFDs) +{ + MOZ_ASSERT(aFDs.IsEmpty()); + + switch (aOptionalSet.type()) { + case OptionalFileDescriptorSet::Tvoid_t: + return; + + case OptionalFileDescriptorSet::TArrayOfFileDescriptor: + aOptionalSet.get_ArrayOfFileDescriptor().SwapElements(aFDs); + return; + + case OptionalFileDescriptorSet::TPFileDescriptorSetChild: { + FileDescriptorSetChild* fdSetActor = + static_cast<FileDescriptorSetChild*>( + aOptionalSet.get_PFileDescriptorSetChild()); + MOZ_ASSERT(fdSetActor); + + fdSetActor->ForgetFileDescriptors(aFDs); + MOZ_ASSERT(!aFDs.IsEmpty()); + + PFileDescriptorSetChild::Send__delete__(fdSetActor); + return; + } + + default: + MOZ_CRASH("Unknown type!"); + } + + MOZ_CRASH("Should never get here!"); +} + +class NS_NO_VTABLE IPrivateRemoteInputStream + : public nsISupports +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(PRIVATE_REMOTE_INPUT_STREAM_IID) + + // This will return the underlying stream. + virtual nsIInputStream* + BlockAndGetInternalStream() = 0; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(IPrivateRemoteInputStream, + PRIVATE_REMOTE_INPUT_STREAM_IID) + +// This class exists to keep a blob alive at least as long as its internal +// stream. +class BlobInputStreamTether final + : public nsIMultiplexInputStream + , public nsISeekableStream + , public nsIIPCSerializableInputStream + , public nsIFileMetadata +{ + nsCOMPtr<nsIInputStream> mStream; + RefPtr<BlobImpl> mBlobImpl; + + nsIMultiplexInputStream* mWeakMultiplexStream; + nsISeekableStream* mWeakSeekableStream; + nsIIPCSerializableInputStream* mWeakSerializableStream; + nsIFileMetadata* mWeakFileMetadata; + +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_FORWARD_NSIINPUTSTREAM(mStream->) + NS_FORWARD_SAFE_NSIMULTIPLEXINPUTSTREAM(mWeakMultiplexStream) + NS_FORWARD_SAFE_NSISEEKABLESTREAM(mWeakSeekableStream) + NS_FORWARD_SAFE_NSIIPCSERIALIZABLEINPUTSTREAM(mWeakSerializableStream) + NS_FORWARD_SAFE_NSIFILEMETADATA(mWeakFileMetadata) + + BlobInputStreamTether(nsIInputStream* aStream, BlobImpl* aBlobImpl) + : mStream(aStream) + , mBlobImpl(aBlobImpl) + , mWeakMultiplexStream(nullptr) + , mWeakSeekableStream(nullptr) + , mWeakSerializableStream(nullptr) + , mWeakFileMetadata(nullptr) + { + MOZ_ASSERT(aStream); + MOZ_ASSERT(aBlobImpl); + + nsCOMPtr<nsIMultiplexInputStream> multiplexStream = + do_QueryInterface(aStream); + if (multiplexStream) { + MOZ_ASSERT(SameCOMIdentity(aStream, multiplexStream)); + mWeakMultiplexStream = multiplexStream; + } + + nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aStream); + if (seekableStream) { + MOZ_ASSERT(SameCOMIdentity(aStream, seekableStream)); + mWeakSeekableStream = seekableStream; + } + + nsCOMPtr<nsIIPCSerializableInputStream> serializableStream = + do_QueryInterface(aStream); + if (serializableStream) { + MOZ_ASSERT(SameCOMIdentity(aStream, serializableStream)); + mWeakSerializableStream = serializableStream; + } + + nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(aStream); + if (fileMetadata) { + MOZ_ASSERT(SameCOMIdentity(aStream, fileMetadata)); + mWeakFileMetadata = fileMetadata; + } + } + +private: + ~BlobInputStreamTether() + { } +}; + +NS_IMPL_ADDREF(BlobInputStreamTether) +NS_IMPL_RELEASE(BlobInputStreamTether) + +NS_INTERFACE_MAP_BEGIN(BlobInputStreamTether) + NS_INTERFACE_MAP_ENTRY(nsIInputStream) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMultiplexInputStream, + mWeakMultiplexStream) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, mWeakSeekableStream) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream, + mWeakSerializableStream) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileMetadata, + mWeakFileMetadata) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream) +NS_INTERFACE_MAP_END + +class RemoteInputStream final + : public nsIInputStream + , public nsISeekableStream + , public nsIIPCSerializableInputStream + , public nsIFileMetadata + , public IPrivateRemoteInputStream +{ + Monitor mMonitor; + BlobChild* mActor; + nsCOMPtr<nsIInputStream> mStream; + RefPtr<BlobImpl> mBlobImpl; + nsCOMPtr<nsIEventTarget> mEventTarget; + nsISeekableStream* mWeakSeekableStream; + nsIFileMetadata* mWeakFileMetadata; + uint64_t mStart; + uint64_t mLength; + +public: + RemoteInputStream(BlobImpl* aBlobImpl, + uint64_t aStart, + uint64_t aLength); + + RemoteInputStream(BlobChild* aActor, + BlobImpl* aBlobImpl, + uint64_t aStart, + uint64_t aLength); + + bool + IsOnOwningThread() const + { + return EventTargetIsOnCurrentThread(mEventTarget); + } + + void + AssertIsOnOwningThread() const + { + MOZ_ASSERT(IsOnOwningThread()); + } + + bool + IsWorkerStream() const + { + return !!mActor; + } + + void + SetStream(nsIInputStream* aStream); + + NS_DECL_THREADSAFE_ISUPPORTS + +private: + ~RemoteInputStream(); + + nsresult + BlockAndWaitForStream(); + + void + ReallyBlockAndWaitForStream(); + + bool + IsSeekableStream(); + + bool + IsFileMetadata(); + + NS_DECL_NSIINPUTSTREAM + NS_DECL_NSISEEKABLESTREAM + NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM + NS_DECL_NSIFILEMETADATA + + virtual nsIInputStream* + BlockAndGetInternalStream() override; +}; + +class InputStreamChild final + : public PBlobStreamChild +{ + RefPtr<RemoteInputStream> mRemoteStream; + +public: + explicit + InputStreamChild(RemoteInputStream* aRemoteStream) + : mRemoteStream(aRemoteStream) + { + MOZ_ASSERT(aRemoteStream); + aRemoteStream->AssertIsOnOwningThread(); + } + + InputStreamChild() + { } + + ~InputStreamChild() + { } + +private: + // This method is only called by the IPDL message machinery. + virtual bool + Recv__delete__(const InputStreamParams& aParams, + const OptionalFileDescriptorSet& aFDs) override; +}; + +class InputStreamParent final + : public PBlobStreamParent +{ + typedef mozilla::ipc::InputStreamParams InputStreamParams; + typedef mozilla::dom::OptionalFileDescriptorSet OptionalFileDescriptorSet; + + bool* mSyncLoopGuard; + InputStreamParams* mParams; + OptionalFileDescriptorSet* mFDs; + +#ifdef DEBUG + PRThread* mOwningThread; +#endif + +public: + InputStreamParent() + : mSyncLoopGuard(nullptr) + , mParams(nullptr) + , mFDs(nullptr) + { +#ifdef DEBUG + mOwningThread = PR_GetCurrentThread(); +#endif + + AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(InputStreamParent); + } + + InputStreamParent(bool* aSyncLoopGuard, + InputStreamParams* aParams, + OptionalFileDescriptorSet* aFDs) + : mSyncLoopGuard(aSyncLoopGuard) + , mParams(aParams) + , mFDs(aFDs) + { +#ifdef DEBUG + mOwningThread = PR_GetCurrentThread(); +#endif + + AssertIsOnOwningThread(); + MOZ_ASSERT(aSyncLoopGuard); + MOZ_ASSERT(!*aSyncLoopGuard); + MOZ_ASSERT(aParams); + MOZ_ASSERT(aFDs); + + MOZ_COUNT_CTOR(InputStreamParent); + } + + ~InputStreamParent() + { + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(InputStreamParent); + } + + void + AssertIsOnOwningThread() const + { +#ifdef DEBUG + MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread); +#endif + } + + bool + Destroy(const InputStreamParams& aParams, + const OptionalFileDescriptorSet& aFDs) + { + AssertIsOnOwningThread(); + + if (mSyncLoopGuard) { + MOZ_ASSERT(!*mSyncLoopGuard); + + *mSyncLoopGuard = true; + *mParams = aParams; + *mFDs = aFDs; + + // We're not a live actor so manage the memory ourselves. + delete this; + return true; + } + + // This will be destroyed by BlobParent::DeallocPBlobStreamParent. + return PBlobStreamParent::Send__delete__(this, aParams, aFDs); + } + +private: + // This method is only called by the IPDL message machinery. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override + { + // Nothing needs to be done here. + } +}; + +struct MOZ_STACK_CLASS CreateBlobImplMetadata final +{ + nsString mContentType; + nsString mName; + uint64_t mLength; + int64_t mLastModifiedDate; + bool mHasRecursed; + const bool mIsSameProcessActor; + + explicit CreateBlobImplMetadata(bool aIsSameProcessActor) + : mLength(0) + , mLastModifiedDate(0) + , mHasRecursed(false) + , mIsSameProcessActor(aIsSameProcessActor) + { + MOZ_COUNT_CTOR(CreateBlobImplMetadata); + + mName.SetIsVoid(true); + } + + ~CreateBlobImplMetadata() + { + MOZ_COUNT_DTOR(CreateBlobImplMetadata); + } + + bool + IsFile() const + { + return !mName.IsVoid(); + } +}; + +already_AddRefed<BlobImpl> +CreateBlobImpl(const nsID& aKnownBlobIDData, + const CreateBlobImplMetadata& aMetadata) +{ + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + MOZ_ASSERT(aMetadata.mHasRecursed); + + RefPtr<BlobImpl> blobImpl = BlobParent::GetBlobImplForID(aKnownBlobIDData); + if (NS_WARN_IF(!blobImpl)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + DebugOnly<bool> isMutable; + MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable))); + MOZ_ASSERT(!isMutable); + + return blobImpl.forget(); +} + +already_AddRefed<BlobImpl> +CreateBlobImpl(const BlobDataStream& aStream, + const CreateBlobImplMetadata& aMetadata) +{ + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + + nsCOMPtr<nsIInputStream> inputStream = DeserializeIPCStream(aStream.stream()); + if (!inputStream) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + uint64_t length = aStream.length(); + + RefPtr<BlobImpl> blobImpl; + if (!aMetadata.mHasRecursed && aMetadata.IsFile()) { + if (length) { + blobImpl = + BlobImplStream::Create(inputStream, + aMetadata.mName, + aMetadata.mContentType, + aMetadata.mLastModifiedDate, + length); + } else { + blobImpl = + new EmptyBlobImpl(aMetadata.mName, + aMetadata.mContentType, + aMetadata.mLastModifiedDate); + } + } else if (length) { + blobImpl = + BlobImplStream::Create(inputStream, aMetadata.mContentType, + length); + } else { + blobImpl = new EmptyBlobImpl(aMetadata.mContentType); + } + + MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false)); + + return blobImpl.forget(); +} + +already_AddRefed<BlobImpl> +CreateBlobImpl(const nsTArray<BlobData>& aBlobData, + CreateBlobImplMetadata& aMetadata); + +already_AddRefed<BlobImpl> +CreateBlobImplFromBlobData(const BlobData& aBlobData, + CreateBlobImplMetadata& aMetadata) +{ + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + + RefPtr<BlobImpl> blobImpl; + + switch (aBlobData.type()) { + case BlobData::TnsID: { + blobImpl = CreateBlobImpl(aBlobData.get_nsID(), aMetadata); + break; + } + + case BlobData::TBlobDataStream: { + blobImpl = CreateBlobImpl(aBlobData.get_BlobDataStream(), aMetadata); + break; + } + + case BlobData::TArrayOfBlobData: { + blobImpl = CreateBlobImpl(aBlobData.get_ArrayOfBlobData(), aMetadata); + break; + } + + default: + MOZ_CRASH("Unknown params!"); + } + + return blobImpl.forget(); +} + +already_AddRefed<BlobImpl> +CreateBlobImpl(const nsTArray<BlobData>& aBlobDatas, + CreateBlobImplMetadata& aMetadata) +{ + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + + // Special case for a multipart blob with only one part. + if (aBlobDatas.Length() == 1) { + const BlobData& blobData = aBlobDatas[0]; + + RefPtr<BlobImpl> blobImpl = + CreateBlobImplFromBlobData(blobData, aMetadata); + if (NS_WARN_IF(!blobImpl)) { + return nullptr; + } + + DebugOnly<bool> isMutable; + MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable))); + MOZ_ASSERT(!isMutable); + + return blobImpl.forget(); + } + + nsTArray<RefPtr<BlobImpl>> blobImpls; + if (NS_WARN_IF(!blobImpls.SetLength(aBlobDatas.Length(), fallible))) { + return nullptr; + } + + const bool hasRecursed = aMetadata.mHasRecursed; + aMetadata.mHasRecursed = true; + + for (uint32_t count = aBlobDatas.Length(), index = 0; + index < count; + index++) { + const BlobData& blobData = aBlobDatas[index]; + RefPtr<BlobImpl>& blobImpl = blobImpls[index]; + + blobImpl = CreateBlobImplFromBlobData(blobData, aMetadata); + if (NS_WARN_IF(!blobImpl)) { + return nullptr; + } + + DebugOnly<bool> isMutable; + MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable))); + MOZ_ASSERT(!isMutable); + } + + ErrorResult rv; + RefPtr<BlobImpl> blobImpl; + if (!hasRecursed && aMetadata.IsFile()) { + blobImpl = MultipartBlobImpl::Create(Move(blobImpls), aMetadata.mName, + aMetadata.mContentType, rv); + } else { + blobImpl = MultipartBlobImpl::Create(Move(blobImpls), aMetadata.mContentType, rv); + } + + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return nullptr; + } + + MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false)); + + return blobImpl.forget(); +} + +already_AddRefed<BlobImpl> +CreateBlobImpl(const ParentBlobConstructorParams& aParams, + const BlobData& aBlobData, + bool aIsSameProcessActor) +{ + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + MOZ_ASSERT(aParams.blobParams().type() == + AnyBlobConstructorParams::TNormalBlobConstructorParams || + aParams.blobParams().type() == + AnyBlobConstructorParams::TFileBlobConstructorParams); + + CreateBlobImplMetadata metadata(aIsSameProcessActor); + + if (aParams.blobParams().type() == + AnyBlobConstructorParams::TNormalBlobConstructorParams) { + const NormalBlobConstructorParams& params = + aParams.blobParams().get_NormalBlobConstructorParams(); + + if (NS_WARN_IF(params.length() == UINT64_MAX)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + metadata.mContentType = params.contentType(); + metadata.mLength = params.length(); + } else { + const FileBlobConstructorParams& params = + aParams.blobParams().get_FileBlobConstructorParams(); + + if (NS_WARN_IF(params.length() == UINT64_MAX)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + if (NS_WARN_IF(params.modDate() == INT64_MAX)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + if (NS_WARN_IF(!params.path().IsEmpty())) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + metadata.mContentType = params.contentType(); + metadata.mName = params.name(); + metadata.mLength = params.length(); + metadata.mLastModifiedDate = params.modDate(); + } + + RefPtr<BlobImpl> blobImpl = + CreateBlobImplFromBlobData(aBlobData, metadata); + return blobImpl.forget(); +} + +template <class ChildManagerType> +void +BlobDataFromBlobImpl(ChildManagerType* aManager, BlobImpl* aBlobImpl, + BlobData& aBlobData, + nsTArray<UniquePtr<AutoIPCStream>>& aIPCStreams) +{ + MOZ_ASSERT(gProcessType != GeckoProcessType_Default); + MOZ_ASSERT(aBlobImpl); + + const nsTArray<RefPtr<BlobImpl>>* subBlobs = aBlobImpl->GetSubBlobImpls(); + + if (subBlobs) { + MOZ_ASSERT(subBlobs->Length()); + + aBlobData = nsTArray<BlobData>(); + + nsTArray<BlobData>& subBlobDatas = aBlobData.get_ArrayOfBlobData(); + subBlobDatas.SetLength(subBlobs->Length()); + + for (uint32_t count = subBlobs->Length(), index = 0; + index < count; + index++) { + BlobDataFromBlobImpl(aManager, subBlobs->ElementAt(index), + subBlobDatas[index], aIPCStreams); + } + + return; + } + + nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlobImpl); + if (remoteBlob) { + BlobChild* actor = remoteBlob->GetBlobChild(); + MOZ_ASSERT(actor); + + aBlobData = actor->ParentID(); + return; + } + + ErrorResult rv; + uint64_t length = aBlobImpl->GetSize(rv); + MOZ_ALWAYS_TRUE(!rv.Failed()); + + nsCOMPtr<nsIInputStream> inputStream; + aBlobImpl->GetInternalStream(getter_AddRefs(inputStream), rv); + MOZ_ALWAYS_TRUE(!rv.Failed()); + + UniquePtr<AutoIPCStream> autoStream(new AutoIPCStream()); + autoStream->Serialize(inputStream, aManager); + aBlobData = BlobDataStream(autoStream->TakeValue(), length); + + aIPCStreams.AppendElement(Move(autoStream)); +} + +RemoteInputStream::RemoteInputStream(BlobImpl* aBlobImpl, + uint64_t aStart, + uint64_t aLength) + : mMonitor("RemoteInputStream.mMonitor") + , mActor(nullptr) + , mBlobImpl(aBlobImpl) + , mWeakSeekableStream(nullptr) + , mWeakFileMetadata(nullptr) + , mStart(aStart) + , mLength(aLength) +{ + MOZ_ASSERT(aBlobImpl); + + if (!NS_IsMainThread()) { + mEventTarget = do_GetCurrentThread(); + MOZ_ASSERT(mEventTarget); + } + + MOZ_ASSERT(IsOnOwningThread()); +} + +RemoteInputStream::RemoteInputStream(BlobChild* aActor, + BlobImpl* aBlobImpl, + uint64_t aStart, + uint64_t aLength) + : mMonitor("RemoteInputStream.mMonitor") + , mActor(aActor) + , mBlobImpl(aBlobImpl) + , mEventTarget(NS_GetCurrentThread()) + , mWeakSeekableStream(nullptr) + , mWeakFileMetadata(nullptr) + , mStart(aStart) + , mLength(aLength) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(aActor); + MOZ_ASSERT(aBlobImpl); + + MOZ_ASSERT(IsOnOwningThread()); +} + +RemoteInputStream::~RemoteInputStream() +{ + if (!IsOnOwningThread()) { + mStream = nullptr; + mWeakSeekableStream = nullptr; + mWeakFileMetadata = nullptr; + + if (mBlobImpl) { + ReleaseOnTarget(mBlobImpl, mEventTarget); + } + } +} + +void +RemoteInputStream::SetStream(nsIInputStream* aStream) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aStream); + + nsCOMPtr<nsIInputStream> stream = aStream; + nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aStream); + nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(aStream); + + MOZ_ASSERT_IF(seekableStream, SameCOMIdentity(aStream, seekableStream)); + MOZ_ASSERT_IF(fileMetadata, SameCOMIdentity(aStream, fileMetadata)); + + { + MonitorAutoLock lock(mMonitor); + + MOZ_ASSERT_IF(mStream, IsWorkerStream()); + + if (!mStream) { + MOZ_ASSERT(!mWeakSeekableStream); + MOZ_ASSERT(!mWeakFileMetadata); + + mStream.swap(stream); + mWeakSeekableStream = seekableStream; + mWeakFileMetadata = fileMetadata; + + mMonitor.Notify(); + } + } +} + +nsresult +RemoteInputStream::BlockAndWaitForStream() +{ + if (mStream) { + return NS_OK; + } + + if (IsOnOwningThread()) { + if (NS_IsMainThread()) { + NS_WARNING("Blocking the main thread is not supported!"); + return NS_ERROR_FAILURE; + } + + MOZ_ASSERT(IsWorkerStream()); + + InputStreamParams params; + OptionalFileDescriptorSet optionalFDs; + + mActor->SendBlobStreamSync(mStart, mLength, ¶ms, &optionalFDs); + + nsTArray<FileDescriptor> fds; + OptionalFileDescriptorSetToFDs(optionalFDs, fds); + + nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(params, fds); + MOZ_ASSERT(stream); + + SetStream(stream); + return NS_OK; + } + + ReallyBlockAndWaitForStream(); + + return NS_OK; +} + +void +RemoteInputStream::ReallyBlockAndWaitForStream() +{ + MOZ_ASSERT(!IsOnOwningThread()); + + DebugOnly<bool> waited; + + { + MonitorAutoLock lock(mMonitor); + + waited = !mStream; + + while (!mStream) { + mMonitor.Wait(); + } + } + + MOZ_ASSERT(mStream); + +#ifdef DEBUG + if (waited && mWeakSeekableStream) { + int64_t position; + if (NS_SUCCEEDED(mWeakSeekableStream->Tell(&position))) { + MOZ_ASSERT(!position, "Stream not starting at 0!"); + } + } +#endif +} + +bool +RemoteInputStream::IsSeekableStream() +{ + if (IsOnOwningThread()) { + if (!mStream) { + NS_WARNING("Don't know if this stream is seekable yet!"); + return true; + } + } else { + ReallyBlockAndWaitForStream(); + } + + return !!mWeakSeekableStream; +} + +bool +RemoteInputStream::IsFileMetadata() +{ + if (IsOnOwningThread()) { + if (!mStream) { + NS_WARNING("Don't know if this stream supports file metadata yet!"); + return true; + } + } else { + ReallyBlockAndWaitForStream(); + } + + return !!mWeakFileMetadata; +} + +NS_IMPL_ADDREF(RemoteInputStream) +NS_IMPL_RELEASE(RemoteInputStream) + +NS_INTERFACE_MAP_BEGIN(RemoteInputStream) + NS_INTERFACE_MAP_ENTRY(nsIInputStream) + NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekableStream()) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileMetadata, IsFileMetadata()) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream) + NS_INTERFACE_MAP_ENTRY(IPrivateRemoteInputStream) +NS_INTERFACE_MAP_END + +NS_IMETHODIMP +RemoteInputStream::Close() +{ + nsresult rv = BlockAndWaitForStream(); + NS_ENSURE_SUCCESS(rv, rv); + + RefPtr<BlobImpl> blobImpl; + mBlobImpl.swap(blobImpl); + + rv = mStream->Close(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +RemoteInputStream::Available(uint64_t* aAvailable) +{ + if (!IsOnOwningThread()) { + nsresult rv = BlockAndWaitForStream(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mStream->Available(aAvailable); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; + } + +#ifdef DEBUG + if (NS_IsMainThread()) { + NS_WARNING("Someone is trying to do main-thread I/O..."); + } +#endif + + nsresult rv; + + // See if we already have our real stream. + nsCOMPtr<nsIInputStream> inputStream; + { + MonitorAutoLock lock(mMonitor); + + inputStream = mStream; + } + + // If we do then just call through. + if (inputStream) { + rv = inputStream->Available(aAvailable); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; + } + + // If the stream is already closed then we can't do anything. + if (!mBlobImpl) { + return NS_BASE_STREAM_CLOSED; + } + + // Otherwise fake it... + NS_WARNING("Available() called before real stream has been delivered, " + "guessing the amount of data available!"); + + ErrorResult error; + *aAvailable = mBlobImpl->GetSize(error); + if (NS_WARN_IF(error.Failed())) { + return error.StealNSResult(); + } + + return NS_OK; +} + +NS_IMETHODIMP +RemoteInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aResult) +{ + nsresult rv = BlockAndWaitForStream(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mStream->Read(aBuffer, aCount, aResult); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +RemoteInputStream::ReadSegments(nsWriteSegmentFun aWriter, + void* aClosure, + uint32_t aCount, + uint32_t* aResult) +{ + nsresult rv = BlockAndWaitForStream(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mStream->ReadSegments(aWriter, aClosure, aCount, aResult); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +RemoteInputStream::IsNonBlocking(bool* aNonBlocking) +{ + NS_ENSURE_ARG_POINTER(aNonBlocking); + + *aNonBlocking = false; + return NS_OK; +} + +NS_IMETHODIMP +RemoteInputStream::Seek(int32_t aWhence, int64_t aOffset) +{ + nsresult rv = BlockAndWaitForStream(); + NS_ENSURE_SUCCESS(rv, rv); + + if (!mWeakSeekableStream) { + NS_WARNING("Underlying blob stream is not seekable!"); + return NS_ERROR_NO_INTERFACE; + } + + rv = mWeakSeekableStream->Seek(aWhence, aOffset); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +RemoteInputStream::Tell(int64_t* aResult) +{ + // We can cheat here and assume that we're going to start at 0 if we don't yet + // have our stream. Though, really, this should abort since most input streams + // could block here. + if (IsOnOwningThread() && !mStream) { + *aResult = 0; + return NS_OK; + } + + nsresult rv = BlockAndWaitForStream(); + NS_ENSURE_SUCCESS(rv, rv); + + if (!mWeakSeekableStream) { + NS_WARNING("Underlying blob stream is not seekable!"); + return NS_ERROR_NO_INTERFACE; + } + + rv = mWeakSeekableStream->Tell(aResult); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +RemoteInputStream::SetEOF() +{ + nsresult rv = BlockAndWaitForStream(); + NS_ENSURE_SUCCESS(rv, rv); + + if (!mWeakSeekableStream) { + NS_WARNING("Underlying blob stream is not seekable!"); + return NS_ERROR_NO_INTERFACE; + } + + rv = mWeakSeekableStream->SetEOF(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +void +RemoteInputStream::Serialize(InputStreamParams& aParams, + FileDescriptorArray& /* aFDs */) +{ + MOZ_RELEASE_ASSERT(mBlobImpl); + + nsCOMPtr<nsIRemoteBlob> remote = do_QueryInterface(mBlobImpl); + MOZ_ASSERT(remote); + + BlobChild* actor = remote->GetBlobChild(); + MOZ_ASSERT(actor); + + aParams = RemoteInputStreamParams(actor->ParentID()); +} + +bool +RemoteInputStream::Deserialize(const InputStreamParams& /* aParams */, + const FileDescriptorArray& /* aFDs */) +{ + // See InputStreamUtils.cpp to see how deserialization of a + // RemoteInputStream is special-cased. + MOZ_CRASH("RemoteInputStream should never be deserialized"); +} + +NS_IMETHODIMP +RemoteInputStream::GetSize(int64_t* aSize) +{ + nsresult rv = BlockAndWaitForStream(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!mWeakFileMetadata) { + NS_WARNING("Underlying blob stream doesn't support file metadata!"); + return NS_ERROR_NO_INTERFACE; + } + + rv = mWeakFileMetadata->GetSize(aSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +NS_IMETHODIMP +RemoteInputStream::GetLastModified(int64_t* aLastModified) +{ + nsresult rv = BlockAndWaitForStream(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!mWeakFileMetadata) { + NS_WARNING("Underlying blob stream doesn't support file metadata!"); + return NS_ERROR_NO_INTERFACE; + } + + rv = mWeakFileMetadata->GetLastModified(aLastModified); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +NS_IMETHODIMP +RemoteInputStream::GetFileDescriptor(PRFileDesc** aFileDescriptor) +{ + nsresult rv = BlockAndWaitForStream(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!mWeakFileMetadata) { + NS_WARNING("Underlying blob stream doesn't support file metadata!"); + return NS_ERROR_NO_INTERFACE; + } + + rv = mWeakFileMetadata->GetFileDescriptor(aFileDescriptor); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +Maybe<uint64_t> +RemoteInputStream::ExpectedSerializedLength() +{ + return Nothing(); +} + +nsIInputStream* +RemoteInputStream::BlockAndGetInternalStream() +{ + MOZ_ASSERT(!IsOnOwningThread()); + + nsresult rv = BlockAndWaitForStream(); + NS_ENSURE_SUCCESS(rv, nullptr); + + return mStream; +} + +} // namespace + +StaticAutoPtr<BlobParent::IDTable> BlobParent::sIDTable; +StaticAutoPtr<Mutex> BlobParent::sIDTableMutex; + +/******************************************************************************* + * BlobParent::IDTableEntry Declaration + ******************************************************************************/ + +class BlobParent::IDTableEntry final +{ + const nsID mID; + const intptr_t mProcessID; + const RefPtr<BlobImpl> mBlobImpl; + +public: + static already_AddRefed<IDTableEntry> + Create(const nsID& aID, intptr_t aProcessID, BlobImpl* aBlobImpl) + { + MOZ_ASSERT(aBlobImpl); + + DebugOnly<bool> isMutable; + MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable))); + MOZ_ASSERT(!isMutable); + + return GetOrCreateInternal(aID, + aProcessID, + aBlobImpl, + /* aMayCreate */ true, + /* aMayGet */ false, + /* aIgnoreProcessID */ false); + } + + static already_AddRefed<IDTableEntry> + Get(const nsID& aID, intptr_t aProcessID) + { + return GetOrCreateInternal(aID, + aProcessID, + nullptr, + /* aMayCreate */ false, + /* aMayGet */ true, + /* aIgnoreProcessID */ false); + } + + static already_AddRefed<IDTableEntry> + Get(const nsID& aID) + { + return GetOrCreateInternal(aID, + 0, + nullptr, + /* aMayCreate */ false, + /* aMayGet */ true, + /* aIgnoreProcessID */ true); + } + + static already_AddRefed<IDTableEntry> + GetOrCreate(const nsID& aID, intptr_t aProcessID, BlobImpl* aBlobImpl) + { + MOZ_ASSERT(aBlobImpl); + + DebugOnly<bool> isMutable; + MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable))); + MOZ_ASSERT(!isMutable); + + return GetOrCreateInternal(aID, + aProcessID, + aBlobImpl, + /* aMayCreate */ true, + /* aMayGet */ true, + /* aIgnoreProcessID */ false); + } + + const nsID& + ID() const + { + return mID; + } + + intptr_t + ProcessID() const + { + return mProcessID; + } + + BlobImpl* + GetBlobImpl() const + { + return mBlobImpl; + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(IDTableEntry) + +private: + IDTableEntry(const nsID& aID, intptr_t aProcessID, BlobImpl* aBlobImpl); + ~IDTableEntry(); + + static already_AddRefed<IDTableEntry> + GetOrCreateInternal(const nsID& aID, + intptr_t aProcessID, + BlobImpl* aBlobImpl, + bool aMayCreate, + bool aMayGet, + bool aIgnoreProcessID); +}; + +/******************************************************************************* + * BlobParent::OpenStreamRunnable Declaration + ******************************************************************************/ + +// Each instance of this class will be dispatched to the network stream thread +// pool to run the first time where it will open the file input stream. It will +// then dispatch itself back to the owning thread to send the child process its +// response (assuming that the child has not crashed). The runnable will then +// dispatch itself to the thread pool again in order to close the file input +// stream. +class BlobParent::OpenStreamRunnable final + : public Runnable +{ + friend class nsRevocableEventPtr<OpenStreamRunnable>; + + // Only safe to access these pointers if mRevoked is false! + BlobParent* mBlobActor; + InputStreamParent* mStreamActor; + + nsCOMPtr<nsIInputStream> mStream; + nsCOMPtr<nsIIPCSerializableInputStream> mSerializable; + nsCOMPtr<nsIEventTarget> mActorTarget; + nsCOMPtr<nsIThread> mIOTarget; + + bool mRevoked; + bool mClosing; + +public: + OpenStreamRunnable(BlobParent* aBlobActor, + InputStreamParent* aStreamActor, + nsIInputStream* aStream, + nsIIPCSerializableInputStream* aSerializable, + nsIThread* aIOTarget) + : mBlobActor(aBlobActor) + , mStreamActor(aStreamActor) + , mStream(aStream) + , mSerializable(aSerializable) + , mIOTarget(aIOTarget) + , mRevoked(false) + , mClosing(false) + { + MOZ_ASSERT(aBlobActor); + aBlobActor->AssertIsOnOwningThread(); + MOZ_ASSERT(aStreamActor); + MOZ_ASSERT(aStream); + // aSerializable may be null. + MOZ_ASSERT(aIOTarget); + + if (!NS_IsMainThread()) { + AssertIsOnBackgroundThread(); + + mActorTarget = do_GetCurrentThread(); + MOZ_ASSERT(mActorTarget); + } + + AssertIsOnOwningThread(); + } + + nsresult + Dispatch() + { + AssertIsOnOwningThread(); + MOZ_ASSERT(mIOTarget); + + nsresult rv = mIOTarget->Dispatch(this, NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; + } + + NS_DECL_ISUPPORTS_INHERITED + +private: + ~OpenStreamRunnable() + { } + + bool + IsOnOwningThread() const + { + return EventTargetIsOnCurrentThread(mActorTarget); + } + + void + AssertIsOnOwningThread() const + { + MOZ_ASSERT(IsOnOwningThread()); + } + + void + Revoke() + { + AssertIsOnOwningThread(); +#ifdef DEBUG + mBlobActor = nullptr; + mStreamActor = nullptr; +#endif + mRevoked = true; + } + + nsresult + OpenStream() + { + MOZ_ASSERT(!IsOnOwningThread()); + MOZ_ASSERT(mStream); + + if (!mSerializable) { + nsCOMPtr<IPrivateRemoteInputStream> remoteStream = + do_QueryInterface(mStream); + MOZ_ASSERT(remoteStream, "Must QI to IPrivateRemoteInputStream here!"); + + nsCOMPtr<nsIInputStream> realStream = + remoteStream->BlockAndGetInternalStream(); + NS_ENSURE_TRUE(realStream, NS_ERROR_FAILURE); + + mSerializable = do_QueryInterface(realStream); + if (!mSerializable) { + MOZ_ASSERT(false, "Must be serializable!"); + return NS_ERROR_FAILURE; + } + + mStream.swap(realStream); + } + + // To force the stream open we call Available(). We don't actually care + // how much data is available. + uint64_t available; + if (NS_FAILED(mStream->Available(&available))) { + NS_WARNING("Available failed on this stream!"); + } + + if (mActorTarget) { + nsresult rv = mActorTarget->Dispatch(this, NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + } else { + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this)); + } + + return NS_OK; + } + + nsresult + CloseStream() + { + MOZ_ASSERT(!IsOnOwningThread()); + MOZ_ASSERT(mStream); + + // Going to always release here. + nsCOMPtr<nsIInputStream> stream; + mStream.swap(stream); + + nsCOMPtr<nsIThread> ioTarget; + mIOTarget.swap(ioTarget); + + DebugOnly<nsresult> rv = stream->Close(); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to close stream!"); + + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewRunnableMethod(ioTarget, &nsIThread::Shutdown))); + + return NS_OK; + } + + nsresult + SendResponse() + { + AssertIsOnOwningThread(); + MOZ_ASSERT(mStream); + MOZ_ASSERT(mSerializable); + MOZ_ASSERT(mIOTarget); + MOZ_ASSERT(!mClosing); + + nsCOMPtr<nsIIPCSerializableInputStream> serializable; + mSerializable.swap(serializable); + + if (mRevoked) { + MOZ_ASSERT(!mBlobActor); + MOZ_ASSERT(!mStreamActor); + } + else { + MOZ_ASSERT(mBlobActor); + MOZ_ASSERT(mBlobActor->HasManager()); + MOZ_ASSERT(mStreamActor); + + InputStreamParams params; + nsTArray<FileDescriptor> fds; + serializable->Serialize(params, fds); + + MOZ_ASSERT(params.type() != InputStreamParams::T__None); + + OptionalFileDescriptorSet optionalFDSet; + if (nsIContentParent* contentManager = mBlobActor->GetContentManager()) { + ConstructFileDescriptorSet(contentManager, fds, optionalFDSet); + } else { + ConstructFileDescriptorSet(mBlobActor->GetBackgroundManager(), + fds, + optionalFDSet); + } + + mStreamActor->Destroy(params, optionalFDSet); + + mBlobActor->NoteRunnableCompleted(this); + +#ifdef DEBUG + mBlobActor = nullptr; + mStreamActor = nullptr; +#endif + } + + // If our luck is *really* bad then it is possible for the CloseStream() and + // nsIThread::Shutdown() functions to run before the Dispatch() call here + // finishes... Keep the thread alive until this method returns. + nsCOMPtr<nsIThread> kungFuDeathGrip = mIOTarget; + + mClosing = true; + + nsresult rv = kungFuDeathGrip->Dispatch(this, NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; + } + + NS_IMETHOD + Run() override + { + MOZ_ASSERT(mIOTarget); + + if (IsOnOwningThread()) { + return SendResponse(); + } + + if (!mClosing) { + return OpenStream(); + } + + return CloseStream(); + } +}; + +NS_IMPL_ISUPPORTS_INHERITED0(BlobParent::OpenStreamRunnable, Runnable) + +/******************************************************************************* + * BlobChild::RemoteBlobImpl Declaration + ******************************************************************************/ + +class BlobChild::RemoteBlobImpl + : public BlobImplBase + , public nsIRemoteBlob +{ +protected: + class CreateStreamHelper; + class WorkerHolder; + + BlobChild* mActor; + nsCOMPtr<nsIEventTarget> mActorTarget; + + // These member variables are protected by mutex and it's set to null when the + // worker goes away. + WorkerPrivate* mWorkerPrivate; + nsAutoPtr<WorkerHolder> mWorkerHolder; + Mutex mMutex; + + // We use this pointer to keep a live a blobImpl coming from a different + // process until this one is fully created. We set it to null when + // SendCreatedFromKnownBlob() is received. This is used only with KnownBlob + // params in the CTOR of a IPC BlobImpl. + RefPtr<BlobImpl> mDifferentProcessBlobImpl; + + RefPtr<BlobImpl> mSameProcessBlobImpl; + + const bool mIsSlice; + + const bool mIsDirectory; + +public: + + enum BlobImplIsDirectory + { + eNotDirectory, + eDirectory + }; + + // For File. + RemoteBlobImpl(BlobChild* aActor, + BlobImpl* aRemoteBlobImpl, + const nsAString& aName, + const nsAString& aContentType, + const nsAString& aPath, + uint64_t aLength, + int64_t aModDate, + BlobImplIsDirectory aIsDirectory, + bool aIsSameProcessBlob); + + // For Blob. + RemoteBlobImpl(BlobChild* aActor, + BlobImpl* aRemoteBlobImpl, + const nsAString& aContentType, + uint64_t aLength, + bool aIsSameProcessBlob); + + // For mystery blobs. + explicit + RemoteBlobImpl(BlobChild* aActor); + + void + NoteDyingActor(); + + BlobChild* + GetActor() const + { + MOZ_ASSERT(ActorEventTargetIsOnCurrentThread()); + + return mActor; + } + + nsIEventTarget* + GetActorEventTarget() const + { + return mActorTarget; + } + + bool + ActorEventTargetIsOnCurrentThread() const + { + return EventTargetIsOnCurrentThread(BaseRemoteBlobImpl()->mActorTarget); + } + + bool + IsSlice() const + { + return mIsSlice; + } + + RemoteBlobSliceImpl* + AsSlice() const; + + RemoteBlobImpl* + BaseRemoteBlobImpl() const; + + NS_DECL_ISUPPORTS_INHERITED + + virtual void + GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const override; + + virtual bool + IsDirectory() const override; + + virtual already_AddRefed<BlobImpl> + CreateSlice(uint64_t aStart, + uint64_t aLength, + const nsAString& aContentType, + ErrorResult& aRv) override; + + virtual void + GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) override; + + virtual int64_t + GetFileId() override; + + virtual int64_t + GetLastModified(ErrorResult& aRv) override; + + virtual void + SetLastModified(int64_t aLastModified) override; + + virtual nsresult + SetMutable(bool aMutable) override; + + virtual BlobChild* + GetBlobChild() override; + + virtual BlobParent* + GetBlobParent() override; + + void + NullifyDifferentProcessBlobImpl() + { + MOZ_ASSERT(mDifferentProcessBlobImpl); + mDifferentProcessBlobImpl = nullptr; + } + + // Used only by CreateStreamHelper, it dispatches a runnable to the target + // thread. This thread can be the main-thread, the background thread or a + // worker. If the thread is a worker, the aRunnable is wrapper into a + // ControlRunnable in order to avoid to be blocked into a sync event loop. + nsresult + DispatchToTarget(nsIRunnable* aRunnable); + + void + WorkerHasNotified(); + +protected: + // For SliceImpl. + RemoteBlobImpl(const nsAString& aContentType, uint64_t aLength); + + ~RemoteBlobImpl() + { + MOZ_ASSERT_IF(mActorTarget, + EventTargetIsOnCurrentThread(mActorTarget)); + } + + void + CommonInit(BlobChild* aActor); + + void + Destroy(); +}; + +class BlobChild::RemoteBlobImpl::WorkerHolder final + : public workers::WorkerHolder +{ + // Raw pointer because this class is kept alive by the mRemoteBlobImpl. + RemoteBlobImpl* mRemoteBlobImpl; + +public: + explicit WorkerHolder(RemoteBlobImpl* aRemoteBlobImpl) + : mRemoteBlobImpl(aRemoteBlobImpl) + { + MOZ_ASSERT(aRemoteBlobImpl); + } + + bool Notify(Status aStatus) override + { + mRemoteBlobImpl->WorkerHasNotified(); + return true; + } +}; + +class BlobChild::RemoteBlobImpl::CreateStreamHelper final + : public CancelableRunnable +{ + Monitor mMonitor; + RefPtr<RemoteBlobImpl> mRemoteBlobImpl; + RefPtr<RemoteInputStream> mInputStream; + const uint64_t mStart; + const uint64_t mLength; + bool mDone; + +public: + explicit CreateStreamHelper(RemoteBlobImpl* aRemoteBlobImpl); + + nsresult + GetStream(nsIInputStream** aInputStream); + + NS_IMETHOD Run() override; + +private: + ~CreateStreamHelper() + { + MOZ_ASSERT(!mRemoteBlobImpl); + MOZ_ASSERT(!mInputStream); + MOZ_ASSERT(mDone); + } + + void + RunInternal(RemoteBlobImpl* aBaseRemoteBlobImpl, bool aNotify); +}; + +class BlobChild::RemoteBlobSliceImpl final + : public RemoteBlobImpl +{ + RefPtr<RemoteBlobImpl> mParent; + bool mActorWasCreated; + +public: + RemoteBlobSliceImpl(RemoteBlobImpl* aParent, + uint64_t aStart, + uint64_t aLength, + const nsAString& aContentType); + + RemoteBlobImpl* + Parent() const + { + MOZ_ASSERT(mParent); + + return const_cast<RemoteBlobImpl*>(mParent.get()); + } + + uint64_t + Start() const + { + return mStart; + } + + void + EnsureActorWasCreated() + { + MOZ_ASSERT_IF(!ActorEventTargetIsOnCurrentThread(), + mActorWasCreated); + + if (!mActorWasCreated) { + EnsureActorWasCreatedInternal(); + } + } + + NS_DECL_ISUPPORTS_INHERITED + + virtual BlobChild* + GetBlobChild() override; + +private: + ~RemoteBlobSliceImpl() + { } + + void + EnsureActorWasCreatedInternal(); +}; + +/******************************************************************************* + * BlobParent::RemoteBlobImpl Declaration + ******************************************************************************/ + +class BlobParent::RemoteBlobImpl final + : public BlobImpl + , public nsIRemoteBlob +{ + BlobParent* mActor; + nsCOMPtr<nsIEventTarget> mActorTarget; + RefPtr<BlobImpl> mBlobImpl; + +public: + RemoteBlobImpl(BlobParent* aActor, BlobImpl* aBlobImpl); + + void + NoteDyingActor(); + + NS_DECL_ISUPPORTS_INHERITED + + virtual void + GetName(nsAString& aName) const override; + + virtual void + GetDOMPath(nsAString& aPath) const override; + + virtual void + SetDOMPath(const nsAString& aPath) override; + + virtual int64_t + GetLastModified(ErrorResult& aRv) override; + + virtual void + SetLastModified(int64_t aLastModified) override; + + virtual void + GetMozFullPath(nsAString& aName, ErrorResult& aRv) const override; + + virtual void + GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const override; + + virtual bool + IsDirectory() const override; + + virtual uint64_t + GetSize(ErrorResult& aRv) override; + + virtual void + GetType(nsAString& aType) override; + + virtual uint64_t + GetSerialNumber() const override; + + virtual already_AddRefed<BlobImpl> + CreateSlice(uint64_t aStart, + uint64_t aLength, + const nsAString& aContentType, + ErrorResult& aRv) override; + + virtual const nsTArray<RefPtr<BlobImpl>>* + GetSubBlobImpls() const override; + + virtual void + GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) override; + + virtual int64_t + GetFileId() override; + + virtual nsresult + GetSendInfo(nsIInputStream** aBody, + uint64_t* aContentLength, + nsACString& aContentType, + nsACString& aCharset) override; + + virtual nsresult + GetMutable(bool* aMutable) const override; + + virtual nsresult + SetMutable(bool aMutable) override; + + virtual void + SetLazyData(const nsAString& aName, + const nsAString& aContentType, + uint64_t aLength, + int64_t aLastModifiedDate) override; + + virtual bool + IsMemoryFile() const override; + + virtual bool + IsSizeUnknown() const override; + + virtual bool + IsDateUnknown() const override; + + virtual bool + IsFile() const override; + + virtual bool + MayBeClonedToOtherThreads() const override; + + virtual BlobChild* + GetBlobChild() override; + + virtual BlobParent* + GetBlobParent() override; + +private: + ~RemoteBlobImpl() + { + MOZ_ASSERT_IF(mActorTarget, + EventTargetIsOnCurrentThread(mActorTarget)); + } + + void + Destroy(); +}; + +/******************************************************************************* + * BlobChild::RemoteBlobImpl + ******************************************************************************/ + +BlobChild:: +RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor, + BlobImpl* aRemoteBlobImpl, + const nsAString& aName, + const nsAString& aContentType, + const nsAString& aDOMPath, + uint64_t aLength, + int64_t aModDate, + BlobImplIsDirectory aIsDirectory, + bool aIsSameProcessBlob) + : BlobImplBase(aName, aContentType, aLength, aModDate) + , mWorkerPrivate(nullptr) + , mMutex("BlobChild::RemoteBlobImpl::mMutex") + , mIsSlice(false), mIsDirectory(aIsDirectory == eDirectory) +{ + SetDOMPath(aDOMPath); + + if (aIsSameProcessBlob) { + MOZ_ASSERT(aRemoteBlobImpl); + mSameProcessBlobImpl = aRemoteBlobImpl; + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + } else { + mDifferentProcessBlobImpl = aRemoteBlobImpl; + } + + CommonInit(aActor); +} + +BlobChild:: +RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor, + BlobImpl* aRemoteBlobImpl, + const nsAString& aContentType, + uint64_t aLength, + bool aIsSameProcessBlob) + : BlobImplBase(aContentType, aLength) + , mWorkerPrivate(nullptr) + , mMutex("BlobChild::RemoteBlobImpl::mMutex") + , mIsSlice(false), mIsDirectory(false) +{ + if (aIsSameProcessBlob) { + MOZ_ASSERT(aRemoteBlobImpl); + mSameProcessBlobImpl = aRemoteBlobImpl; + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + } else { + mDifferentProcessBlobImpl = aRemoteBlobImpl; + } + + CommonInit(aActor); +} + +BlobChild:: +RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor) + : BlobImplBase(EmptyString(), EmptyString(), UINT64_MAX, INT64_MAX) + , mWorkerPrivate(nullptr) + , mMutex("BlobChild::RemoteBlobImpl::mMutex") + , mIsSlice(false), mIsDirectory(false) +{ + CommonInit(aActor); +} + +BlobChild:: +RemoteBlobImpl::RemoteBlobImpl(const nsAString& aContentType, uint64_t aLength) + : BlobImplBase(aContentType, aLength) + , mActor(nullptr) + , mWorkerPrivate(nullptr) + , mMutex("BlobChild::RemoteBlobImpl::mMutex") + , mIsSlice(true) + , mIsDirectory(false) +{ + mImmutable = true; +} + +void +BlobChild:: +RemoteBlobImpl::CommonInit(BlobChild* aActor) +{ + MOZ_ASSERT(aActor); + aActor->AssertIsOnOwningThread(); + + mActor = aActor; + mActorTarget = aActor->EventTarget(); + + if (!NS_IsMainThread()) { + mWorkerPrivate = GetCurrentThreadWorkerPrivate(); + // We must comunicate via IPC in the owning thread, so, if this BlobImpl has + // been created on a Workerr and then it's sent to a different thread (for + // instance the main-thread), we still need to keep alive that Worker. + if (mWorkerPrivate) { + mWorkerHolder = new RemoteBlobImpl::WorkerHolder(this); + if (NS_WARN_IF(!mWorkerHolder->HoldWorker(mWorkerPrivate, Closing))) { + // We don't care too much if the worker is already going away because no + // sync-event-loop can be created at this point. + mWorkerPrivate = nullptr; + mWorkerHolder = nullptr; + } + } + } + + mImmutable = true; +} + +void +BlobChild:: +RemoteBlobImpl::NoteDyingActor() +{ + MOZ_ASSERT(mActor); + mActor->AssertIsOnOwningThread(); + + mActor = nullptr; +} + +BlobChild::RemoteBlobSliceImpl* +BlobChild:: +RemoteBlobImpl::AsSlice() const +{ + MOZ_ASSERT(IsSlice()); + + return static_cast<RemoteBlobSliceImpl*>(const_cast<RemoteBlobImpl*>(this)); +} + +BlobChild::RemoteBlobImpl* +BlobChild:: +RemoteBlobImpl::BaseRemoteBlobImpl() const +{ + if (IsSlice()) { + return AsSlice()->Parent()->BaseRemoteBlobImpl(); + } + + return const_cast<RemoteBlobImpl*>(this); +} + +void +BlobChild:: +RemoteBlobImpl::Destroy() +{ + if (EventTargetIsOnCurrentThread(mActorTarget)) { + if (mActor) { + mActor->AssertIsOnOwningThread(); + mActor->NoteDyingRemoteBlobImpl(); + } + + if (mWorkerHolder) { + // We are in the worker thread. + MutexAutoLock lock(mMutex); + mWorkerPrivate = nullptr; + mWorkerHolder = nullptr; + } + + delete this; + return; + } + + nsCOMPtr<nsIRunnable> destroyRunnable = + NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy); + + if (mActorTarget) { + destroyRunnable = + new CancelableRunnableWrapper(destroyRunnable, mActorTarget); + + MOZ_ALWAYS_SUCCEEDS(mActorTarget->Dispatch(destroyRunnable, + NS_DISPATCH_NORMAL)); + } else { + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(destroyRunnable)); + } +} + +NS_IMPL_ADDREF(BlobChild::RemoteBlobImpl) +NS_IMPL_RELEASE_WITH_DESTROY(BlobChild::RemoteBlobImpl, Destroy()) +NS_IMPL_QUERY_INTERFACE_INHERITED(BlobChild::RemoteBlobImpl, + BlobImpl, + nsIRemoteBlob) + +void +BlobChild:: +RemoteBlobImpl::GetMozFullPathInternal(nsAString& aFilePath, + ErrorResult& aRv) const +{ + if (!EventTargetIsOnCurrentThread(mActorTarget)) { + MOZ_CRASH("Not implemented!"); + } + + if (mSameProcessBlobImpl) { + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + + mSameProcessBlobImpl->GetMozFullPathInternal(aFilePath, aRv); + return; + } + + if (!mActor) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return; + } + + nsString filePath; + if (!mActor->SendGetFilePath(&filePath)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + aFilePath = filePath; +} + +bool +BlobChild:: +RemoteBlobImpl::IsDirectory() const +{ + return mIsDirectory; +} + +already_AddRefed<BlobImpl> +BlobChild:: +RemoteBlobImpl::CreateSlice(uint64_t aStart, + uint64_t aLength, + const nsAString& aContentType, + ErrorResult& aRv) +{ + // May be called on any thread. + if (mSameProcessBlobImpl) { + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + + return mSameProcessBlobImpl->CreateSlice(aStart, + aLength, + aContentType, + aRv); + } + + RefPtr<RemoteBlobSliceImpl> slice = + new RemoteBlobSliceImpl(this, aStart, aLength, aContentType); + return slice.forget(); +} + +void +BlobChild:: +RemoteBlobImpl::GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) +{ + // May be called on any thread. + if (mSameProcessBlobImpl) { + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + + nsCOMPtr<nsIInputStream> realStream; + mSameProcessBlobImpl->GetInternalStream(getter_AddRefs(realStream), aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + RefPtr<BlobInputStreamTether> tether = + new BlobInputStreamTether(realStream, mSameProcessBlobImpl); + tether.forget(aStream); + return; + } + + RefPtr<CreateStreamHelper> helper = new CreateStreamHelper(this); + aRv = helper->GetStream(aStream); + if (NS_WARN_IF(aRv.Failed())) { + return; + } +} + +int64_t +BlobChild:: +RemoteBlobImpl::GetFileId() +{ + if (!EventTargetIsOnCurrentThread(mActorTarget)) { + MOZ_CRASH("Not implemented!"); + } + + if (mSameProcessBlobImpl) { + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + + return mSameProcessBlobImpl->GetFileId(); + } + + int64_t fileId; + if (mActor && mActor->SendGetFileId(&fileId)) { + return fileId; + } + + return -1; +} + +int64_t +BlobChild:: +RemoteBlobImpl::GetLastModified(ErrorResult& aRv) +{ + if (IsDateUnknown()) { + return 0; + } + + return mLastModificationDate; +} + +void +BlobChild:: +RemoteBlobImpl::SetLastModified(int64_t aLastModified) +{ + MOZ_CRASH("SetLastModified of a remote blob is not allowed!"); +} + +nsresult +BlobChild:: +RemoteBlobImpl::SetMutable(bool aMutable) +{ + if (!aMutable && IsSlice()) { + // Make sure that slices are backed by a real actor now while we are still + // on the correct thread. + AsSlice()->EnsureActorWasCreated(); + } + + nsresult rv = BlobImplBase::SetMutable(aMutable); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT_IF(!aMutable, mImmutable); + + return NS_OK; +} + +BlobChild* +BlobChild:: +RemoteBlobImpl::GetBlobChild() +{ + return mActor; +} + +BlobParent* +BlobChild:: +RemoteBlobImpl::GetBlobParent() +{ + return nullptr; +} + +class RemoteBlobControlRunnable : public WorkerControlRunnable +{ + nsCOMPtr<nsIRunnable> mRunnable; + +public: + RemoteBlobControlRunnable(WorkerPrivate* aWorkerPrivate, + nsIRunnable* aRunnable) + : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) + , mRunnable(aRunnable) + { + MOZ_ASSERT(aRunnable); + } + + bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override + { + mRunnable->Run(); + return true; + } +}; + +nsresult +BlobChild:: +RemoteBlobImpl::DispatchToTarget(nsIRunnable* aRunnable) +{ + MOZ_ASSERT(aRunnable); + + // We have to protected mWorkerPrivate because this method can be called by + // any thread (sort of). + MutexAutoLock lock(mMutex); + + if (mWorkerPrivate) { + MOZ_ASSERT(mWorkerHolder); + + RefPtr<RemoteBlobControlRunnable> controlRunnable = + new RemoteBlobControlRunnable(mWorkerPrivate, aRunnable); + if (!controlRunnable->Dispatch()) { + return NS_ERROR_FAILURE; + } + + return NS_OK; + } + + nsCOMPtr<nsIEventTarget> target = BaseRemoteBlobImpl()->GetActorEventTarget(); + if (!target) { + target = do_GetMainThread(); + } + + MOZ_ASSERT(target); + + return target->Dispatch(aRunnable, NS_DISPATCH_NORMAL); +} + +void +BlobChild:: +RemoteBlobImpl::WorkerHasNotified() +{ + MutexAutoLock lock(mMutex); + + mWorkerHolder->ReleaseWorker(); + + mWorkerHolder = nullptr; + mWorkerPrivate = nullptr; +} + +/******************************************************************************* + * BlobChild::RemoteBlobImpl::CreateStreamHelper + ******************************************************************************/ + +BlobChild::RemoteBlobImpl:: +CreateStreamHelper::CreateStreamHelper(RemoteBlobImpl* aRemoteBlobImpl) + : mMonitor("BlobChild::RemoteBlobImpl::CreateStreamHelper::mMonitor") + , mRemoteBlobImpl(aRemoteBlobImpl) + , mStart(aRemoteBlobImpl->IsSlice() ? aRemoteBlobImpl->AsSlice()->Start() : 0) + , mLength(0) + , mDone(false) +{ + // This may be created on any thread. + MOZ_ASSERT(aRemoteBlobImpl); + + ErrorResult rv; + const_cast<uint64_t&>(mLength) = aRemoteBlobImpl->GetSize(rv); + MOZ_ASSERT(!rv.Failed()); +} + +nsresult +BlobChild::RemoteBlobImpl:: +CreateStreamHelper::GetStream(nsIInputStream** aInputStream) +{ + // This may be called on any thread. + MOZ_ASSERT(aInputStream); + MOZ_ASSERT(mRemoteBlobImpl); + MOZ_ASSERT(!mInputStream); + MOZ_ASSERT(!mDone); + + RefPtr<RemoteBlobImpl> baseRemoteBlobImpl = + mRemoteBlobImpl->BaseRemoteBlobImpl(); + MOZ_ASSERT(baseRemoteBlobImpl); + + if (EventTargetIsOnCurrentThread(baseRemoteBlobImpl->GetActorEventTarget())) { + RunInternal(baseRemoteBlobImpl, false); + } else { + nsresult rv = baseRemoteBlobImpl->DispatchToTarget(this); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + DebugOnly<bool> warned = false; + + { + MonitorAutoLock lock(mMonitor); + + while (!mDone) { +#ifdef DEBUG + if (!warned) { + NS_WARNING("RemoteBlobImpl::GetInternalStream() called on thread " + "that can't send messages, blocking here to wait for the " + "actor's thread to send the message!"); + } +#endif + lock.Wait(); + } + } + } + + MOZ_ASSERT(!mRemoteBlobImpl); + MOZ_ASSERT(mDone); + + if (!mInputStream) { + return NS_ERROR_UNEXPECTED; + } + + mInputStream.forget(aInputStream); + return NS_OK; +} + +void +BlobChild::RemoteBlobImpl:: +CreateStreamHelper::RunInternal(RemoteBlobImpl* aBaseRemoteBlobImpl, + bool aNotify) +{ + MOZ_ASSERT(aBaseRemoteBlobImpl); + MOZ_ASSERT(aBaseRemoteBlobImpl->ActorEventTargetIsOnCurrentThread()); + MOZ_ASSERT(!mInputStream); + MOZ_ASSERT(!mDone); + + if (BlobChild* actor = aBaseRemoteBlobImpl->GetActor()) { + RefPtr<RemoteInputStream> stream; + + if (!NS_IsMainThread() && GetCurrentThreadWorkerPrivate()) { + stream = + new RemoteInputStream(actor, mRemoteBlobImpl, mStart, mLength); + } else { + stream = new RemoteInputStream(mRemoteBlobImpl, mStart, mLength); + } + + InputStreamChild* streamActor = new InputStreamChild(stream); + if (actor->SendPBlobStreamConstructor(streamActor, mStart, mLength)) { + stream.swap(mInputStream); + } + } + + mRemoteBlobImpl = nullptr; + + if (aNotify) { + MonitorAutoLock lock(mMonitor); + mDone = true; + lock.Notify(); + } else { + mDone = true; + } +} + +NS_IMETHODIMP +BlobChild::RemoteBlobImpl:: +CreateStreamHelper::Run() +{ + MOZ_ASSERT(mRemoteBlobImpl); + MOZ_ASSERT(mRemoteBlobImpl->ActorEventTargetIsOnCurrentThread()); + + RefPtr<RemoteBlobImpl> baseRemoteBlobImpl = + mRemoteBlobImpl->BaseRemoteBlobImpl(); + MOZ_ASSERT(baseRemoteBlobImpl); + + RunInternal(baseRemoteBlobImpl, true); + return NS_OK; +} + +/******************************************************************************* + * BlobChild::RemoteBlobSliceImpl + ******************************************************************************/ + +BlobChild:: +RemoteBlobSliceImpl::RemoteBlobSliceImpl(RemoteBlobImpl* aParent, + uint64_t aStart, + uint64_t aLength, + const nsAString& aContentType) + : RemoteBlobImpl(aContentType, aLength) + , mParent(aParent->BaseRemoteBlobImpl()) + , mActorWasCreated(false) +{ + MOZ_ASSERT(mParent); + MOZ_ASSERT(mParent->BaseRemoteBlobImpl() == mParent); + + DebugOnly<bool> isMutable; + MOZ_ASSERT(NS_SUCCEEDED(aParent->GetMutable(&isMutable))); + MOZ_ASSERT(!isMutable); + +#ifdef DEBUG + { + ErrorResult rv; + uint64_t parentSize = aParent->GetSize(rv); + MOZ_ASSERT(!rv.Failed()); + MOZ_ASSERT(parentSize >= aStart + aLength); + } +#endif + + // Account for the offset of the parent slice, if any. + mStart = aParent->IsSlice() ? aParent->AsSlice()->mStart + aStart : aStart; +} + +void +BlobChild:: +RemoteBlobSliceImpl::EnsureActorWasCreatedInternal() +{ + MOZ_ASSERT(ActorEventTargetIsOnCurrentThread()); + MOZ_ASSERT(!mActorWasCreated); + + mActorWasCreated = true; + + BlobChild* baseActor = mParent->GetActor(); + MOZ_ASSERT(baseActor); + MOZ_ASSERT(baseActor->HasManager()); + + nsID id; + MOZ_ALWAYS_SUCCEEDS(gUUIDGenerator->GenerateUUIDInPlace(&id)); + + ParentBlobConstructorParams params( + SlicedBlobConstructorParams(nullptr /* sourceParent */, + baseActor /* sourceChild */, + id /* id */, + mStart /* begin */, + mStart + mLength /* end */, + mContentType /* contentType */)); + + BlobChild* actor; + + if (nsIContentChild* contentManager = baseActor->GetContentManager()) { + actor = SendSliceConstructor(contentManager, this, params); + } else { + actor = + SendSliceConstructor(baseActor->GetBackgroundManager(), this, params); + } + + CommonInit(actor); +} + +NS_IMPL_ISUPPORTS_INHERITED0(BlobChild::RemoteBlobSliceImpl, + BlobChild::RemoteBlobImpl) + +BlobChild* +BlobChild:: +RemoteBlobSliceImpl::GetBlobChild() +{ + EnsureActorWasCreated(); + + return RemoteBlobImpl::GetBlobChild(); +} + +/******************************************************************************* + * BlobParent::RemoteBlobImpl + ******************************************************************************/ + +BlobParent:: +RemoteBlobImpl::RemoteBlobImpl(BlobParent* aActor, BlobImpl* aBlobImpl) + : mActor(aActor) + , mActorTarget(aActor->EventTarget()) + , mBlobImpl(aBlobImpl) +{ + MOZ_ASSERT(aActor); + aActor->AssertIsOnOwningThread(); + MOZ_ASSERT(aBlobImpl); + + DebugOnly<bool> isMutable; + MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable))); + MOZ_ASSERT(!isMutable); +} + +void +BlobParent:: +RemoteBlobImpl::NoteDyingActor() +{ + MOZ_ASSERT(mActor); + mActor->AssertIsOnOwningThread(); + + mActor = nullptr; +} + +void +BlobParent:: +RemoteBlobImpl::Destroy() +{ + if (EventTargetIsOnCurrentThread(mActorTarget)) { + if (mActor) { + mActor->AssertIsOnOwningThread(); + mActor->NoteDyingRemoteBlobImpl(); + } + + delete this; + return; + } + + nsCOMPtr<nsIRunnable> destroyRunnable = + NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy); + + if (mActorTarget) { + destroyRunnable = + new CancelableRunnableWrapper(destroyRunnable, mActorTarget); + + MOZ_ALWAYS_SUCCEEDS(mActorTarget->Dispatch(destroyRunnable, + NS_DISPATCH_NORMAL)); + } else { + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(destroyRunnable)); + } +} + +NS_IMPL_ADDREF(BlobParent::RemoteBlobImpl) +NS_IMPL_RELEASE_WITH_DESTROY(BlobParent::RemoteBlobImpl, Destroy()) +NS_IMPL_QUERY_INTERFACE_INHERITED(BlobParent::RemoteBlobImpl, + BlobImpl, + nsIRemoteBlob) + +void +BlobParent:: +RemoteBlobImpl::GetName(nsAString& aName) const +{ + mBlobImpl->GetName(aName); +} + +void +BlobParent:: +RemoteBlobImpl::GetDOMPath(nsAString& aPath) const +{ + mBlobImpl->GetDOMPath(aPath); +} + +void +BlobParent:: +RemoteBlobImpl::SetDOMPath(const nsAString& aPath) +{ + mBlobImpl->SetDOMPath(aPath); +} + +int64_t +BlobParent:: +RemoteBlobImpl::GetLastModified(ErrorResult& aRv) +{ + return mBlobImpl->GetLastModified(aRv); +} + +void +BlobParent:: +RemoteBlobImpl::SetLastModified(int64_t aLastModified) +{ + MOZ_CRASH("SetLastModified of a remote blob is not allowed!"); +} + +void +BlobParent:: +RemoteBlobImpl::GetMozFullPath(nsAString& aName, ErrorResult& aRv) const +{ + mBlobImpl->GetMozFullPath(aName, aRv); +} + +void +BlobParent:: +RemoteBlobImpl::GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const +{ + mBlobImpl->GetMozFullPathInternal(aFileName, aRv); +} + +bool +BlobParent:: +RemoteBlobImpl::IsDirectory() const +{ + return mBlobImpl->IsDirectory(); +} + +uint64_t +BlobParent:: +RemoteBlobImpl::GetSize(ErrorResult& aRv) +{ + return mBlobImpl->GetSize(aRv); +} + +void +BlobParent:: +RemoteBlobImpl::GetType(nsAString& aType) +{ + mBlobImpl->GetType(aType); +} + +uint64_t +BlobParent:: +RemoteBlobImpl::GetSerialNumber() const +{ + return mBlobImpl->GetSerialNumber(); +} + +already_AddRefed<BlobImpl> +BlobParent:: +RemoteBlobImpl::CreateSlice(uint64_t aStart, + uint64_t aLength, + const nsAString& aContentType, + ErrorResult& aRv) +{ + return mBlobImpl->CreateSlice(aStart, aLength, aContentType, aRv); +} + +const nsTArray<RefPtr<BlobImpl>>* +BlobParent:: +RemoteBlobImpl::GetSubBlobImpls() const +{ + return mBlobImpl->GetSubBlobImpls(); +} + +void +BlobParent:: +RemoteBlobImpl::GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) +{ + mBlobImpl->GetInternalStream(aStream, aRv); +} + +int64_t +BlobParent:: +RemoteBlobImpl::GetFileId() +{ + return mBlobImpl->GetFileId(); +} + +nsresult +BlobParent:: +RemoteBlobImpl::GetSendInfo(nsIInputStream** aBody, + uint64_t* aContentLength, + nsACString& aContentType, + nsACString& aCharset) +{ + return mBlobImpl->GetSendInfo(aBody, + aContentLength, + aContentType, + aCharset); +} + +nsresult +BlobParent:: +RemoteBlobImpl::GetMutable(bool* aMutable) const +{ + return mBlobImpl->GetMutable(aMutable); +} + +nsresult +BlobParent:: +RemoteBlobImpl::SetMutable(bool aMutable) +{ + return mBlobImpl->SetMutable(aMutable); +} + +void +BlobParent:: +RemoteBlobImpl::SetLazyData(const nsAString& aName, + const nsAString& aContentType, + uint64_t aLength, + int64_t aLastModifiedDate) +{ + MOZ_CRASH("This should never be called!"); +} + +bool +BlobParent:: +RemoteBlobImpl::IsMemoryFile() const +{ + return mBlobImpl->IsMemoryFile(); +} + +bool +BlobParent:: +RemoteBlobImpl::IsSizeUnknown() const +{ + return mBlobImpl->IsSizeUnknown(); +} + +bool +BlobParent:: +RemoteBlobImpl::IsDateUnknown() const +{ + return mBlobImpl->IsDateUnknown(); +} + +bool +BlobParent:: +RemoteBlobImpl::IsFile() const +{ + return mBlobImpl->IsFile(); +} + +bool +BlobParent:: +RemoteBlobImpl::MayBeClonedToOtherThreads() const +{ + return mBlobImpl->MayBeClonedToOtherThreads(); +} + +BlobChild* +BlobParent:: +RemoteBlobImpl::GetBlobChild() +{ + return nullptr; +} + +BlobParent* +BlobParent:: +RemoteBlobImpl::GetBlobParent() +{ + return mActor; +} + +/******************************************************************************* + * BlobChild + ******************************************************************************/ + +BlobChild::BlobChild(nsIContentChild* aManager, BlobImpl* aBlobImpl) + : mBackgroundManager(nullptr) + , mContentManager(aManager) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + CommonInit(aBlobImpl); +} + +BlobChild::BlobChild(PBackgroundChild* aManager, BlobImpl* aBlobImpl) + : mBackgroundManager(aManager) + , mContentManager(nullptr) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + if (!NS_IsMainThread()) { + mEventTarget = do_GetCurrentThread(); + MOZ_ASSERT(mEventTarget); + } + + CommonInit(aBlobImpl); +} + +BlobChild::BlobChild(nsIContentChild* aManager, BlobChild* aOther) + : mBackgroundManager(nullptr) + , mContentManager(aManager) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + CommonInit(aOther, /* aBlobImpl */ nullptr); +} + +BlobChild::BlobChild(PBackgroundChild* aManager, + BlobChild* aOther, + BlobImpl* aBlobImpl) + : mBackgroundManager(aManager) + , mContentManager(nullptr) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + MOZ_ASSERT(aBlobImpl); + + if (!NS_IsMainThread()) { + mEventTarget = do_GetCurrentThread(); + MOZ_ASSERT(mEventTarget); + } + + CommonInit(aOther, aBlobImpl); +} + +BlobChild::BlobChild(nsIContentChild* aManager, + const ChildBlobConstructorParams& aParams) + : mBackgroundManager(nullptr) + , mContentManager(aManager) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + CommonInit(aParams); +} + +BlobChild::BlobChild(PBackgroundChild* aManager, + const ChildBlobConstructorParams& aParams) + : mBackgroundManager(aManager) + , mContentManager(nullptr) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + if (!NS_IsMainThread()) { + mEventTarget = do_GetCurrentThread(); + MOZ_ASSERT(mEventTarget); + } + + CommonInit(aParams); +} + +BlobChild::BlobChild(nsIContentChild* aManager, + const nsID& aParentID, + RemoteBlobSliceImpl* aRemoteBlobSliceImpl) + : mBackgroundManager(nullptr) + , mContentManager(aManager) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + CommonInit(aParentID, aRemoteBlobSliceImpl); +} + +BlobChild::BlobChild(PBackgroundChild* aManager, + const nsID& aParentID, + RemoteBlobSliceImpl* aRemoteBlobSliceImpl) + : mBackgroundManager(aManager) + , mContentManager(nullptr) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + if (!NS_IsMainThread()) { + mEventTarget = do_GetCurrentThread(); + MOZ_ASSERT(mEventTarget); + } + + CommonInit(aParentID, aRemoteBlobSliceImpl); +} + +BlobChild::~BlobChild() +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(BlobChild); +} + +void +BlobChild::CommonInit(BlobImpl* aBlobImpl) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aBlobImpl); + + MOZ_COUNT_CTOR(BlobChild); + + mBlobImpl = aBlobImpl; + mRemoteBlobImpl = nullptr; + + mBlobImpl->AddRef(); + mOwnsBlobImpl = true; + + memset(&mParentID, 0, sizeof(mParentID)); +} + +void +BlobChild::CommonInit(BlobChild* aOther, BlobImpl* aBlobImpl) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aOther); + MOZ_ASSERT_IF(mContentManager, aOther->GetBackgroundManager()); + MOZ_ASSERT_IF(mContentManager, !aBlobImpl); + MOZ_ASSERT_IF(mBackgroundManager, aBlobImpl); + + RefPtr<BlobImpl> otherImpl; + if (mBackgroundManager && aOther->GetBackgroundManager()) { + otherImpl = aBlobImpl; + } else { + otherImpl = aOther->GetBlobImpl(); + } + MOZ_ASSERT(otherImpl); + + nsString contentType; + otherImpl->GetType(contentType); + + ErrorResult rv; + uint64_t length = otherImpl->GetSize(rv); + MOZ_ASSERT(!rv.Failed()); + + RemoteBlobImpl* remoteBlob = nullptr; + if (otherImpl->IsFile()) { + nsAutoString name; + otherImpl->GetName(name); + + nsAutoString domPath; + otherImpl->GetDOMPath(domPath); + + int64_t modDate = otherImpl->GetLastModified(rv); + MOZ_ASSERT(!rv.Failed()); + + RemoteBlobImpl::BlobImplIsDirectory directory = otherImpl->IsDirectory() ? + RemoteBlobImpl::BlobImplIsDirectory::eDirectory : + RemoteBlobImpl::BlobImplIsDirectory::eNotDirectory; + + remoteBlob = + new RemoteBlobImpl(this, otherImpl, name, contentType, domPath, + length, modDate, directory, + false /* SameProcessBlobImpl */); + } else { + remoteBlob = new RemoteBlobImpl(this, otherImpl, contentType, length, + false /* SameProcessBlobImpl */); + } + + // This RemoteBlob must be kept alive untill RecvCreatedFromKnownBlob is + // called because the parent will send this notification and we must be able + // to manage it. + MOZ_ASSERT(remoteBlob); + remoteBlob->AddRef(); + + CommonInit(aOther->ParentID(), remoteBlob); +} + +void +BlobChild::CommonInit(const ChildBlobConstructorParams& aParams) +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(BlobChild); + + const AnyBlobConstructorParams& blobParams = aParams.blobParams(); + + AnyBlobConstructorParams::Type paramsType = blobParams.type(); + MOZ_ASSERT(paramsType != AnyBlobConstructorParams::T__None && + paramsType != + AnyBlobConstructorParams::TSlicedBlobConstructorParams && + paramsType != + AnyBlobConstructorParams::TKnownBlobConstructorParams); + + RefPtr<RemoteBlobImpl> remoteBlob; + + switch (paramsType) { + case AnyBlobConstructorParams::TNormalBlobConstructorParams: { + const NormalBlobConstructorParams& params = + blobParams.get_NormalBlobConstructorParams(); + remoteBlob = + new RemoteBlobImpl(this, nullptr, params.contentType(), params.length(), + false /* SameProcessBlobImpl */); + break; + } + + case AnyBlobConstructorParams::TFileBlobConstructorParams: { + const FileBlobConstructorParams& params = + blobParams.get_FileBlobConstructorParams(); + RemoteBlobImpl::BlobImplIsDirectory directory = params.isDirectory() ? + RemoteBlobImpl::BlobImplIsDirectory::eDirectory : + RemoteBlobImpl::BlobImplIsDirectory::eNotDirectory; + remoteBlob = new RemoteBlobImpl(this, + nullptr, + params.name(), + params.contentType(), + params.path(), + params.length(), + params.modDate(), + directory, + false /* SameProcessBlobImpl */); + break; + } + + case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: { + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + + const SameProcessBlobConstructorParams& params = + blobParams.get_SameProcessBlobConstructorParams(); + MOZ_ASSERT(params.addRefedBlobImpl()); + + RefPtr<BlobImpl> blobImpl = + dont_AddRef(reinterpret_cast<BlobImpl*>(params.addRefedBlobImpl())); + + ErrorResult rv; + uint64_t size = blobImpl->GetSize(rv); + MOZ_ASSERT(!rv.Failed()); + + nsString contentType; + blobImpl->GetType(contentType); + + if (blobImpl->IsFile()) { + nsAutoString name; + blobImpl->GetName(name); + + nsAutoString domPath; + blobImpl->GetDOMPath(domPath); + + int64_t lastModifiedDate = blobImpl->GetLastModified(rv); + MOZ_ASSERT(!rv.Failed()); + + RemoteBlobImpl::BlobImplIsDirectory directory = + blobImpl->IsDirectory() ? + RemoteBlobImpl::BlobImplIsDirectory::eDirectory : + RemoteBlobImpl::BlobImplIsDirectory::eNotDirectory; + + remoteBlob = + new RemoteBlobImpl(this, + blobImpl, + name, + contentType, + domPath, + size, + lastModifiedDate, + directory, + true /* SameProcessBlobImpl */); + } else { + remoteBlob = new RemoteBlobImpl(this, blobImpl, contentType, size, + true /* SameProcessBlobImpl */); + } + + break; + } + + case AnyBlobConstructorParams::TMysteryBlobConstructorParams: { + remoteBlob = new RemoteBlobImpl(this); + break; + } + + default: + MOZ_CRASH("Unknown params!"); + } + + MOZ_ASSERT(remoteBlob); + + DebugOnly<bool> isMutable; + MOZ_ASSERT(NS_SUCCEEDED(remoteBlob->GetMutable(&isMutable))); + MOZ_ASSERT(!isMutable); + + mRemoteBlobImpl = remoteBlob; + + remoteBlob.forget(&mBlobImpl); + mOwnsBlobImpl = true; + + mParentID = aParams.id(); +} + +void +BlobChild::CommonInit(const nsID& aParentID, RemoteBlobImpl* aRemoteBlobImpl) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aRemoteBlobImpl); + + DebugOnly<bool> isMutable; + MOZ_ASSERT(NS_SUCCEEDED(aRemoteBlobImpl->GetMutable(&isMutable))); + MOZ_ASSERT(!isMutable); + + MOZ_COUNT_CTOR(BlobChild); + + RefPtr<RemoteBlobImpl> remoteBlob = aRemoteBlobImpl; + + mRemoteBlobImpl = remoteBlob; + + remoteBlob.forget(&mBlobImpl); + mOwnsBlobImpl = true; + + mParentID = aParentID; +} + +#ifdef DEBUG + +void +BlobChild::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(IsOnOwningThread()); +} + +#endif // DEBUG + +// static +void +BlobChild::Startup(const FriendKey& /* aKey */) +{ + MOZ_ASSERT(!XRE_IsParentProcess()); + + CommonStartup(); +} + +// static +BlobChild* +BlobChild::GetOrCreate(nsIContentChild* aManager, BlobImpl* aBlobImpl) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + return GetOrCreateFromImpl(aManager, aBlobImpl); +} + +// static +BlobChild* +BlobChild::GetOrCreate(PBackgroundChild* aManager, BlobImpl* aBlobImpl) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + return GetOrCreateFromImpl(aManager, aBlobImpl); +} + +// static +BlobChild* +BlobChild::Create(nsIContentChild* aManager, + const ChildBlobConstructorParams& aParams) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + return CreateFromParams(aManager, aParams); +} + +// static +BlobChild* +BlobChild::Create(PBackgroundChild* aManager, + const ChildBlobConstructorParams& aParams) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + return CreateFromParams(aManager, aParams); +} + +// static +template <class ChildManagerType> +BlobChild* +BlobChild::GetOrCreateFromImpl(ChildManagerType* aManager, + BlobImpl* aBlobImpl) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + MOZ_ASSERT(aBlobImpl); + + // If the blob represents a wrapper around real blob implementation (so called + // snapshot) then we need to get the real one. + if (nsCOMPtr<PIBlobImplSnapshot> snapshot = do_QueryInterface(aBlobImpl)) { + aBlobImpl = snapshot->GetBlobImpl(); + if (!aBlobImpl) { + // The snapshot is not valid anymore. + return nullptr; + } + } + + // If the blob represents a remote blob then we can simply pass its actor back + // here. + if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlobImpl)) { + BlobChild* actor = + MaybeGetActorFromRemoteBlob(remoteBlob, aManager, aBlobImpl); + if (actor) { + return actor; + } + } + + // All blobs shared between threads or processes must be immutable. + if (NS_WARN_IF(NS_FAILED(aBlobImpl->SetMutable(false)))) { + return nullptr; + } + + MOZ_ASSERT(!aBlobImpl->IsSizeUnknown()); + MOZ_ASSERT(!aBlobImpl->IsDateUnknown()); + + AnyBlobConstructorParams blobParams; + nsTArray<UniquePtr<AutoIPCStream>> autoIPCStreams; + + if (gProcessType == GeckoProcessType_Default) { + RefPtr<BlobImpl> sameProcessImpl = aBlobImpl; + auto addRefedBlobImpl = + reinterpret_cast<intptr_t>(sameProcessImpl.forget().take()); + + blobParams = SameProcessBlobConstructorParams(addRefedBlobImpl); + } else { + // BlobData is going to be populate here and it _must_ be send via IPC in + // order to avoid leaks. + BlobData blobData; + BlobDataFromBlobImpl(aManager, aBlobImpl, blobData, autoIPCStreams); + + nsString contentType; + aBlobImpl->GetType(contentType); + + ErrorResult rv; + uint64_t length = aBlobImpl->GetSize(rv); + MOZ_ASSERT(!rv.Failed()); + + if (aBlobImpl->IsFile()) { + nsAutoString name; + aBlobImpl->GetName(name); + + nsAutoString domPath; + aBlobImpl->GetDOMPath(domPath); + + int64_t modDate = aBlobImpl->GetLastModified(rv); + MOZ_ASSERT(!rv.Failed()); + + blobParams = + FileBlobConstructorParams(name, contentType, domPath, length, modDate, + aBlobImpl->IsDirectory(), blobData); + } else { + blobParams = NormalBlobConstructorParams(contentType, length, blobData); + } + } + + BlobChild* actor = new BlobChild(aManager, aBlobImpl); + + ParentBlobConstructorParams params(blobParams); + + if (NS_WARN_IF(!aManager->SendPBlobConstructor(actor, params))) { + return nullptr; + } + + autoIPCStreams.Clear(); + return actor; +} + +// static +template <class ChildManagerType> +BlobChild* +BlobChild::CreateFromParams(ChildManagerType* aManager, + const ChildBlobConstructorParams& aParams) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + const AnyBlobConstructorParams& blobParams = aParams.blobParams(); + + switch (blobParams.type()) { + case AnyBlobConstructorParams::TNormalBlobConstructorParams: + case AnyBlobConstructorParams::TFileBlobConstructorParams: + case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: + case AnyBlobConstructorParams::TMysteryBlobConstructorParams: { + return new BlobChild(aManager, aParams); + } + + case AnyBlobConstructorParams::TSlicedBlobConstructorParams: { + MOZ_CRASH("Parent should never send SlicedBlobConstructorParams!"); + } + + case AnyBlobConstructorParams::TKnownBlobConstructorParams: { + MOZ_CRASH("Parent should never send KnownBlobConstructorParams!"); + } + + default: + MOZ_CRASH("Unknown params!"); + } + + MOZ_CRASH("Should never get here!"); +} + +// static +template <class ChildManagerType> +BlobChild* +BlobChild::SendSliceConstructor(ChildManagerType* aManager, + RemoteBlobSliceImpl* aRemoteBlobSliceImpl, + const ParentBlobConstructorParams& aParams) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + MOZ_ASSERT(aRemoteBlobSliceImpl); + MOZ_ASSERT(aParams.blobParams().type() == + AnyBlobConstructorParams::TSlicedBlobConstructorParams); + + const nsID& id = aParams.blobParams().get_SlicedBlobConstructorParams().id(); + + BlobChild* newActor = new BlobChild(aManager, id, aRemoteBlobSliceImpl); + + if (aManager->SendPBlobConstructor(newActor, aParams)) { + if (gProcessType != GeckoProcessType_Default || !NS_IsMainThread()) { + newActor->SendWaitForSliceCreation(); + } + return newActor; + } + + return nullptr; +} + +// static +BlobChild* +BlobChild::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob, + nsIContentChild* aManager, + BlobImpl* aBlobImpl) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aRemoteBlob); + MOZ_ASSERT(aManager); + MOZ_ASSERT(aBlobImpl); + + if (BlobChild* actor = aRemoteBlob->GetBlobChild()) { + if (actor->GetContentManager() == aManager) { + return actor; + } + + MOZ_ASSERT(actor->GetBackgroundManager()); + + actor = new BlobChild(aManager, actor); + + ParentBlobConstructorParams params( + KnownBlobConstructorParams(actor->ParentID())); + + aManager->SendPBlobConstructor(actor, params); + + return actor; + } + + return nullptr; +} + +// static +BlobChild* +BlobChild::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob, + PBackgroundChild* aManager, + BlobImpl* aBlobImpl) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aRemoteBlob); + MOZ_ASSERT(aManager); + MOZ_ASSERT(aBlobImpl); + + if (BlobChild* actor = aRemoteBlob->GetBlobChild()) { + if (actor->GetBackgroundManager() == aManager) { + return actor; + } + + actor = new BlobChild(aManager, actor, aBlobImpl); + + ParentBlobConstructorParams params( + KnownBlobConstructorParams(actor->ParentID())); + + aManager->SendPBlobConstructor(actor, params); + + return actor; + } + + return nullptr; +} + +const nsID& +BlobChild::ParentID() const +{ + MOZ_ASSERT(mRemoteBlobImpl); + + return mParentID; +} + +already_AddRefed<BlobImpl> +BlobChild::GetBlobImpl() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mBlobImpl); + + RefPtr<BlobImpl> blobImpl; + + // Remote blobs are held alive until the first call to GetBlobImpl. Thereafter + // we only hold a weak reference. Normal blobs are held alive until the actor + // is destroyed. + if (mRemoteBlobImpl && mOwnsBlobImpl) { + blobImpl = dont_AddRef(mBlobImpl); + mOwnsBlobImpl = false; + } else { + blobImpl = mBlobImpl; + } + + MOZ_ASSERT(blobImpl); + + return blobImpl.forget(); +} + +bool +BlobChild::SetMysteryBlobInfo(const nsString& aName, + const nsString& aContentType, + uint64_t aLength, + int64_t aLastModifiedDate) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mBlobImpl); + MOZ_ASSERT(!mBlobImpl->IsDirectory()); + MOZ_ASSERT(mRemoteBlobImpl); + MOZ_ASSERT(!mRemoteBlobImpl->IsDirectory()); + MOZ_ASSERT(aLastModifiedDate != INT64_MAX); + + mBlobImpl->SetLazyData(aName, aContentType, aLength, aLastModifiedDate); + + FileBlobConstructorParams params(aName, + aContentType, + EmptyString(), + aLength, + aLastModifiedDate, + mBlobImpl->IsDirectory(), + void_t() /* optionalBlobData */); + return SendResolveMystery(params); +} + +bool +BlobChild::SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mBlobImpl); + MOZ_ASSERT(mRemoteBlobImpl); + + mBlobImpl->SetLazyData(NullString(), aContentType, aLength, INT64_MAX); + + NormalBlobConstructorParams params(aContentType, + aLength, + void_t() /* optionalBlobData */); + return SendResolveMystery(params); +} + +void +BlobChild::NoteDyingRemoteBlobImpl() +{ + MOZ_ASSERT(mBlobImpl); + MOZ_ASSERT(mRemoteBlobImpl); + MOZ_ASSERT(!mOwnsBlobImpl); + + // This may be called on any thread due to the fact that RemoteBlobImpl is + // designed to be passed between threads. We must start the shutdown process + // on the owning thread, so we proxy here if necessary. + if (!IsOnOwningThread()) { + nsCOMPtr<nsIRunnable> runnable = + NewNonOwningRunnableMethod(this, &BlobChild::NoteDyingRemoteBlobImpl); + + if (mEventTarget) { + runnable = new CancelableRunnableWrapper(runnable, mEventTarget); + + MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(runnable, + NS_DISPATCH_NORMAL)); + } else { + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable)); + } + + return; + } + + // Must do this before calling Send__delete__ or we'll crash there trying to + // access a dangling pointer. + mBlobImpl = nullptr; + mRemoteBlobImpl = nullptr; + + PBlobChild::Send__delete__(this); +} + +bool +BlobChild::IsOnOwningThread() const +{ + return EventTargetIsOnCurrentThread(mEventTarget); +} + +void +BlobChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + if (mRemoteBlobImpl) { + mRemoteBlobImpl->NoteDyingActor(); + } + + if (mBlobImpl && mOwnsBlobImpl) { + mBlobImpl->Release(); + } + +#ifdef DEBUG + mBlobImpl = nullptr; + mRemoteBlobImpl = nullptr; + mBackgroundManager = nullptr; + mContentManager = nullptr; + mOwnsBlobImpl = false; +#endif +} + +PBlobStreamChild* +BlobChild::AllocPBlobStreamChild(const uint64_t& aStart, + const uint64_t& aLength) +{ + AssertIsOnOwningThread(); + + return new InputStreamChild(); +} + +bool +BlobChild::DeallocPBlobStreamChild(PBlobStreamChild* aActor) +{ + AssertIsOnOwningThread(); + + delete static_cast<InputStreamChild*>(aActor); + return true; +} + +bool +BlobChild::RecvCreatedFromKnownBlob() +{ + MOZ_ASSERT(mRemoteBlobImpl); + + // Releasing the other blob now that this blob is fully created. + mRemoteBlobImpl->NullifyDifferentProcessBlobImpl(); + + // Release the additional reference to ourself that was added in order to + // receive this RecvCreatedFromKnownBlob. + mRemoteBlobImpl->Release(); + return true; +} + +/******************************************************************************* + * BlobParent + ******************************************************************************/ + +BlobParent::BlobParent(nsIContentParent* aManager, IDTableEntry* aIDTableEntry) + : mBackgroundManager(nullptr) + , mContentManager(aManager) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + CommonInit(aIDTableEntry); +} + +BlobParent::BlobParent(PBackgroundParent* aManager, IDTableEntry* aIDTableEntry) + : mBackgroundManager(aManager) + , mContentManager(nullptr) + , mEventTarget(do_GetCurrentThread()) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + MOZ_ASSERT(mEventTarget); + + CommonInit(aIDTableEntry); +} + +BlobParent::BlobParent(nsIContentParent* aManager, + BlobImpl* aBlobImpl, + IDTableEntry* aIDTableEntry) + : mBackgroundManager(nullptr) + , mContentManager(aManager) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + CommonInit(aBlobImpl, aIDTableEntry); +} + +BlobParent::BlobParent(PBackgroundParent* aManager, + BlobImpl* aBlobImpl, + IDTableEntry* aIDTableEntry) + : mBackgroundManager(aManager) + , mContentManager(nullptr) + , mEventTarget(do_GetCurrentThread()) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + MOZ_ASSERT(mEventTarget); + + CommonInit(aBlobImpl, aIDTableEntry); +} + +BlobParent::~BlobParent() +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(BlobParent); +} + +void +BlobParent::CommonInit(IDTableEntry* aIDTableEntry) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aIDTableEntry); + MOZ_ASSERT(aIDTableEntry->GetBlobImpl()); + + MOZ_COUNT_CTOR(BlobParent); + + mBlobImpl = aIDTableEntry->GetBlobImpl(); + mRemoteBlobImpl = nullptr; + + mBlobImpl->AddRef(); + mOwnsBlobImpl = true; + + mIDTableEntry = aIDTableEntry; +} + +void +BlobParent::CommonInit(BlobImpl* aBlobImpl, IDTableEntry* aIDTableEntry) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aBlobImpl); + MOZ_ASSERT(aIDTableEntry); + + MOZ_COUNT_CTOR(BlobParent); + + DebugOnly<bool> isMutable; + MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable))); + MOZ_ASSERT(!isMutable); + + RefPtr<RemoteBlobImpl> remoteBlobImpl = new RemoteBlobImpl(this, aBlobImpl); + + MOZ_ASSERT(NS_SUCCEEDED(remoteBlobImpl->GetMutable(&isMutable))); + MOZ_ASSERT(!isMutable); + + mRemoteBlobImpl = remoteBlobImpl; + + remoteBlobImpl.forget(&mBlobImpl); + mOwnsBlobImpl = true; + + mIDTableEntry = aIDTableEntry; +} + +#ifdef DEBUG + +void +BlobParent::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(IsOnOwningThread()); +} + +#endif // DEBUG + +// static +void +BlobParent::Startup(const FriendKey& /* aKey */) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + + CommonStartup(); + + ClearOnShutdown(&sIDTable); + + sIDTableMutex = new Mutex("BlobParent::sIDTableMutex"); + ClearOnShutdown(&sIDTableMutex); +} + +// static +BlobParent* +BlobParent::GetOrCreate(nsIContentParent* aManager, BlobImpl* aBlobImpl) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + return GetOrCreateFromImpl(aManager, aBlobImpl); +} + +// static +BlobParent* +BlobParent::GetOrCreate(PBackgroundParent* aManager, BlobImpl* aBlobImpl) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + return GetOrCreateFromImpl(aManager, aBlobImpl); +} + +// static +BlobParent* +BlobParent::Create(nsIContentParent* aManager, + const ParentBlobConstructorParams& aParams) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + return CreateFromParams(aManager, aParams); +} + +// static +BlobParent* +BlobParent::Create(PBackgroundParent* aManager, + const ParentBlobConstructorParams& aParams) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + return CreateFromParams(aManager, aParams); +} + +// static +already_AddRefed<BlobImpl> +BlobParent::GetBlobImplForID(const nsID& aID) +{ + if (NS_WARN_IF(gProcessType != GeckoProcessType_Default)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + RefPtr<IDTableEntry> idTableEntry = IDTableEntry::Get(aID); + if (NS_WARN_IF(!idTableEntry)) { + return nullptr; + } + + RefPtr<BlobImpl> blobImpl = idTableEntry->GetBlobImpl(); + MOZ_ASSERT(blobImpl); + + return blobImpl.forget(); +} + +// static +template <class ParentManagerType> +BlobParent* +BlobParent::GetOrCreateFromImpl(ParentManagerType* aManager, + BlobImpl* aBlobImpl) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + MOZ_ASSERT(aBlobImpl); + + MOZ_ASSERT(!nsCOMPtr<PIBlobImplSnapshot>(do_QueryInterface(aBlobImpl))); + + // If the blob represents a remote blob for this manager then we can simply + // pass its actor back here. + if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlobImpl)) { + BlobParent* actor = MaybeGetActorFromRemoteBlob(remoteBlob, aManager); + if (actor) { + return actor; + } + } + + // All blobs shared between threads or processes must be immutable. + if (NS_WARN_IF(NS_FAILED(aBlobImpl->SetMutable(false)))) { + return nullptr; + } + + AnyBlobConstructorParams blobParams; + + if (ActorManagerIsSameProcess(aManager)) { + RefPtr<BlobImpl> sameProcessImpl = aBlobImpl; + auto addRefedBlobImpl = + reinterpret_cast<intptr_t>(sameProcessImpl.forget().take()); + + blobParams = SameProcessBlobConstructorParams(addRefedBlobImpl); + } else { + if (aBlobImpl->IsSizeUnknown() || aBlobImpl->IsDateUnknown()) { + // We don't want to call GetSize or GetLastModifiedDate yet since that may + // stat a file on the this thread. Instead we'll learn the size lazily + // from the other side. + blobParams = MysteryBlobConstructorParams(); + } else { + nsString contentType; + aBlobImpl->GetType(contentType); + + ErrorResult rv; + uint64_t length = aBlobImpl->GetSize(rv); + MOZ_ASSERT(!rv.Failed()); + + if (aBlobImpl->IsFile()) { + nsAutoString name; + aBlobImpl->GetName(name); + + nsAutoString domPath; + aBlobImpl->GetDOMPath(domPath); + + int64_t modDate = aBlobImpl->GetLastModified(rv); + MOZ_ASSERT(!rv.Failed()); + + blobParams = + FileBlobConstructorParams(name, contentType, domPath, length, modDate, + aBlobImpl->IsDirectory(), void_t()); + } else { + blobParams = NormalBlobConstructorParams(contentType, length, void_t()); + } + } + } + + nsID id; + MOZ_ALWAYS_SUCCEEDS(gUUIDGenerator->GenerateUUIDInPlace(&id)); + + RefPtr<IDTableEntry> idTableEntry = + IDTableEntry::GetOrCreate(id, ActorManagerProcessID(aManager), aBlobImpl); + MOZ_ASSERT(idTableEntry); + + BlobParent* actor = new BlobParent(aManager, idTableEntry); + + ChildBlobConstructorParams params(id, blobParams); + if (NS_WARN_IF(!aManager->SendPBlobConstructor(actor, params))) { + return nullptr; + } + + return actor; +} + +// static +template <class ParentManagerType> +BlobParent* +BlobParent::CreateFromParams(ParentManagerType* aManager, + const ParentBlobConstructorParams& aParams) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + const AnyBlobConstructorParams& blobParams = aParams.blobParams(); + + switch (blobParams.type()) { + case AnyBlobConstructorParams::TMysteryBlobConstructorParams: { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + case AnyBlobConstructorParams::TNormalBlobConstructorParams: + case AnyBlobConstructorParams::TFileBlobConstructorParams: { + const OptionalBlobData& optionalBlobData = + blobParams.type() == + AnyBlobConstructorParams::TNormalBlobConstructorParams ? + blobParams.get_NormalBlobConstructorParams().optionalBlobData() : + blobParams.get_FileBlobConstructorParams().optionalBlobData(); + + if (NS_WARN_IF(optionalBlobData.type() != OptionalBlobData::TBlobData)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + RefPtr<BlobImpl> blobImpl = + CreateBlobImpl(aParams, + optionalBlobData.get_BlobData(), + ActorManagerIsSameProcess(aManager)); + if (NS_WARN_IF(!blobImpl)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + nsID id; + MOZ_ALWAYS_SUCCEEDS(gUUIDGenerator->GenerateUUIDInPlace(&id)); + + RefPtr<IDTableEntry> idTableEntry = + IDTableEntry::Create(id, ActorManagerProcessID(aManager), blobImpl); + if (NS_WARN_IF(!idTableEntry)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + return new BlobParent(aManager, blobImpl, idTableEntry); + } + + case AnyBlobConstructorParams::TSlicedBlobConstructorParams: { + const SlicedBlobConstructorParams& params = + blobParams.get_SlicedBlobConstructorParams(); + + if (NS_WARN_IF(params.end() < params.begin())) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + auto* actor = + const_cast<BlobParent*>( + static_cast<const BlobParent*>(params.sourceParent())); + MOZ_ASSERT(actor); + + RefPtr<BlobImpl> source = actor->GetBlobImpl(); + MOZ_ASSERT(source); + + ErrorResult rv; + RefPtr<BlobImpl> slice = + source->CreateSlice(params.begin(), + params.end() - params.begin(), + params.contentType(), + rv); + if (NS_WARN_IF(rv.Failed())) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + MOZ_ALWAYS_SUCCEEDS(slice->SetMutable(false)); + + RefPtr<IDTableEntry> idTableEntry = + IDTableEntry::Create(params.id(), + ActorManagerProcessID(aManager), + slice); + if (NS_WARN_IF(!idTableEntry)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + return new BlobParent(aManager, slice, idTableEntry); + } + + case AnyBlobConstructorParams::TKnownBlobConstructorParams: { + const KnownBlobConstructorParams& params = + blobParams.get_KnownBlobConstructorParams(); + + RefPtr<IDTableEntry> idTableEntry = + IDTableEntry::Get(params.id(), ActorManagerProcessID(aManager)); + if (NS_WARN_IF(!idTableEntry)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + return new BlobParent(aManager, idTableEntry); + } + + case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: { + if (NS_WARN_IF(!ActorManagerIsSameProcess(aManager))) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + const SameProcessBlobConstructorParams& params = + blobParams.get_SameProcessBlobConstructorParams(); + + RefPtr<BlobImpl> blobImpl = + dont_AddRef(reinterpret_cast<BlobImpl*>(params.addRefedBlobImpl())); + MOZ_ASSERT(blobImpl); + + nsID id; + MOZ_ALWAYS_SUCCEEDS(gUUIDGenerator->GenerateUUIDInPlace(&id)); + + RefPtr<IDTableEntry> idTableEntry = + IDTableEntry::Create(id, ActorManagerProcessID(aManager), blobImpl); + MOZ_ASSERT(idTableEntry); + + return new BlobParent(aManager, blobImpl, idTableEntry); + } + + default: + MOZ_CRASH("Unknown params!"); + } + + MOZ_CRASH("Should never get here!"); +} + +// static +template <class ParentManagerType> +BlobParent* +BlobParent::SendSliceConstructor( + ParentManagerType* aManager, + const ParentBlobConstructorParams& aParams, + const ChildBlobConstructorParams& aOtherSideParams) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aManager); + + BlobParent* newActor = BlobParent::Create(aManager, aParams); + MOZ_ASSERT(newActor); + + if (aManager->SendPBlobConstructor(newActor, aOtherSideParams)) { + return newActor; + } + + return nullptr; +} + +// static +BlobParent* +BlobParent::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob, + nsIContentParent* aManager) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aRemoteBlob); + MOZ_ASSERT(aManager); + + BlobParent* actor = aRemoteBlob->GetBlobParent(); + if (actor && actor->GetContentManager() == aManager) { + return actor; + } + + return nullptr; +} + +// static +BlobParent* +BlobParent::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob, + PBackgroundParent* aManager) +{ + AssertCorrectThreadForManager(aManager); + MOZ_ASSERT(aRemoteBlob); + MOZ_ASSERT(aManager); + + BlobParent* actor = aRemoteBlob->GetBlobParent(); + if (actor && actor->GetBackgroundManager() == aManager) { + return actor; + } + + return nullptr; +} + +already_AddRefed<BlobImpl> +BlobParent::GetBlobImpl() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mBlobImpl); + + RefPtr<BlobImpl> blobImpl; + + // Remote blobs are held alive until the first call to GetBlobImpl. Thereafter + // we only hold a weak reference. Normal blobs are held alive until the actor + // is destroyed. + if (mRemoteBlobImpl && mOwnsBlobImpl) { + blobImpl = dont_AddRef(mBlobImpl); + mOwnsBlobImpl = false; + } else { + blobImpl = mBlobImpl; + } + + MOZ_ASSERT(blobImpl); + + return blobImpl.forget(); +} + +void +BlobParent::NoteDyingRemoteBlobImpl() +{ + MOZ_ASSERT(mRemoteBlobImpl); + MOZ_ASSERT(!mOwnsBlobImpl); + + // This may be called on any thread due to the fact that RemoteBlobImpl is + // designed to be passed between threads. We must start the shutdown process + // on the main thread, so we proxy here if necessary. + if (!IsOnOwningThread()) { + nsCOMPtr<nsIRunnable> runnable = + NewNonOwningRunnableMethod(this, &BlobParent::NoteDyingRemoteBlobImpl); + + if (mEventTarget) { + runnable = new CancelableRunnableWrapper(runnable, mEventTarget); + + MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(runnable, + NS_DISPATCH_NORMAL)); + } else { + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable)); + } + + return; + } + + // Must do this before calling Send__delete__ or we'll crash there trying to + // access a dangling pointer. + mBlobImpl = nullptr; + mRemoteBlobImpl = nullptr; + + Unused << PBlobParent::Send__delete__(this); +} + +void +BlobParent::NoteRunnableCompleted(OpenStreamRunnable* aRunnable) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aRunnable); + + for (uint32_t count = mOpenStreamRunnables.Length(), index = 0; + index < count; + index++) { + nsRevocableEventPtr<OpenStreamRunnable>& runnable = + mOpenStreamRunnables[index]; + + if (runnable.get() == aRunnable) { + runnable.Forget(); + mOpenStreamRunnables.RemoveElementAt(index); + return; + } + } + + MOZ_CRASH("Runnable not in our array!"); +} + +bool +BlobParent::IsOnOwningThread() const +{ + return EventTargetIsOnCurrentThread(mEventTarget); +} + +void +BlobParent::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + if (mRemoteBlobImpl) { + mRemoteBlobImpl->NoteDyingActor(); + } + + if (mBlobImpl && mOwnsBlobImpl) { + mBlobImpl->Release(); + } + +#ifdef DEBUG + mBlobImpl = nullptr; + mRemoteBlobImpl = nullptr; + mBackgroundManager = nullptr; + mContentManager = nullptr; + mOwnsBlobImpl = false; +#endif +} + +PBlobStreamParent* +BlobParent::AllocPBlobStreamParent(const uint64_t& aStart, + const uint64_t& aLength) +{ + AssertIsOnOwningThread(); + + if (NS_WARN_IF(mRemoteBlobImpl)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + return new InputStreamParent(); +} + +bool +BlobParent::RecvPBlobStreamConstructor(PBlobStreamParent* aActor, + const uint64_t& aStart, + const uint64_t& aLength) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(mBlobImpl); + MOZ_ASSERT(!mRemoteBlobImpl); + MOZ_ASSERT(mOwnsBlobImpl); + + auto* actor = static_cast<InputStreamParent*>(aActor); + + // Make sure we can't overflow. + if (NS_WARN_IF(UINT64_MAX - aLength < aStart)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + ErrorResult errorResult; + uint64_t blobLength = mBlobImpl->GetSize(errorResult); + MOZ_ASSERT(!errorResult.Failed()); + + if (NS_WARN_IF(aStart + aLength > blobLength)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + RefPtr<BlobImpl> blobImpl; + + if (!aStart && aLength == blobLength) { + blobImpl = mBlobImpl; + } else { + nsString type; + mBlobImpl->GetType(type); + + blobImpl = mBlobImpl->CreateSlice(aStart, aLength, type, errorResult); + if (NS_WARN_IF(errorResult.Failed())) { + return false; + } + } + + nsCOMPtr<nsIInputStream> stream; + blobImpl->GetInternalStream(getter_AddRefs(stream), errorResult); + if (NS_WARN_IF(errorResult.Failed())) { + return false; + } + + // If the stream is entirely backed by memory then we can serialize and send + // it immediately. + if (mBlobImpl->IsMemoryFile()) { + InputStreamParams params; + nsTArray<FileDescriptor> fds; + SerializeInputStream(stream, params, fds); + + MOZ_ASSERT(params.type() != InputStreamParams::T__None); + MOZ_ASSERT(fds.IsEmpty()); + + return actor->Destroy(params, void_t()); + } + + nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(mBlobImpl); + nsCOMPtr<IPrivateRemoteInputStream> remoteStream; + if (remoteBlob) { + remoteStream = do_QueryInterface(stream); + } + + // There are three cases in which we can use the stream obtained from the blob + // directly as our serialized stream: + // + // 1. The blob is not a remote blob. + // 2. The blob is a remote blob that represents this actor. + // 3. The blob is a remote blob representing a different actor but we + // already have a non-remote, i.e. serialized, serialized stream. + // + // In all other cases we need to be on a background thread before we can get + // to the real stream. + nsCOMPtr<nsIIPCSerializableInputStream> serializableStream; + if (!remoteBlob || + remoteBlob->GetBlobParent() == this || + !remoteStream) { + serializableStream = do_QueryInterface(stream); + if (!serializableStream) { + MOZ_ASSERT(false, "Must be serializable!"); + return false; + } + } + + nsCOMPtr<nsIThread> target; + errorResult = NS_NewNamedThread("Blob Opener", getter_AddRefs(target)); + if (NS_WARN_IF(errorResult.Failed())) { + return false; + } + + RefPtr<OpenStreamRunnable> runnable = + new OpenStreamRunnable(this, actor, stream, serializableStream, target); + + errorResult = runnable->Dispatch(); + if (NS_WARN_IF(errorResult.Failed())) { + return false; + } + + // nsRevocableEventPtr lacks some of the operators needed for anything nicer. + *mOpenStreamRunnables.AppendElement() = runnable; + return true; +} + +bool +BlobParent::DeallocPBlobStreamParent(PBlobStreamParent* aActor) +{ + AssertIsOnOwningThread(); + + delete static_cast<InputStreamParent*>(aActor); + return true; +} + +bool +BlobParent::RecvResolveMystery(const ResolveMysteryParams& aParams) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aParams.type() != ResolveMysteryParams::T__None); + MOZ_ASSERT(mBlobImpl); + MOZ_ASSERT(!mRemoteBlobImpl); + MOZ_ASSERT(mOwnsBlobImpl); + + switch (aParams.type()) { + case ResolveMysteryParams::TNormalBlobConstructorParams: { + const NormalBlobConstructorParams& params = + aParams.get_NormalBlobConstructorParams(); + + if (NS_WARN_IF(params.length() == UINT64_MAX)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + mBlobImpl->SetLazyData(NullString(), + params.contentType(), + params.length(), + INT64_MAX); + return true; + } + + case ResolveMysteryParams::TFileBlobConstructorParams: { + const FileBlobConstructorParams& params = + aParams.get_FileBlobConstructorParams(); + if (NS_WARN_IF(params.name().IsVoid())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(params.length() == UINT64_MAX)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(params.modDate() == INT64_MAX)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + mBlobImpl->SetLazyData(params.name(), + params.contentType(), + params.length(), + params.modDate()); + return true; + } + + default: + MOZ_CRASH("Unknown params!"); + } + + MOZ_CRASH("Should never get here!"); +} + +bool +BlobParent::RecvBlobStreamSync(const uint64_t& aStart, + const uint64_t& aLength, + InputStreamParams* aParams, + OptionalFileDescriptorSet* aFDs) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mBlobImpl); + MOZ_ASSERT(!mRemoteBlobImpl); + MOZ_ASSERT(mOwnsBlobImpl); + + bool finished = false; + + { + // Calling RecvPBlobStreamConstructor() may synchronously delete the actor + // we pass in so don't touch it outside this block. + auto* streamActor = new InputStreamParent(&finished, aParams, aFDs); + + if (NS_WARN_IF(!RecvPBlobStreamConstructor(streamActor, aStart, aLength))) { + // If RecvPBlobStreamConstructor() returns false then it is our + // responsibility to destroy the actor. + delete streamActor; + return false; + } + } + + if (finished) { + // The actor is already dead and we have already set our out params. + return true; + } + + // The actor is alive and will be doing asynchronous work to load the stream. + // Spin a nested loop here while we wait for it. + nsIThread* currentThread = NS_GetCurrentThread(); + MOZ_ASSERT(currentThread); + + while (!finished) { + MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread)); + } + + return true; +} + +bool +BlobParent::RecvWaitForSliceCreation() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mBlobImpl); + MOZ_ASSERT(mOwnsBlobImpl); + + // The whole point of this message is to ensure that the sliced blob created + // by the child has been inserted into our IDTable. + MOZ_ASSERT(mIDTableEntry); + +#ifdef DEBUG + { + MOZ_ASSERT(sIDTableMutex); + MutexAutoLock lock(*sIDTableMutex); + + MOZ_ASSERT(sIDTable); + MOZ_ASSERT(sIDTable->Contains(mIDTableEntry->ID())); + } +#endif + + return true; +} + +bool +BlobParent::RecvGetFileId(int64_t* aFileId) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mBlobImpl); + MOZ_ASSERT(!mRemoteBlobImpl); + MOZ_ASSERT(mOwnsBlobImpl); + + if (NS_WARN_IF(!IndexedDatabaseManager::InTestingMode())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + *aFileId = mBlobImpl->GetFileId(); + return true; +} + +bool +BlobParent::RecvGetFilePath(nsString* aFilePath) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mBlobImpl); + MOZ_ASSERT(!mRemoteBlobImpl); + MOZ_ASSERT(mOwnsBlobImpl); + + // In desktop e10s the file picker code sends this message. + + nsString filePath; + ErrorResult rv; + mBlobImpl->GetMozFullPathInternal(filePath, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return false; + } + + *aFilePath = filePath; + return true; +} + +/******************************************************************************* + * BlobParent::IDTableEntry + ******************************************************************************/ + +BlobParent:: +IDTableEntry::IDTableEntry(const nsID& aID, + intptr_t aProcessID, + BlobImpl* aBlobImpl) + : mID(aID) + , mProcessID(aProcessID) + , mBlobImpl(aBlobImpl) +{ + MOZ_ASSERT(aBlobImpl); +} + +BlobParent:: +IDTableEntry::~IDTableEntry() +{ + MOZ_ASSERT(sIDTableMutex); + sIDTableMutex->AssertNotCurrentThreadOwns(); + MOZ_ASSERT(sIDTable); + + { + MutexAutoLock lock(*sIDTableMutex); + MOZ_ASSERT(sIDTable->Get(mID) == this); + + sIDTable->Remove(mID); + + if (!sIDTable->Count()) { + sIDTable = nullptr; + } + } +} + +// static +already_AddRefed<BlobParent::IDTableEntry> +BlobParent:: +IDTableEntry::GetOrCreateInternal(const nsID& aID, + intptr_t aProcessID, + BlobImpl* aBlobImpl, + bool aMayCreate, + bool aMayGet, + bool aIgnoreProcessID) +{ + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + MOZ_ASSERT(sIDTableMutex); + sIDTableMutex->AssertNotCurrentThreadOwns(); + + RefPtr<IDTableEntry> entry; + + { + MutexAutoLock lock(*sIDTableMutex); + + if (!sIDTable) { + if (NS_WARN_IF(!aMayCreate)) { + return nullptr; + } + + sIDTable = new IDTable(); + } + + entry = sIDTable->Get(aID); + + if (entry) { + MOZ_ASSERT_IF(aBlobImpl, entry->GetBlobImpl() == aBlobImpl); + + if (NS_WARN_IF(!aMayGet)) { + return nullptr; + } + + if (!aIgnoreProcessID && NS_WARN_IF(entry->mProcessID != aProcessID)) { + return nullptr; + } + } else { + if (NS_WARN_IF(!aMayCreate)) { + return nullptr; + } + + MOZ_ASSERT(aBlobImpl); + + entry = new IDTableEntry(aID, aProcessID, aBlobImpl); + + sIDTable->Put(aID, entry); + } + } + + MOZ_ASSERT(entry); + + return entry.forget(); +} + +/******************************************************************************* + * Other stuff + ******************************************************************************/ + +bool +InputStreamChild::Recv__delete__(const InputStreamParams& aParams, + const OptionalFileDescriptorSet& aOptionalSet) +{ + MOZ_ASSERT(mRemoteStream); + mRemoteStream->AssertIsOnOwningThread(); + + nsTArray<FileDescriptor> fds; + OptionalFileDescriptorSetToFDs( + // XXX Fix this somehow... + const_cast<OptionalFileDescriptorSet&>(aOptionalSet), + fds); + + nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds); + MOZ_ASSERT(stream); + + mRemoteStream->SetStream(stream); + return true; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/BlobChild.h b/dom/ipc/BlobChild.h new file mode 100644 index 000000000..affa9934a --- /dev/null +++ b/dom/ipc/BlobChild.h @@ -0,0 +1,246 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_ipc_BlobChild_h +#define mozilla_dom_ipc_BlobChild_h + +#include "mozilla/Attributes.h" +#include "mozilla/dom/PBlobChild.h" +#include "nsCOMPtr.h" +#include "nsID.h" + +class nsIEventTarget; +class nsIRemoteBlob; +class nsString; + +namespace mozilla { +namespace ipc { + +class PBackgroundChild; + +} // namespace ipc + +namespace dom { + +class Blob; +class BlobImpl; +class ContentChild; +class nsIContentChild; +class PBlobStreamChild; + +class BlobChild final + : public PBlobChild +{ + typedef mozilla::ipc::PBackgroundChild PBackgroundChild; + + class RemoteBlobImpl; + friend class RemoteBlobImpl; + + class RemoteBlobSliceImpl; + friend class RemoteBlobSliceImpl; + + BlobImpl* mBlobImpl; + RemoteBlobImpl* mRemoteBlobImpl; + + // One of these will be null and the other non-null. + PBackgroundChild* mBackgroundManager; + nsCOMPtr<nsIContentChild> mContentManager; + + nsCOMPtr<nsIEventTarget> mEventTarget; + + nsID mParentID; + + bool mOwnsBlobImpl; + +public: + class FriendKey; + + static void + Startup(const FriendKey& aKey); + + // These create functions are called on the sending side. + static BlobChild* + GetOrCreate(nsIContentChild* aManager, BlobImpl* aBlobImpl); + + static BlobChild* + GetOrCreate(PBackgroundChild* aManager, BlobImpl* aBlobImpl); + + // These create functions are called on the receiving side. + static BlobChild* + Create(nsIContentChild* aManager, const ChildBlobConstructorParams& aParams); + + static BlobChild* + Create(PBackgroundChild* aManager, + const ChildBlobConstructorParams& aParams); + + static void + Destroy(PBlobChild* aActor) + { + delete static_cast<BlobChild*>(aActor); + } + + bool + HasManager() const + { + return mBackgroundManager || mContentManager; + } + + PBackgroundChild* + GetBackgroundManager() const + { + return mBackgroundManager; + } + + nsIContentChild* + GetContentManager() const + { + return mContentManager; + } + + const nsID& + ParentID() const; + + // Get the BlobImpl associated with this actor. This may always be called + // on the sending side. It may also be called on the receiving side unless + // this is a "mystery" blob that has not yet received a SetMysteryBlobInfo() + // call. + already_AddRefed<BlobImpl> + GetBlobImpl(); + + // Use this for files. + bool + SetMysteryBlobInfo(const nsString& aName, + const nsString& aContentType, + uint64_t aLength, + int64_t aLastModifiedDate); + + // Use this for non-file blobs. + bool + SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength); + + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + +private: + // These constructors are called on the sending side. + BlobChild(nsIContentChild* aManager, BlobImpl* aBlobImpl); + + BlobChild(PBackgroundChild* aManager, BlobImpl* aBlobImpl); + + BlobChild(nsIContentChild* aManager, BlobChild* aOther); + + BlobChild(PBackgroundChild* aManager, BlobChild* aOther, BlobImpl* aBlobImpl); + + // These constructors are called on the receiving side. + BlobChild(nsIContentChild* aManager, + const ChildBlobConstructorParams& aParams); + + BlobChild(PBackgroundChild* aManager, + const ChildBlobConstructorParams& aParams); + + // These constructors are called for slices. + BlobChild(nsIContentChild* aManager, + const nsID& aParentID, + RemoteBlobSliceImpl* aRemoteBlobSliceImpl); + + BlobChild(PBackgroundChild* aManager, + const nsID& aParentID, + RemoteBlobSliceImpl* aRemoteBlobSliceImpl); + + // Only called by Destroy(). + ~BlobChild(); + + void + CommonInit(BlobImpl* aBlobImpl); + + void + CommonInit(BlobChild* aOther, BlobImpl* aBlobImpl); + + void + CommonInit(const ChildBlobConstructorParams& aParams); + + void + CommonInit(const nsID& aParentID, RemoteBlobImpl* aRemoteBlobImpl); + + template <class ChildManagerType> + static BlobChild* + GetOrCreateFromImpl(ChildManagerType* aManager, BlobImpl* aBlobImpl); + + template <class ChildManagerType> + static BlobChild* + CreateFromParams(ChildManagerType* aManager, + const ChildBlobConstructorParams& aParams); + + template <class ChildManagerType> + static BlobChild* + SendSliceConstructor(ChildManagerType* aManager, + RemoteBlobSliceImpl* aRemoteBlobSliceImpl, + const ParentBlobConstructorParams& aParams); + + static BlobChild* + MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob, + nsIContentChild* aManager, + BlobImpl* aBlobImpl); + + static BlobChild* + MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob, + PBackgroundChild* aManager, + BlobImpl* aBlobImpl); + + void + NoteDyingRemoteBlobImpl(); + + nsIEventTarget* + EventTarget() const + { + return mEventTarget; + } + + bool + IsOnOwningThread() const; + + // These methods are only called by the IPDL message machinery. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + virtual PBlobStreamChild* + AllocPBlobStreamChild(const uint64_t& aStart, + const uint64_t& aLength) override; + + virtual bool + DeallocPBlobStreamChild(PBlobStreamChild* aActor) override; + + virtual bool + RecvCreatedFromKnownBlob() override; +}; + +// Only let ContentChild call BlobChild::Startup() and ensure that +// ContentChild can't access any other BlobChild internals. +class BlobChild::FriendKey final +{ + friend class ContentChild; + +private: + FriendKey() + { } + + FriendKey(const FriendKey& /* aOther */) + { } + +public: + ~FriendKey() + { } +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_ipc_BlobChild_h diff --git a/dom/ipc/BlobParent.h b/dom/ipc/BlobParent.h new file mode 100644 index 000000000..98cfe6d28 --- /dev/null +++ b/dom/ipc/BlobParent.h @@ -0,0 +1,260 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_ipc_BlobParent_h +#define mozilla_dom_ipc_BlobParent_h + +#include "mozilla/Attributes.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/dom/PBlobParent.h" +#include "nsCOMPtr.h" +#include "nsTArray.h" + +template <class, class> class nsDataHashtable; +class nsIDHashKey; +class nsIEventTarget; +class nsIRemoteBlob; +template <class> class nsRevocableEventPtr; +class nsString; + +namespace mozilla { + +class Mutex; + +namespace ipc { + +class PBackgroundParent; + +} // namespace ipc + +namespace dom { + +class ContentParent; +class BlobImpl; +class nsIContentParent; +class PBlobStreamParent; + +class BlobParent final + : public PBlobParent +{ + typedef mozilla::ipc::PBackgroundParent PBackgroundParent; + + class IDTableEntry; + typedef nsDataHashtable<nsIDHashKey, IDTableEntry*> IDTable; + + class OpenStreamRunnable; + friend class OpenStreamRunnable; + + class RemoteBlobImpl; + + struct CreateBlobImplMetadata; + + static StaticAutoPtr<IDTable> sIDTable; + static StaticAutoPtr<Mutex> sIDTableMutex; + + BlobImpl* mBlobImpl; + RemoteBlobImpl* mRemoteBlobImpl; + + // One of these will be null and the other non-null. + PBackgroundParent* mBackgroundManager; + nsCOMPtr<nsIContentParent> mContentManager; + + nsCOMPtr<nsIEventTarget> mEventTarget; + + // nsIInputStreams backed by files must ensure that the files are actually + // opened and closed on a background thread before we can send their file + // handles across to the child. The child process could crash during this + // process so we need to make sure we cancel the intended response in such a + // case. We do that by holding an array of nsRevocableEventPtr. If the child + // crashes then this actor will be destroyed and the nsRevocableEventPtr + // destructor will cancel any stream events that are currently in flight. + nsTArray<nsRevocableEventPtr<OpenStreamRunnable>> mOpenStreamRunnables; + + RefPtr<IDTableEntry> mIDTableEntry; + + bool mOwnsBlobImpl; + +public: + class FriendKey; + + static void + Startup(const FriendKey& aKey); + + // These create functions are called on the sending side. + static BlobParent* + GetOrCreate(nsIContentParent* aManager, BlobImpl* aBlobImpl); + + static BlobParent* + GetOrCreate(PBackgroundParent* aManager, BlobImpl* aBlobImpl); + + // These create functions are called on the receiving side. + static BlobParent* + Create(nsIContentParent* aManager, + const ParentBlobConstructorParams& aParams); + + static BlobParent* + Create(PBackgroundParent* aManager, + const ParentBlobConstructorParams& aParams); + + static void + Destroy(PBlobParent* aActor) + { + delete static_cast<BlobParent*>(aActor); + } + + static already_AddRefed<BlobImpl> + GetBlobImplForID(const nsID& aID); + + bool + HasManager() const + { + return mBackgroundManager || mContentManager; + } + + PBackgroundParent* + GetBackgroundManager() const + { + return mBackgroundManager; + } + + nsIContentParent* + GetContentManager() const + { + return mContentManager; + } + + // Get the BlobImpl associated with this actor. + already_AddRefed<BlobImpl> + GetBlobImpl(); + + void + AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { } +#endif + +private: + // These constructors are called on the sending side. + BlobParent(nsIContentParent* aManager, IDTableEntry* aIDTableEntry); + + BlobParent(PBackgroundParent* aManager, IDTableEntry* aIDTableEntry); + + // These constructors are called on the receiving side. + BlobParent(nsIContentParent* aManager, + BlobImpl* aBlobImpl, + IDTableEntry* aIDTableEntry); + + BlobParent(PBackgroundParent* aManager, + BlobImpl* aBlobImpl, + IDTableEntry* aIDTableEntry); + + // Only destroyed by BackgroundParentImpl and ContentParent. + ~BlobParent(); + + void + CommonInit(IDTableEntry* aIDTableEntry); + + void + CommonInit(BlobImpl* aBlobImpl, IDTableEntry* aIDTableEntry); + + template <class ParentManagerType> + static BlobParent* + GetOrCreateFromImpl(ParentManagerType* aManager, + BlobImpl* aBlobImpl); + + template <class ParentManagerType> + static BlobParent* + CreateFromParams(ParentManagerType* aManager, + const ParentBlobConstructorParams& aParams); + + template <class ParentManagerType> + static BlobParent* + SendSliceConstructor(ParentManagerType* aManager, + const ParentBlobConstructorParams& aParams, + const ChildBlobConstructorParams& aOtherSideParams); + + static BlobParent* + MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob, + nsIContentParent* aManager); + + static BlobParent* + MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob, + PBackgroundParent* aManager); + + void + NoteDyingRemoteBlobImpl(); + + void + NoteRunnableCompleted(OpenStreamRunnable* aRunnable); + + nsIEventTarget* + EventTarget() const + { + return mEventTarget; + } + + bool + IsOnOwningThread() const; + + // These methods are only called by the IPDL message machinery. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + virtual PBlobStreamParent* + AllocPBlobStreamParent(const uint64_t& aStart, + const uint64_t& aLength) override; + + virtual bool + RecvPBlobStreamConstructor(PBlobStreamParent* aActor, + const uint64_t& aStart, + const uint64_t& aLength) override; + + virtual bool + DeallocPBlobStreamParent(PBlobStreamParent* aActor) override; + + virtual bool + RecvResolveMystery(const ResolveMysteryParams& aParams) override; + + virtual bool + RecvBlobStreamSync(const uint64_t& aStart, + const uint64_t& aLength, + InputStreamParams* aParams, + OptionalFileDescriptorSet* aFDs) override; + + virtual bool + RecvWaitForSliceCreation() override; + + virtual bool + RecvGetFileId(int64_t* aFileId) override; + + virtual bool + RecvGetFilePath(nsString* aFilePath) override; +}; + +// Only let ContentParent call BlobParent::Startup() and ensure that +// ContentParent can't access any other BlobParent internals. +class BlobParent::FriendKey final +{ + friend class ContentParent; + +private: + FriendKey() + { } + + FriendKey(const FriendKey& /* aOther */) + { } + +public: + ~FriendKey() + { } +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_ipc_BlobParent_h diff --git a/dom/ipc/BlobTypes.ipdlh b/dom/ipc/BlobTypes.ipdlh new file mode 100644 index 000000000..b0bd7f951 --- /dev/null +++ b/dom/ipc/BlobTypes.ipdlh @@ -0,0 +1,21 @@ +/* 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 protocol PFileDescriptorSet; + +using struct mozilla::void_t + from "ipc/IPCMessageUtils.h"; + +namespace mozilla { +namespace dom { + +union OptionalFileDescriptorSet +{ + PFileDescriptorSet; + FileDescriptor[]; + void_t; +}; + +} +} diff --git a/dom/ipc/CPOWManagerGetter.h b/dom/ipc/CPOWManagerGetter.h new file mode 100644 index 000000000..541bff665 --- /dev/null +++ b/dom/ipc/CPOWManagerGetter.h @@ -0,0 +1,27 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_CPOWManagerGetter_h +#define mozilla_dom_CPOWManagerGetter_h + +namespace mozilla { + +namespace jsipc { +class CPOWManager; +} /* namespace jsipc */ + +namespace dom { + +class CPOWManagerGetter +{ +public: + virtual mozilla::jsipc::CPOWManager* GetCPOWManager() = 0; +}; + +} /* namespace dom */ +} /* namespace mozilla */ + +#endif /* mozilla_dom_CPOWManagerGetter_h */ diff --git a/dom/ipc/ColorPickerParent.cpp b/dom/ipc/ColorPickerParent.cpp new file mode 100644 index 000000000..6d6f1dfe5 --- /dev/null +++ b/dom/ipc/ColorPickerParent.cpp @@ -0,0 +1,87 @@ +/* -*- 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 "ColorPickerParent.h" +#include "nsComponentManagerUtils.h" +#include "nsIDocument.h" +#include "nsIDOMWindow.h" +#include "mozilla/Unused.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/TabParent.h" + +using mozilla::Unused; +using namespace mozilla::dom; + +NS_IMPL_ISUPPORTS(ColorPickerParent::ColorPickerShownCallback, + nsIColorPickerShownCallback); + +NS_IMETHODIMP +ColorPickerParent::ColorPickerShownCallback::Update(const nsAString& aColor) +{ + if (mColorPickerParent) { + Unused << mColorPickerParent->SendUpdate(nsString(aColor)); + } + return NS_OK; +} + +NS_IMETHODIMP +ColorPickerParent::ColorPickerShownCallback::Done(const nsAString& aColor) +{ + if (mColorPickerParent) { + Unused << mColorPickerParent->Send__delete__(mColorPickerParent, + nsString(aColor)); + } + return NS_OK; +} + +void +ColorPickerParent::ColorPickerShownCallback::Destroy() +{ + mColorPickerParent = nullptr; +} + +bool +ColorPickerParent::CreateColorPicker() +{ + mPicker = do_CreateInstance("@mozilla.org/colorpicker;1"); + if (!mPicker) { + return false; + } + + Element* ownerElement = TabParent::GetFrom(Manager())->GetOwnerElement(); + if (!ownerElement) { + return false; + } + + nsCOMPtr<nsPIDOMWindowOuter> window = ownerElement->OwnerDoc()->GetWindow(); + if (!window) { + return false; + } + + return NS_SUCCEEDED(mPicker->Init(window, mTitle, mInitialColor)); +} + +bool +ColorPickerParent::RecvOpen() +{ + if (!CreateColorPicker()) { + Unused << Send__delete__(this, mInitialColor); + return true; + } + + mCallback = new ColorPickerShownCallback(this); + + mPicker->Open(mCallback); + return true; +}; + +void +ColorPickerParent::ActorDestroy(ActorDestroyReason aWhy) +{ + if (mCallback) { + mCallback->Destroy(); + } +} diff --git a/dom/ipc/ColorPickerParent.h b/dom/ipc/ColorPickerParent.h new file mode 100644 index 000000000..bfdc82bdb --- /dev/null +++ b/dom/ipc/ColorPickerParent.h @@ -0,0 +1,61 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_ColorPickerParent_h +#define mozilla_dom_ColorPickerParent_h + +#include "mozilla/dom/PColorPickerParent.h" +#include "nsIColorPicker.h" + +namespace mozilla { +namespace dom { + +class ColorPickerParent : public PColorPickerParent +{ + public: + ColorPickerParent(const nsString& aTitle, + const nsString& aInitialColor) + : mTitle(aTitle) + , mInitialColor(aInitialColor) + {} + + virtual bool RecvOpen() override; + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + class ColorPickerShownCallback final + : public nsIColorPickerShownCallback + { + public: + explicit ColorPickerShownCallback(ColorPickerParent* aColorPickerParnet) + : mColorPickerParent(aColorPickerParnet) + {} + + NS_DECL_ISUPPORTS + NS_DECL_NSICOLORPICKERSHOWNCALLBACK + + void Destroy(); + + private: + ~ColorPickerShownCallback() {} + ColorPickerParent* mColorPickerParent; + }; + + private: + virtual ~ColorPickerParent() {} + + bool CreateColorPicker(); + + RefPtr<ColorPickerShownCallback> mCallback; + nsCOMPtr<nsIColorPicker> mPicker; + + nsString mTitle; + nsString mInitialColor; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_ColorPickerParent_h diff --git a/dom/ipc/ContentBridgeChild.cpp b/dom/ipc/ContentBridgeChild.cpp new file mode 100644 index 000000000..ae07528a2 --- /dev/null +++ b/dom/ipc/ContentBridgeChild.cpp @@ -0,0 +1,206 @@ +/* -*- 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/dom/ContentBridgeChild.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/TabChild.h" +#include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" +#include "mozilla/ipc/InputStreamUtils.h" +#include "base/task.h" + +using namespace mozilla::ipc; +using namespace mozilla::jsipc; + +namespace mozilla { +namespace dom { + +NS_IMPL_ISUPPORTS(ContentBridgeChild, + nsIContentChild) + +ContentBridgeChild::ContentBridgeChild(Transport* aTransport) + : mTransport(aTransport) +{} + +ContentBridgeChild::~ContentBridgeChild() +{ +} + +void +ContentBridgeChild::ActorDestroy(ActorDestroyReason aWhy) +{ + MessageLoop::current()->PostTask(NewRunnableMethod(this, &ContentBridgeChild::DeferredDestroy)); +} + +/*static*/ ContentBridgeChild* +ContentBridgeChild::Create(Transport* aTransport, ProcessId aOtherPid) +{ + RefPtr<ContentBridgeChild> bridge = + new ContentBridgeChild(aTransport); + bridge->mSelfRef = bridge; + + DebugOnly<bool> ok = bridge->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop()); + MOZ_ASSERT(ok); + + return bridge; +} + +void +ContentBridgeChild::DeferredDestroy() +{ + mSelfRef = nullptr; + // |this| was just destroyed, hands off +} + +bool +ContentBridgeChild::RecvAsyncMessage(const nsString& aMsg, + InfallibleTArray<jsipc::CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData) +{ + return nsIContentChild::RecvAsyncMessage(aMsg, Move(aCpows), aPrincipal, aData); +} + +PBlobChild* +ContentBridgeChild::SendPBlobConstructor(PBlobChild* actor, + const BlobConstructorParams& params) +{ + return PContentBridgeChild::SendPBlobConstructor(actor, params); +} + +bool +ContentBridgeChild::SendPBrowserConstructor(PBrowserChild* aActor, + const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) +{ + return PContentBridgeChild::SendPBrowserConstructor(aActor, + aTabId, + aContext, + aChromeFlags, + aCpID, + aIsForApp, + aIsForBrowser); +} + +PFileDescriptorSetChild* +ContentBridgeChild::SendPFileDescriptorSetConstructor(const FileDescriptor& aFD) +{ + return PContentBridgeChild::SendPFileDescriptorSetConstructor(aFD); +} + +PSendStreamChild* +ContentBridgeChild::SendPSendStreamConstructor(PSendStreamChild* aActor) +{ + return PContentBridgeChild::SendPSendStreamConstructor(aActor); +} + +// This implementation is identical to ContentChild::GetCPOWManager but we can't +// move it to nsIContentChild because it calls ManagedPJavaScriptChild() which +// only exists in PContentChild and PContentBridgeChild. +jsipc::CPOWManager* +ContentBridgeChild::GetCPOWManager() +{ + if (PJavaScriptChild* c = LoneManagedOrNullAsserts(ManagedPJavaScriptChild())) { + return CPOWManagerFor(c); + } + return CPOWManagerFor(SendPJavaScriptConstructor()); +} + +mozilla::jsipc::PJavaScriptChild * +ContentBridgeChild::AllocPJavaScriptChild() +{ + return nsIContentChild::AllocPJavaScriptChild(); +} + +bool +ContentBridgeChild::DeallocPJavaScriptChild(PJavaScriptChild *child) +{ + return nsIContentChild::DeallocPJavaScriptChild(child); +} + +PBrowserChild* +ContentBridgeChild::AllocPBrowserChild(const TabId& aTabId, + const IPCTabContext &aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) +{ + return nsIContentChild::AllocPBrowserChild(aTabId, + aContext, + aChromeFlags, + aCpID, + aIsForApp, + aIsForBrowser); +} + +bool +ContentBridgeChild::DeallocPBrowserChild(PBrowserChild* aChild) +{ + return nsIContentChild::DeallocPBrowserChild(aChild); +} + +bool +ContentBridgeChild::RecvPBrowserConstructor(PBrowserChild* aActor, + const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) +{ + return ContentChild::GetSingleton()->RecvPBrowserConstructor(aActor, + aTabId, + aContext, + aChromeFlags, + aCpID, + aIsForApp, + aIsForBrowser); +} + +PBlobChild* +ContentBridgeChild::AllocPBlobChild(const BlobConstructorParams& aParams) +{ + return nsIContentChild::AllocPBlobChild(aParams); +} + +bool +ContentBridgeChild::DeallocPBlobChild(PBlobChild* aActor) +{ + return nsIContentChild::DeallocPBlobChild(aActor); +} + +PSendStreamChild* +ContentBridgeChild::AllocPSendStreamChild() +{ + return nsIContentChild::AllocPSendStreamChild(); +} + +bool +ContentBridgeChild::DeallocPSendStreamChild(PSendStreamChild* aActor) +{ + return nsIContentChild::DeallocPSendStreamChild(aActor); +} + +PFileDescriptorSetChild* +ContentBridgeChild::AllocPFileDescriptorSetChild(const FileDescriptor& aFD) +{ + return nsIContentChild::AllocPFileDescriptorSetChild(aFD); +} + +bool +ContentBridgeChild::DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor) +{ + return nsIContentChild::DeallocPFileDescriptorSetChild(aActor); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/ContentBridgeChild.h b/dom/ipc/ContentBridgeChild.h new file mode 100644 index 000000000..179328929 --- /dev/null +++ b/dom/ipc/ContentBridgeChild.h @@ -0,0 +1,102 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_ContentBridgeChild_h +#define mozilla_dom_ContentBridgeChild_h + +#include "mozilla/dom/PContentBridgeChild.h" +#include "mozilla/dom/nsIContentChild.h" + +namespace mozilla { +namespace dom { + +class ContentBridgeChild final : public PContentBridgeChild + , public nsIContentChild +{ +public: + explicit ContentBridgeChild(Transport* aTransport); + + NS_DECL_ISUPPORTS + + static ContentBridgeChild* + Create(Transport* aTransport, ProcessId aOtherProcess); + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + void DeferredDestroy(); + + virtual bool RecvAsyncMessage(const nsString& aMsg, + InfallibleTArray<jsipc::CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData) override; + + virtual PBlobChild* + SendPBlobConstructor(PBlobChild* actor, + const BlobConstructorParams& aParams) override; + + jsipc::CPOWManager* GetCPOWManager() override; + + virtual bool SendPBrowserConstructor(PBrowserChild* aActor, + const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) override; + + virtual mozilla::ipc::PFileDescriptorSetChild* + SendPFileDescriptorSetConstructor(const mozilla::ipc::FileDescriptor&) override; + + virtual mozilla::ipc::PSendStreamChild* + SendPSendStreamConstructor(mozilla::ipc::PSendStreamChild*) override; + + FORWARD_SHMEM_ALLOCATOR_TO(PContentBridgeChild) + +protected: + virtual ~ContentBridgeChild(); + + virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) override; + virtual bool DeallocPBrowserChild(PBrowserChild*) override; + virtual bool RecvPBrowserConstructor(PBrowserChild* aCctor, + const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) override; + + virtual mozilla::jsipc::PJavaScriptChild* AllocPJavaScriptChild() override; + virtual bool DeallocPJavaScriptChild(mozilla::jsipc::PJavaScriptChild*) override; + + virtual PBlobChild* AllocPBlobChild(const BlobConstructorParams& aParams) override; + virtual bool DeallocPBlobChild(PBlobChild*) override; + + virtual mozilla::ipc::PSendStreamChild* AllocPSendStreamChild() override; + + virtual bool + DeallocPSendStreamChild(mozilla::ipc::PSendStreamChild* aActor) override; + + virtual PFileDescriptorSetChild* + AllocPFileDescriptorSetChild(const mozilla::ipc::FileDescriptor& aFD) override; + + virtual bool + DeallocPFileDescriptorSetChild(mozilla::ipc::PFileDescriptorSetChild* aActor) override; + + DISALLOW_EVIL_CONSTRUCTORS(ContentBridgeChild); + +protected: // members + RefPtr<ContentBridgeChild> mSelfRef; + Transport* mTransport; // owned +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_ContentBridgeChild_h diff --git a/dom/ipc/ContentBridgeParent.cpp b/dom/ipc/ContentBridgeParent.cpp new file mode 100644 index 000000000..eee0e1c10 --- /dev/null +++ b/dom/ipc/ContentBridgeParent.cpp @@ -0,0 +1,221 @@ +/* -*- 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/dom/ContentBridgeParent.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" +#include "nsXULAppAPI.h" +#include "nsIObserverService.h" +#include "base/task.h" + +using namespace mozilla::ipc; +using namespace mozilla::jsipc; + +namespace mozilla { +namespace dom { + +NS_IMPL_ISUPPORTS(ContentBridgeParent, + nsIContentParent, + nsIObserver) + +ContentBridgeParent::ContentBridgeParent(Transport* aTransport) + : mTransport(aTransport) +{} + +ContentBridgeParent::~ContentBridgeParent() +{ +} + +void +ContentBridgeParent::ActorDestroy(ActorDestroyReason aWhy) +{ + nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); + if (os) { + os->RemoveObserver(this, "content-child-shutdown"); + } + MessageLoop::current()->PostTask(NewRunnableMethod(this, &ContentBridgeParent::DeferredDestroy)); +} + +/*static*/ ContentBridgeParent* +ContentBridgeParent::Create(Transport* aTransport, ProcessId aOtherPid) +{ + RefPtr<ContentBridgeParent> bridge = + new ContentBridgeParent(aTransport); + bridge->mSelfRef = bridge; + + DebugOnly<bool> ok = bridge->Open(aTransport, aOtherPid, + XRE_GetIOMessageLoop()); + MOZ_ASSERT(ok); + + nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); + if (os) { + os->AddObserver(bridge, "content-child-shutdown", false); + } + + // Initialize the message manager (and load delayed scripts) now that we + // have established communications with the child. + bridge->mMessageManager->InitWithCallback(bridge); + + return bridge.get(); +} + +void +ContentBridgeParent::DeferredDestroy() +{ + mSelfRef = nullptr; + // |this| was just destroyed, hands off +} + +bool +ContentBridgeParent::RecvSyncMessage(const nsString& aMsg, + const ClonedMessageData& aData, + InfallibleTArray<jsipc::CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + nsTArray<StructuredCloneData>* aRetvals) +{ + return nsIContentParent::RecvSyncMessage(aMsg, aData, Move(aCpows), + aPrincipal, aRetvals); +} + +bool +ContentBridgeParent::RecvAsyncMessage(const nsString& aMsg, + InfallibleTArray<jsipc::CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData) +{ + return nsIContentParent::RecvAsyncMessage(aMsg, Move(aCpows), + aPrincipal, aData); +} + +PBlobParent* +ContentBridgeParent::SendPBlobConstructor(PBlobParent* actor, + const BlobConstructorParams& params) +{ + return PContentBridgeParent::SendPBlobConstructor(actor, params); +} + +PBrowserParent* +ContentBridgeParent::SendPBrowserConstructor(PBrowserParent* aActor, + const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) +{ + return PContentBridgeParent::SendPBrowserConstructor(aActor, + aTabId, + aContext, + aChromeFlags, + aCpID, + aIsForApp, + aIsForBrowser); +} + +PBlobParent* +ContentBridgeParent::AllocPBlobParent(const BlobConstructorParams& aParams) +{ + return nsIContentParent::AllocPBlobParent(aParams); +} + +bool +ContentBridgeParent::DeallocPBlobParent(PBlobParent* aActor) +{ + return nsIContentParent::DeallocPBlobParent(aActor); +} + +mozilla::jsipc::PJavaScriptParent * +ContentBridgeParent::AllocPJavaScriptParent() +{ + return nsIContentParent::AllocPJavaScriptParent(); +} + +bool +ContentBridgeParent::DeallocPJavaScriptParent(PJavaScriptParent *parent) +{ + return nsIContentParent::DeallocPJavaScriptParent(parent); +} + +PBrowserParent* +ContentBridgeParent::AllocPBrowserParent(const TabId& aTabId, + const IPCTabContext &aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) +{ + return nsIContentParent::AllocPBrowserParent(aTabId, + aContext, + aChromeFlags, + aCpID, + aIsForApp, + aIsForBrowser); +} + +bool +ContentBridgeParent::DeallocPBrowserParent(PBrowserParent* aParent) +{ + return nsIContentParent::DeallocPBrowserParent(aParent); +} + +void +ContentBridgeParent::NotifyTabDestroyed() +{ + int32_t numLiveTabs = ManagedPBrowserParent().Count(); + if (numLiveTabs == 1) { + MessageLoop::current()->PostTask(NewRunnableMethod(this, &ContentBridgeParent::Close)); + } +} + +// This implementation is identical to ContentParent::GetCPOWManager but we can't +// move it to nsIContentParent because it calls ManagedPJavaScriptParent() which +// only exists in PContentParent and PContentBridgeParent. +jsipc::CPOWManager* +ContentBridgeParent::GetCPOWManager() +{ + if (PJavaScriptParent* p = LoneManagedOrNullAsserts(ManagedPJavaScriptParent())) { + return CPOWManagerFor(p); + } + return nullptr; +} + +NS_IMETHODIMP +ContentBridgeParent::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + if (!strcmp(aTopic, "content-child-shutdown")) { + Close(); + } + return NS_OK; +} + +PFileDescriptorSetParent* +ContentBridgeParent::AllocPFileDescriptorSetParent(const FileDescriptor& aFD) +{ + return nsIContentParent::AllocPFileDescriptorSetParent(aFD); +} + +bool +ContentBridgeParent::DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor) +{ + return nsIContentParent::DeallocPFileDescriptorSetParent(aActor); +} + +PSendStreamParent* +ContentBridgeParent::AllocPSendStreamParent() +{ + return nsIContentParent::AllocPSendStreamParent(); +} + +bool +ContentBridgeParent::DeallocPSendStreamParent(PSendStreamParent* aActor) +{ + return nsIContentParent::DeallocPSendStreamParent(aActor); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/ContentBridgeParent.h b/dom/ipc/ContentBridgeParent.h new file mode 100644 index 000000000..2f7c951b5 --- /dev/null +++ b/dom/ipc/ContentBridgeParent.h @@ -0,0 +1,154 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_ContentBridgeParent_h +#define mozilla_dom_ContentBridgeParent_h + +#include "mozilla/dom/PContentBridgeParent.h" +#include "mozilla/dom/nsIContentParent.h" +#include "mozilla/dom/ipc/IdType.h" +#include "nsIObserver.h" + +namespace mozilla { +namespace dom { + +class ContentBridgeParent : public PContentBridgeParent + , public nsIContentParent + , public nsIObserver +{ +public: + explicit ContentBridgeParent(Transport* aTransport); + + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + void DeferredDestroy(); + virtual bool IsContentBridgeParent() const override { return true; } + void NotifyTabDestroyed(); + + static ContentBridgeParent* + Create(Transport* aTransport, ProcessId aOtherProcess); + + virtual PBlobParent* + SendPBlobConstructor(PBlobParent* actor, + const BlobConstructorParams& params) override; + + virtual PBrowserParent* + SendPBrowserConstructor(PBrowserParent* aActor, + const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) override; + + FORWARD_SHMEM_ALLOCATOR_TO(PContentBridgeParent) + + jsipc::CPOWManager* GetCPOWManager() override; + + virtual ContentParentId ChildID() const override + { + return mChildID; + } + virtual bool IsForApp() const override + { + return mIsForApp; + } + virtual bool IsForBrowser() const override + { + return mIsForBrowser; + } + virtual int32_t Pid() const override + { + // XXX: do we need this for ContentBridgeParent? + return -1; + } + +protected: + virtual ~ContentBridgeParent(); + + void SetChildID(ContentParentId aId) + { + mChildID = aId; + } + + void SetIsForApp(bool aIsForApp) + { + mIsForApp = aIsForApp; + } + + void SetIsForBrowser(bool aIsForBrowser) + { + mIsForBrowser = aIsForBrowser; + } + + void Close() + { + // Trick NewRunnableMethod + PContentBridgeParent::Close(); + } + +protected: + virtual bool + RecvSyncMessage(const nsString& aMsg, + const ClonedMessageData& aData, + InfallibleTArray<jsipc::CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + nsTArray<StructuredCloneData>* aRetvals) override; + + virtual bool RecvAsyncMessage(const nsString& aMsg, + InfallibleTArray<jsipc::CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData) override; + + virtual jsipc::PJavaScriptParent* AllocPJavaScriptParent() override; + + virtual bool + DeallocPJavaScriptParent(jsipc::PJavaScriptParent*) override; + + virtual PBrowserParent* + AllocPBrowserParent(const TabId& aTabId, + const IPCTabContext &aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) override; + + virtual bool DeallocPBrowserParent(PBrowserParent*) override; + + virtual PBlobParent* + AllocPBlobParent(const BlobConstructorParams& aParams) override; + + virtual bool DeallocPBlobParent(PBlobParent*) override; + + virtual PSendStreamParent* AllocPSendStreamParent() override; + + virtual bool DeallocPSendStreamParent(PSendStreamParent* aActor) override; + + virtual PFileDescriptorSetParent* + AllocPFileDescriptorSetParent(const mozilla::ipc::FileDescriptor&) override; + + virtual bool + DeallocPFileDescriptorSetParent(PFileDescriptorSetParent*) override; + + DISALLOW_EVIL_CONSTRUCTORS(ContentBridgeParent); + +protected: // members + RefPtr<ContentBridgeParent> mSelfRef; + Transport* mTransport; // owned + ContentParentId mChildID; + bool mIsForApp; + bool mIsForBrowser; + +private: + friend class ContentParent; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_ContentBridgeParent_h diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp new file mode 100644 index 000000000..ca4acf114 --- /dev/null +++ b/dom/ipc/ContentChild.cpp @@ -0,0 +1,3310 @@ +/* -*- 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/. */ + +#ifdef MOZ_WIDGET_GTK +#include <gtk/gtk.h> +#endif + +#include "ContentChild.h" + +#include "BlobChild.h" +#include "CrashReporterChild.h" +#include "GeckoProfiler.h" +#include "TabChild.h" +#include "HandlerServiceChild.h" + +#include "mozilla/Attributes.h" +#include "mozilla/LookAndFeel.h" +#include "mozilla/Preferences.h" +#include "mozilla/ProcessHangMonitorIPC.h" +#include "mozilla/Unused.h" +#include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h" +#include "mozilla/docshell/OfflineCacheUpdateChild.h" +#include "mozilla/dom/ContentBridgeChild.h" +#include "mozilla/dom/ContentBridgeParent.h" +#include "mozilla/dom/VideoDecoderManagerChild.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/DataTransfer.h" +#include "mozilla/dom/DOMStorageIPC.h" +#include "mozilla/dom/ExternalHelperAppChild.h" +#include "mozilla/dom/FlyWebPublishedServerIPC.h" +#include "mozilla/dom/GetFilesHelper.h" +#include "mozilla/dom/PCrashReporterChild.h" +#include "mozilla/dom/ProcessGlobal.h" +#include "mozilla/dom/PushNotifier.h" +#include "mozilla/dom/workers/ServiceWorkerManager.h" +#include "mozilla/dom/nsIContentChild.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/psm/PSMContentListener.h" +#include "mozilla/hal_sandbox/PHalChild.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/FileDescriptorSetChild.h" +#include "mozilla/ipc/FileDescriptorUtils.h" +#include "mozilla/ipc/GeckoChildProcessHost.h" +#include "mozilla/ipc/ProcessChild.h" +#include "mozilla/ipc/PSendStreamChild.h" +#include "mozilla/ipc/TestShellChild.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" +#include "mozilla/layers/APZChild.h" +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/ContentProcessController.h" +#include "mozilla/layers/ImageBridgeChild.h" +#include "mozilla/layout/RenderFrameChild.h" +#include "mozilla/net/NeckoChild.h" +#include "mozilla/net/CaptivePortalService.h" +#include "mozilla/plugins/PluginInstanceParent.h" +#include "mozilla/plugins/PluginModuleParent.h" +#include "mozilla/widget/WidgetMessageUtils.h" +#include "nsBaseDragService.h" +#include "mozilla/media/MediaChild.h" +#include "mozilla/BasePrincipal.h" +#include "mozilla/WebBrowserPersistDocumentChild.h" +#include "imgLoader.h" +#include "GMPServiceChild.h" + +#if defined(MOZ_CONTENT_SANDBOX) +#if defined(XP_WIN) +#define TARGET_SANDBOX_EXPORTS +#include "mozilla/sandboxTarget.h" +#elif defined(XP_LINUX) +#include "mozilla/Sandbox.h" +#include "mozilla/SandboxInfo.h" + +// Remove this include with Bug 1104619 +#include "CubebUtils.h" +#elif defined(XP_MACOSX) +#include "mozilla/Sandbox.h" +#endif +#endif + +#include "mozilla/Unused.h" + +#include "mozInlineSpellChecker.h" +#include "nsDocShell.h" +#include "nsIConsoleListener.h" +#include "nsICycleCollectorListener.h" +#include "nsIIdlePeriod.h" +#include "nsIDragService.h" +#include "nsIIPCBackgroundChildCreateCallback.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIMemoryReporter.h" +#include "nsIMemoryInfoDumper.h" +#include "nsIMutable.h" +#include "nsIObserverService.h" +#include "nsIScriptSecurityManager.h" +#include "nsScreenManagerProxy.h" +#include "nsMemoryInfoDumper.h" +#include "nsServiceManagerUtils.h" +#include "nsStyleSheetService.h" +#include "nsVariant.h" +#include "nsXULAppAPI.h" +#include "nsIScriptError.h" +#include "nsIConsoleService.h" +#include "nsJSEnvironment.h" +#include "SandboxHal.h" +#include "nsDebugImpl.h" +#include "nsHashPropertyBag.h" +#include "nsLayoutStylesheetCache.h" +#include "nsThreadManager.h" +#include "nsAnonymousTemporaryFile.h" +#include "nsISpellChecker.h" +#include "nsClipboardProxy.h" +#include "nsDirectoryService.h" +#include "nsDirectoryServiceUtils.h" +#include "nsDirectoryServiceDefs.h" +#include "nsContentPermissionHelper.h" +#ifdef NS_PRINTING +#include "nsPrintingProxy.h" +#endif + +#include "IHistory.h" +#include "nsNetUtil.h" + +#include "base/message_loop.h" +#include "base/process_util.h" +#include "base/task.h" + +#include "nsChromeRegistryContent.h" +#include "nsFrameMessageManager.h" + +#include "nsIGeolocationProvider.h" +#include "mozilla/dom/PMemoryReportRequestChild.h" +#include "mozilla/dom/PCycleCollectWithLogsChild.h" + +#include "nsIScriptSecurityManager.h" +#include "nsHostObjectProtocolHandler.h" + +#ifdef MOZ_WEBRTC +#include "signaling/src/peerconnection/WebrtcGlobalChild.h" +#endif + +#ifdef MOZ_PERMISSIONS +#include "nsPermission.h" +#include "nsPermissionManager.h" +#endif + +#include "PermissionMessageUtils.h" + +#if defined(MOZ_WIDGET_ANDROID) +#include "APKOpen.h" +#endif + +#if defined(MOZ_WIDGET_GONK) +#include "nsVolume.h" +#include "nsVolumeService.h" +#include "SpeakerManagerService.h" +#endif + +#ifdef XP_WIN +#include <process.h> +#define getpid _getpid +#include "mozilla/widget/AudioSession.h" +#endif + +#ifdef MOZ_X11 +#include "mozilla/X11Util.h" +#endif + +#ifdef ACCESSIBILITY +#include "nsAccessibilityService.h" +#endif + +#include "mozilla/dom/File.h" +#include "mozilla/dom/PPresentationChild.h" +#include "mozilla/dom/PresentationIPCService.h" +#include "mozilla/ipc/InputStreamUtils.h" + +#ifdef MOZ_WEBSPEECH +#include "mozilla/dom/PSpeechSynthesisChild.h" +#endif + +#include "ProcessUtils.h" +#include "URIUtils.h" +#include "nsContentUtils.h" +#include "nsIPrincipal.h" +#include "DomainPolicy.h" +#include "mozilla/dom/ipc/StructuredCloneData.h" +#include "mozilla/dom/time/DateCacheCleaner.h" +#include "mozilla/net/NeckoMessageUtils.h" +#include "mozilla/widget/PuppetBidiKeyboard.h" +#include "mozilla/RemoteSpellCheckEngineChild.h" +#include "GMPServiceChild.h" +#include "gfxPlatform.h" +#include "nscore.h" // for NS_FREE_PERMANENT_DATA +#include "VRManagerChild.h" + +using namespace mozilla; +using namespace mozilla::docshell; +using namespace mozilla::dom::ipc; +using namespace mozilla::dom::workers; +using namespace mozilla::media; +using namespace mozilla::embedding; +using namespace mozilla::gmp; +using namespace mozilla::hal_sandbox; +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; +#if defined(MOZ_WIDGET_GONK) +using namespace mozilla::system; +#endif +using namespace mozilla::widget; + +namespace mozilla { +namespace dom { + +class MemoryReportRequestChild : public PMemoryReportRequestChild, + public nsIRunnable +{ +public: + NS_DECL_ISUPPORTS + + MemoryReportRequestChild(bool aAnonymize, + const MaybeFileDesc& aDMDFile); + NS_IMETHOD Run() override; + +private: + virtual ~MemoryReportRequestChild(); + + bool mAnonymize; + FileDescriptor mDMDFile; +}; + +NS_IMPL_ISUPPORTS(MemoryReportRequestChild, nsIRunnable) + +MemoryReportRequestChild::MemoryReportRequestChild( + bool aAnonymize, const MaybeFileDesc& aDMDFile) +: mAnonymize(aAnonymize) +{ + MOZ_COUNT_CTOR(MemoryReportRequestChild); + if (aDMDFile.type() == MaybeFileDesc::TFileDescriptor) { + mDMDFile = aDMDFile.get_FileDescriptor(); + } +} + +MemoryReportRequestChild::~MemoryReportRequestChild() +{ + MOZ_COUNT_DTOR(MemoryReportRequestChild); +} + +// IPC sender for remote GC/CC logging. +class CycleCollectWithLogsChild final + : public PCycleCollectWithLogsChild + , public nsICycleCollectorLogSink +{ +public: + NS_DECL_ISUPPORTS + + CycleCollectWithLogsChild(const FileDescriptor& aGCLog, + const FileDescriptor& aCCLog) + { + mGCLog = FileDescriptorToFILE(aGCLog, "w"); + mCCLog = FileDescriptorToFILE(aCCLog, "w"); + } + + NS_IMETHOD Open(FILE** aGCLog, FILE** aCCLog) override + { + if (NS_WARN_IF(!mGCLog) || NS_WARN_IF(!mCCLog)) { + return NS_ERROR_FAILURE; + } + *aGCLog = mGCLog; + *aCCLog = mCCLog; + return NS_OK; + } + + NS_IMETHOD CloseGCLog() override + { + MOZ_ASSERT(mGCLog); + fclose(mGCLog); + mGCLog = nullptr; + SendCloseGCLog(); + return NS_OK; + } + + NS_IMETHOD CloseCCLog() override + { + MOZ_ASSERT(mCCLog); + fclose(mCCLog); + mCCLog = nullptr; + SendCloseCCLog(); + return NS_OK; + } + + NS_IMETHOD GetFilenameIdentifier(nsAString& aIdentifier) override + { + return UnimplementedProperty(); + } + + NS_IMETHOD SetFilenameIdentifier(const nsAString& aIdentifier) override + { + return UnimplementedProperty(); + } + + NS_IMETHOD GetProcessIdentifier(int32_t *aIdentifier) override + { + return UnimplementedProperty(); + } + + NS_IMETHOD SetProcessIdentifier(int32_t aIdentifier) override + { + return UnimplementedProperty(); + } + + NS_IMETHOD GetGcLog(nsIFile** aPath) override + { + return UnimplementedProperty(); + } + + NS_IMETHOD GetCcLog(nsIFile** aPath) override + { + return UnimplementedProperty(); + } + +private: + ~CycleCollectWithLogsChild() + { + if (mGCLog) { + fclose(mGCLog); + mGCLog = nullptr; + } + if (mCCLog) { + fclose(mCCLog); + mCCLog = nullptr; + } + // The XPCOM refcount drives the IPC lifecycle; see also + // DeallocPCycleCollectWithLogsChild. + Unused << Send__delete__(this); + } + + nsresult UnimplementedProperty() + { + MOZ_ASSERT(false, "This object is a remote GC/CC logger;" + " this property isn't meaningful."); + return NS_ERROR_UNEXPECTED; + } + + FILE* mGCLog; + FILE* mCCLog; +}; + +NS_IMPL_ISUPPORTS(CycleCollectWithLogsChild, nsICycleCollectorLogSink); + +class AlertObserver +{ +public: + + AlertObserver(nsIObserver *aObserver, const nsString& aData) + : mObserver(aObserver) + , mData(aData) + { + } + + ~AlertObserver() {} + + bool ShouldRemoveFrom(nsIObserver* aObserver, + const nsString& aData) const + { + return (mObserver == aObserver && mData == aData); + } + + bool Observes(const nsString& aData) const + { + return mData.Equals(aData); + } + + bool Notify(const nsCString& aType) const + { + mObserver->Observe(nullptr, aType.get(), mData.get()); + return true; + } + +private: + nsCOMPtr<nsIObserver> mObserver; + nsString mData; +}; + +class ConsoleListener final : public nsIConsoleListener +{ +public: + explicit ConsoleListener(ContentChild* aChild) + : mChild(aChild) {} + + NS_DECL_ISUPPORTS + NS_DECL_NSICONSOLELISTENER + +private: + ~ConsoleListener() {} + + ContentChild* mChild; + friend class ContentChild; +}; + +NS_IMPL_ISUPPORTS(ConsoleListener, nsIConsoleListener) + +// Before we send the error to the parent process (which +// involves copying the memory), truncate any long lines. CSS +// errors in particular share the memory for long lines with +// repeated errors, but the IPC communication we're about to do +// will break that sharing, so we better truncate now. +static void +TruncateString(nsAString& aString) +{ + if (aString.Length() > 1000) { + aString.Truncate(1000); + } +} + +NS_IMETHODIMP +ConsoleListener::Observe(nsIConsoleMessage* aMessage) +{ + if (!mChild) { + return NS_OK; + } + + nsCOMPtr<nsIScriptError> scriptError = do_QueryInterface(aMessage); + if (scriptError) { + nsAutoString msg, sourceName, sourceLine; + nsXPIDLCString category; + uint32_t lineNum, colNum, flags; + + nsresult rv = scriptError->GetErrorMessage(msg); + NS_ENSURE_SUCCESS(rv, rv); + TruncateString(msg); + rv = scriptError->GetSourceName(sourceName); + NS_ENSURE_SUCCESS(rv, rv); + TruncateString(sourceName); + rv = scriptError->GetSourceLine(sourceLine); + NS_ENSURE_SUCCESS(rv, rv); + TruncateString(sourceLine); + + rv = scriptError->GetCategory(getter_Copies(category)); + NS_ENSURE_SUCCESS(rv, rv); + rv = scriptError->GetLineNumber(&lineNum); + NS_ENSURE_SUCCESS(rv, rv); + rv = scriptError->GetColumnNumber(&colNum); + NS_ENSURE_SUCCESS(rv, rv); + rv = scriptError->GetFlags(&flags); + NS_ENSURE_SUCCESS(rv, rv); + + mChild->SendScriptError(msg, sourceName, sourceLine, + lineNum, colNum, flags, category); + return NS_OK; + } + + nsXPIDLString msg; + nsresult rv = aMessage->GetMessageMoz(getter_Copies(msg)); + NS_ENSURE_SUCCESS(rv, rv); + mChild->SendConsoleMessage(msg); + return NS_OK; +} + +class BackgroundChildPrimer final : + public nsIIPCBackgroundChildCreateCallback +{ +public: + BackgroundChildPrimer() + { } + + NS_DECL_ISUPPORTS + +private: + ~BackgroundChildPrimer() + { } + + virtual void + ActorCreated(PBackgroundChild* aActor) override + { + MOZ_ASSERT(aActor, "Failed to create a PBackgroundChild actor!"); + } + + virtual void + ActorFailed() override + { + MOZ_CRASH("Failed to create a PBackgroundChild actor!"); + } +}; + +NS_IMPL_ISUPPORTS(BackgroundChildPrimer, nsIIPCBackgroundChildCreateCallback) + +ContentChild* ContentChild::sSingleton; + +ContentChild::ContentChild() + : mID(uint64_t(-1)) +#if defined(XP_WIN) && defined(ACCESSIBILITY) + , mMsaaID(0) +#endif + , mCanOverrideProcessName(true) + , mIsAlive(true) + , mShuttingDown(false) +{ + // This process is a content process, so it's clearly running in + // multiprocess mode! + nsDebugImpl::SetMultiprocessMode("Child"); +} + +ContentChild::~ContentChild() +{ +#ifndef NS_FREE_PERMANENT_DATA + NS_RUNTIMEABORT("Content Child shouldn't be destroyed."); +#endif +} + +NS_INTERFACE_MAP_BEGIN(ContentChild) + NS_INTERFACE_MAP_ENTRY(nsIContentChild) + NS_INTERFACE_MAP_ENTRY(nsIWindowProvider) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentChild) +NS_INTERFACE_MAP_END + +bool +ContentChild::Init(MessageLoop* aIOLoop, + base::ProcessId aParentPid, + IPC::Channel* aChannel) +{ +#ifdef MOZ_WIDGET_GTK + // We need to pass a display down to gtk_init because it's not going to + // use the one from the environment on its own when deciding which backend + // to use, and when starting under XWayland, it may choose to start with + // the wayland backend instead of the x11 backend. + // The DISPLAY environment variable is normally set by the parent process. + char* display_name = PR_GetEnv("DISPLAY"); + if (display_name) { + int argc = 3; + char option_name[] = "--display"; + char* argv[] = { + // argv0 is unused because g_set_prgname() was called in + // XRE_InitChildProcess(). + nullptr, + option_name, + display_name, + nullptr + }; + char** argvp = argv; + gtk_init(&argc, &argvp); + } else { + gtk_init(nullptr, nullptr); + } +#endif + +#ifdef MOZ_X11 + // Do this after initializing GDK, or GDK will install its own handler. + XRE_InstallX11ErrorHandler(); +#endif + + NS_ASSERTION(!sSingleton, "only one ContentChild per child"); + + // Once we start sending IPC messages, we need the thread manager to be + // initialized so we can deal with the responses. Do that here before we + // try to construct the crash reporter. + nsresult rv = nsThreadManager::get().Init(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + if (!Open(aChannel, aParentPid, aIOLoop)) { + return false; + } + sSingleton = this; + + // If communications with the parent have broken down, take the process + // down so it's not hanging around. + GetIPCChannel()->SetAbortOnError(true); +#if defined(XP_WIN) && defined(ACCESSIBILITY) + GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_A11Y_REENTRY); +#endif + +#ifdef MOZ_X11 + // Send the parent our X socket to act as a proxy reference for our X + // resources. + int xSocketFd = ConnectionNumber(DefaultXDisplay()); + SendBackUpXResources(FileDescriptor(xSocketFd)); +#endif + +#ifdef MOZ_CRASHREPORTER + SendPCrashReporterConstructor(CrashReporter::CurrentThreadId(), + XRE_GetProcessType()); +#endif + + SendGetProcessAttributes(&mID, &mIsForApp, &mIsForBrowser); + InitProcessAttributes(); + +#ifdef NS_PRINTING + // Force the creation of the nsPrintingProxy so that it's IPC counterpart, + // PrintingParent, is always available for printing initiated from the parent. + RefPtr<nsPrintingProxy> printingProxy = nsPrintingProxy::GetInstance(); +#endif + + return true; +} + +void +ContentChild::InitProcessAttributes() +{ +#ifdef MOZ_WIDGET_GONK + if (mIsForApp && !mIsForBrowser) { + SetProcessName(NS_LITERAL_STRING("(Preallocated app)"), false); + } else { + SetProcessName(NS_LITERAL_STRING("Browser"), false); + } +#else + SetProcessName(NS_LITERAL_STRING("Web Content"), true); +#endif +} + +void +ContentChild::SetProcessName(const nsAString& aName, bool aDontOverride) +{ + if (!mCanOverrideProcessName) { + return; + } + + char* name; + if ((name = PR_GetEnv("MOZ_DEBUG_APP_PROCESS")) && + aName.EqualsASCII(name)) { +#ifdef OS_POSIX + printf_stderr("\n\nCHILDCHILDCHILDCHILD\n [%s] debug me @%d\n\n", name, + getpid()); + sleep(30); +#elif defined(OS_WIN) + // Windows has a decent JIT debugging story, so NS_DebugBreak does the + // right thing. + NS_DebugBreak(NS_DEBUG_BREAK, + "Invoking NS_DebugBreak() to debug child process", + nullptr, __FILE__, __LINE__); +#endif + } + + mProcessName = aName; + mozilla::ipc::SetThisProcessName(NS_LossyConvertUTF16toASCII(aName).get()); + + if (aDontOverride) { + mCanOverrideProcessName = false; + } +} + +NS_IMETHODIMP +ContentChild::ProvideWindow(mozIDOMWindowProxy* aParent, + uint32_t aChromeFlags, + bool aCalledFromJS, + bool aPositionSpecified, + bool aSizeSpecified, + nsIURI* aURI, + const nsAString& aName, + const nsACString& aFeatures, + bool aForceNoOpener, + bool* aWindowIsNew, + mozIDOMWindowProxy** aReturn) +{ + return ProvideWindowCommon(nullptr, aParent, false, aChromeFlags, + aCalledFromJS, aPositionSpecified, + aSizeSpecified, aURI, aName, aFeatures, + aForceNoOpener, aWindowIsNew, aReturn); +} + +nsresult +ContentChild::ProvideWindowCommon(TabChild* aTabOpener, + mozIDOMWindowProxy* aParent, + bool aIframeMoz, + uint32_t aChromeFlags, + bool aCalledFromJS, + bool aPositionSpecified, + bool aSizeSpecified, + nsIURI* aURI, + const nsAString& aName, + const nsACString& aFeatures, + bool aForceNoOpener, + bool* aWindowIsNew, + mozIDOMWindowProxy** aReturn) +{ + *aReturn = nullptr; + + nsAutoPtr<IPCTabContext> ipcContext; + TabId openerTabId = TabId(0); + + if (aTabOpener) { + PopupIPCTabContext context; + openerTabId = aTabOpener->GetTabId(); + context.opener() = openerTabId; + context.isMozBrowserElement() = aTabOpener->IsMozBrowserElement(); + ipcContext = new IPCTabContext(context); + } else { + // It's possible to not have a TabChild opener in the case + // of ServiceWorker::OpenWindow. + UnsafeIPCTabContext unsafeTabContext; + ipcContext = new IPCTabContext(unsafeTabContext); + } + + MOZ_ASSERT(ipcContext); + TabId tabId; + SendAllocateTabId(openerTabId, + *ipcContext, + GetID(), + &tabId); + + TabContext newTabContext = aTabOpener ? *aTabOpener : TabContext(); + RefPtr<TabChild> newChild = new TabChild(this, tabId, + newTabContext, aChromeFlags); + if (NS_FAILED(newChild->Init())) { + return NS_ERROR_ABORT; + } + + if (aTabOpener) { + MOZ_ASSERT(ipcContext->type() == IPCTabContext::TPopupIPCTabContext); + ipcContext->get_PopupIPCTabContext().opener() = aTabOpener; + } + + Unused << SendPBrowserConstructor( + // We release this ref in DeallocPBrowserChild + RefPtr<TabChild>(newChild).forget().take(), + tabId, *ipcContext, aChromeFlags, + GetID(), IsForApp(), IsForBrowser()); + + nsString name(aName); + nsAutoCString features(aFeatures); + nsTArray<FrameScriptInfo> frameScripts; + nsCString urlToLoad; + + PRenderFrameChild* renderFrame = newChild->SendPRenderFrameConstructor(); + TextureFactoryIdentifier textureFactoryIdentifier; + uint64_t layersId = 0; + + if (aIframeMoz) { + MOZ_ASSERT(aTabOpener); + nsAutoCString url; + if (aURI) { + aURI->GetSpec(url); + } else { + // We can't actually send a nullptr up as the URI, since IPDL doesn't let us + // send nullptr's for primitives. We indicate that the nsString for the URI + // should be converted to a nullptr by voiding the string. + url.SetIsVoid(true); + } + + newChild->SendBrowserFrameOpenWindow(aTabOpener, renderFrame, NS_ConvertUTF8toUTF16(url), + name, NS_ConvertUTF8toUTF16(features), + aWindowIsNew, &textureFactoryIdentifier, + &layersId); + } else { + nsAutoCString baseURIString; + if (aTabOpener) { + auto* opener = nsPIDOMWindowOuter::From(aParent); + nsCOMPtr<nsIDocument> doc = opener->GetDoc(); + nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI(); + if (!baseURI) { + NS_ERROR("nsIDocument didn't return a base URI"); + return NS_ERROR_FAILURE; + } + + baseURI->GetSpec(baseURIString); + } + + auto* opener = nsPIDOMWindowOuter::From(aParent); + nsIDocShell* openerShell; + RefPtr<nsDocShell> openerDocShell; + float fullZoom = 1.0f; + if (opener && (openerShell = opener->GetDocShell())) { + openerDocShell = static_cast<nsDocShell*>(openerShell); + nsCOMPtr<nsIContentViewer> cv; + openerDocShell->GetContentViewer(getter_AddRefs(cv)); + if (cv) { + cv->GetFullZoom(&fullZoom); + } + } + + nsresult rv; + if (!SendCreateWindow(aTabOpener, newChild, renderFrame, + aChromeFlags, aCalledFromJS, aPositionSpecified, + aSizeSpecified, + features, + baseURIString, + openerDocShell + ? openerDocShell->GetOriginAttributes() + : DocShellOriginAttributes(), + fullZoom, + &rv, + aWindowIsNew, + &frameScripts, + &urlToLoad, + &textureFactoryIdentifier, + &layersId)) { + PRenderFrameChild::Send__delete__(renderFrame); + return NS_ERROR_NOT_AVAILABLE; + } + + if (NS_FAILED(rv)) { + PRenderFrameChild::Send__delete__(renderFrame); + return rv; + } + } + if (!*aWindowIsNew) { + PRenderFrameChild::Send__delete__(renderFrame); + return NS_ERROR_ABORT; + } + + if (layersId == 0) { // if renderFrame is invalid. + PRenderFrameChild::Send__delete__(renderFrame); + renderFrame = nullptr; + } + + ShowInfo showInfo(EmptyString(), false, false, true, false, 0, 0, 0); + auto* opener = nsPIDOMWindowOuter::From(aParent); + nsIDocShell* openerShell; + if (opener && (openerShell = opener->GetDocShell())) { + nsCOMPtr<nsILoadContext> context = do_QueryInterface(openerShell); + showInfo = ShowInfo(EmptyString(), false, + context->UsePrivateBrowsing(), true, false, + aTabOpener->mDPI, aTabOpener->mRounding, + aTabOpener->mDefaultScale); + } + + // Set the opener window for this window before we start loading the document + // inside of it. We have to do this before loading the remote scripts, because + // they can poke at the document and cause the nsDocument to be created before + // the openerwindow + nsCOMPtr<mozIDOMWindowProxy> windowProxy = do_GetInterface(newChild->WebNavigation()); + if (!aForceNoOpener && windowProxy && aParent) { + nsPIDOMWindowOuter* outer = nsPIDOMWindowOuter::From(windowProxy); + nsPIDOMWindowOuter* parent = nsPIDOMWindowOuter::From(aParent); + outer->SetOpenerWindow(parent, *aWindowIsNew); + } + + // Unfortunately we don't get a window unless we've shown the frame. That's + // pretty bogus; see bug 763602. + newChild->DoFakeShow(textureFactoryIdentifier, layersId, renderFrame, + showInfo); + + for (size_t i = 0; i < frameScripts.Length(); i++) { + FrameScriptInfo& info = frameScripts[i]; + if (!newChild->RecvLoadRemoteScript(info.url(), info.runInGlobalScope())) { + MOZ_CRASH(); + } + } + + if (!urlToLoad.IsEmpty()) { + newChild->RecvLoadURL(urlToLoad, showInfo); + } + + nsCOMPtr<mozIDOMWindowProxy> win = do_GetInterface(newChild->WebNavigation()); + win.forget(aReturn); + return NS_OK; +} + +void +ContentChild::GetProcessName(nsAString& aName) const +{ + aName.Assign(mProcessName); +} + +bool +ContentChild::IsAlive() const +{ + return mIsAlive; +} + +bool +ContentChild::IsShuttingDown() const +{ + return mShuttingDown; +} + +void +ContentChild::GetProcessName(nsACString& aName) const +{ + aName.Assign(NS_ConvertUTF16toUTF8(mProcessName)); +} + +/* static */ void +ContentChild::AppendProcessId(nsACString& aName) +{ + if (!aName.IsEmpty()) { + aName.Append(' '); + } + unsigned pid = getpid(); + aName.Append(nsPrintfCString("(pid %u)", pid)); +} + +void +ContentChild::InitGraphicsDeviceData() +{ + // Initialize the graphics platform. This may contact the parent process + // to read device preferences. + gfxPlatform::GetPlatform(); +} + +void +ContentChild::InitXPCOM() +{ + // Do this as early as possible to get the parent process to initialize the + // background thread since we'll likely need database information very soon. + BackgroundChild::Startup(); + + nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback = + new BackgroundChildPrimer(); + if (!BackgroundChild::GetOrCreateForCurrentThread(callback)) { + MOZ_CRASH("Failed to create PBackgroundChild!"); + } + + BlobChild::Startup(BlobChild::FriendKey()); + + nsCOMPtr<nsIConsoleService> svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID)); + if (!svc) { + NS_WARNING("Couldn't acquire console service"); + return; + } + + mConsoleListener = new ConsoleListener(this); + if (NS_FAILED(svc->RegisterListener(mConsoleListener))) + NS_WARNING("Couldn't register console listener for child process"); + + bool isOffline, isLangRTL, haveBidiKeyboards; + bool isConnected; + ClipboardCapabilities clipboardCaps; + DomainPolicyClone domainPolicy; + StructuredCloneData initialData; + OptionalURIParams userContentSheetURL; + + int32_t captivePortalState; + SendGetXPCOMProcessAttributes(&isOffline, &isConnected, &captivePortalState, + &isLangRTL, &haveBidiKeyboards, + &mAvailableDictionaries, + &clipboardCaps, &domainPolicy, &initialData, + &userContentSheetURL); + RecvSetOffline(isOffline); + RecvSetConnectivity(isConnected); + RecvSetCaptivePortalState(captivePortalState); + RecvBidiKeyboardNotify(isLangRTL, haveBidiKeyboards); + + // Create the CPOW manager as soon as possible. + SendPJavaScriptConstructor(); + + if (domainPolicy.active()) { + nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); + MOZ_ASSERT(ssm); + ssm->ActivateDomainPolicyInternal(getter_AddRefs(mPolicy)); + if (!mPolicy) { + MOZ_CRASH("Failed to activate domain policy."); + } + mPolicy->ApplyClone(&domainPolicy); + } + + nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1")); + if (nsCOMPtr<nsIClipboardProxy> clipboardProxy = do_QueryInterface(clipboard)) { + clipboardProxy->SetCapabilities(clipboardCaps); + } + + { + AutoJSAPI jsapi; + if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) { + MOZ_CRASH(); + } + ErrorResult rv; + JS::RootedValue data(jsapi.cx()); + initialData.Read(jsapi.cx(), &data, rv); + if (NS_WARN_IF(rv.Failed())) { + MOZ_CRASH(); + } + ProcessGlobal* global = ProcessGlobal::Get(); + global->SetInitialProcessData(data); + } + + // The stylesheet cache is not ready yet. Store this URL for future use. + nsCOMPtr<nsIURI> ucsURL = DeserializeURI(userContentSheetURL); + nsLayoutStylesheetCache::SetUserContentCSSURL(ucsURL); + + // This will register cross-process observer. + mozilla::dom::time::InitializeDateCacheCleaner(); +} + +PMemoryReportRequestChild* +ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration, + const bool &aAnonymize, + const bool &aMinimizeMemoryUsage, + const MaybeFileDesc& aDMDFile) +{ + MemoryReportRequestChild *actor = + new MemoryReportRequestChild(aAnonymize, aDMDFile); + actor->AddRef(); + return actor; +} + +class HandleReportCallback final : public nsIHandleReportCallback +{ +public: + NS_DECL_ISUPPORTS + + explicit HandleReportCallback(MemoryReportRequestChild* aActor, + const nsACString& aProcess) + : mActor(aActor) + , mProcess(aProcess) + { } + + NS_IMETHOD Callback(const nsACString& aProcess, const nsACString &aPath, + int32_t aKind, int32_t aUnits, int64_t aAmount, + const nsACString& aDescription, + nsISupports* aUnused) override + { + MemoryReport memreport(mProcess, nsCString(aPath), aKind, aUnits, + aAmount, nsCString(aDescription)); + mActor->SendReport(memreport); + return NS_OK; + } +private: + ~HandleReportCallback() {} + + RefPtr<MemoryReportRequestChild> mActor; + const nsCString mProcess; +}; + +NS_IMPL_ISUPPORTS( + HandleReportCallback +, nsIHandleReportCallback +) + +class FinishReportingCallback final : public nsIFinishReportingCallback +{ +public: + NS_DECL_ISUPPORTS + + explicit FinishReportingCallback(MemoryReportRequestChild* aActor) + : mActor(aActor) + { + } + + NS_IMETHOD Callback(nsISupports* aUnused) override + { + bool sent = PMemoryReportRequestChild::Send__delete__(mActor); + return sent ? NS_OK : NS_ERROR_FAILURE; + } + +private: + ~FinishReportingCallback() {} + + RefPtr<MemoryReportRequestChild> mActor; +}; + +NS_IMPL_ISUPPORTS( + FinishReportingCallback +, nsIFinishReportingCallback +) + +bool +ContentChild::RecvPMemoryReportRequestConstructor( + PMemoryReportRequestChild* aChild, + const uint32_t& aGeneration, + const bool& aAnonymize, + const bool& aMinimizeMemoryUsage, + const MaybeFileDesc& aDMDFile) +{ + MemoryReportRequestChild *actor = + static_cast<MemoryReportRequestChild*>(aChild); + DebugOnly<nsresult> rv; + + if (aMinimizeMemoryUsage) { + nsCOMPtr<nsIMemoryReporterManager> mgr = + do_GetService("@mozilla.org/memory-reporter-manager;1"); + rv = mgr->MinimizeMemoryUsage(actor); + // mgr will eventually call actor->Run() + } else { + rv = actor->Run(); + } + + // Bug 1295622: don't kill the process just because this failed. + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "actor operation failed"); + return true; +} + +NS_IMETHODIMP MemoryReportRequestChild::Run() +{ + ContentChild *child = static_cast<ContentChild*>(Manager()); + nsCOMPtr<nsIMemoryReporterManager> mgr = + do_GetService("@mozilla.org/memory-reporter-manager;1"); + + nsCString process; + child->GetProcessName(process); + child->AppendProcessId(process); + + // Run the reporters. The callback will turn each measurement into a + // MemoryReport. + RefPtr<HandleReportCallback> handleReport = + new HandleReportCallback(this, process); + RefPtr<FinishReportingCallback> finishReporting = + new FinishReportingCallback(this); + + nsresult rv = + mgr->GetReportsForThisProcessExtended(handleReport, nullptr, mAnonymize, + FileDescriptorToFILE(mDMDFile, "wb"), + finishReporting, nullptr); + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "GetReportsForThisProcessExtended failed"); + return rv; +} + +bool +ContentChild::DeallocPMemoryReportRequestChild(PMemoryReportRequestChild* actor) +{ + static_cast<MemoryReportRequestChild*>(actor)->Release(); + return true; +} + +PCycleCollectWithLogsChild* +ContentChild::AllocPCycleCollectWithLogsChild(const bool& aDumpAllTraces, + const FileDescriptor& aGCLog, + const FileDescriptor& aCCLog) +{ + CycleCollectWithLogsChild* actor = new CycleCollectWithLogsChild(aGCLog, aCCLog); + // Return actor with refcount 0, which is safe because it has a non-XPCOM type. + return actor; +} + +bool +ContentChild::RecvPCycleCollectWithLogsConstructor(PCycleCollectWithLogsChild* aActor, + const bool& aDumpAllTraces, + const FileDescriptor& aGCLog, + const FileDescriptor& aCCLog) +{ + // Take a reference here, where the XPCOM type is regained. + RefPtr<CycleCollectWithLogsChild> sink = static_cast<CycleCollectWithLogsChild*>(aActor); + nsCOMPtr<nsIMemoryInfoDumper> dumper = do_GetService("@mozilla.org/memory-info-dumper;1"); + + dumper->DumpGCAndCCLogsToSink(aDumpAllTraces, sink); + + // The actor's destructor is called when the last reference goes away... + return true; +} + +bool +ContentChild::DeallocPCycleCollectWithLogsChild(PCycleCollectWithLogsChild* /* aActor */) +{ + // ...so when we get here, there's nothing for us to do. + // + // Also, we're already in ~CycleCollectWithLogsChild (q.v.) at + // this point, so we shouldn't touch the actor in any case. + return true; +} + +mozilla::plugins::PPluginModuleParent* +ContentChild::AllocPPluginModuleParent(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess) +{ + return plugins::PluginModuleContentParent::Initialize(aTransport, aOtherProcess); +} + +PContentBridgeChild* +ContentChild::AllocPContentBridgeChild(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess) +{ + return ContentBridgeChild::Create(aTransport, aOtherProcess); +} + +PContentBridgeParent* +ContentChild::AllocPContentBridgeParent(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess) +{ + MOZ_ASSERT(!mLastBridge); + mLastBridge = static_cast<ContentBridgeParent*>( + ContentBridgeParent::Create(aTransport, aOtherProcess)); + return mLastBridge; +} + +PGMPServiceChild* +ContentChild::AllocPGMPServiceChild(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess) +{ + return GMPServiceChild::Create(aTransport, aOtherProcess); +} + +bool +ContentChild::RecvGMPsChanged(nsTArray<GMPCapabilityData>&& capabilities) +{ + GeckoMediaPluginServiceChild::UpdateGMPCapabilities(Move(capabilities)); + return true; +} + +bool +ContentChild::RecvInitRendering(Endpoint<PCompositorBridgeChild>&& aCompositor, + Endpoint<PImageBridgeChild>&& aImageBridge, + Endpoint<PVRManagerChild>&& aVRBridge, + Endpoint<PVideoDecoderManagerChild>&& aVideoManager) +{ + if (!CompositorBridgeChild::InitForContent(Move(aCompositor))) { + return false; + } + if (!ImageBridgeChild::InitForContent(Move(aImageBridge))) { + return false; + } + if (!gfx::VRManagerChild::InitForContent(Move(aVRBridge))) { + return false; + } + VideoDecoderManagerChild::InitForContent(Move(aVideoManager)); + return true; +} + +bool +ContentChild::RecvReinitRendering(Endpoint<PCompositorBridgeChild>&& aCompositor, + Endpoint<PImageBridgeChild>&& aImageBridge, + Endpoint<PVRManagerChild>&& aVRBridge, + Endpoint<PVideoDecoderManagerChild>&& aVideoManager) +{ + nsTArray<RefPtr<TabChild>> tabs = TabChild::GetAll(); + + // Zap all the old layer managers we have lying around. + for (const auto& tabChild : tabs) { + if (tabChild->LayersId()) { + tabChild->InvalidateLayers(); + } + } + + // Re-establish singleton bridges to the compositor. + if (!CompositorBridgeChild::ReinitForContent(Move(aCompositor))) { + return false; + } + if (!ImageBridgeChild::ReinitForContent(Move(aImageBridge))) { + return false; + } + if (!gfx::VRManagerChild::ReinitForContent(Move(aVRBridge))) { + return false; + } + + // Establish new PLayerTransactions. + for (const auto& tabChild : tabs) { + if (tabChild->LayersId()) { + tabChild->ReinitRendering(); + } + } + + VideoDecoderManagerChild::InitForContent(Move(aVideoManager)); + return true; +} + +PBackgroundChild* +ContentChild::AllocPBackgroundChild(Transport* aTransport, + ProcessId aOtherProcess) +{ + return BackgroundChild::Alloc(aTransport, aOtherProcess); +} + +PProcessHangMonitorChild* +ContentChild::AllocPProcessHangMonitorChild(Transport* aTransport, + ProcessId aOtherProcess) +{ + return CreateHangMonitorChild(aTransport, aOtherProcess); +} + +#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) + +#include <stdlib.h> + +static bool +GetAppPaths(nsCString &aAppPath, nsCString &aAppBinaryPath, nsCString &aAppDir) +{ + nsAutoCString appPath; + nsAutoCString appBinaryPath( + (CommandLine::ForCurrentProcess()->argv()[0]).c_str()); + + nsAutoCString::const_iterator start, end; + appBinaryPath.BeginReading(start); + appBinaryPath.EndReading(end); + if (RFindInReadable(NS_LITERAL_CSTRING(".app/Contents/MacOS/"), start, end)) { + end = start; + ++end; ++end; ++end; ++end; + appBinaryPath.BeginReading(start); + appPath.Assign(Substring(start, end)); + } else { + return false; + } + + nsCOMPtr<nsIFile> app, appBinary; + nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(appPath), + true, getter_AddRefs(app)); + if (NS_FAILED(rv)) { + return false; + } + rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(appBinaryPath), + true, getter_AddRefs(appBinary)); + if (NS_FAILED(rv)) { + return false; + } + + nsCOMPtr<nsIFile> appDir; + nsCOMPtr<nsIProperties> dirSvc = + do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); + if (!dirSvc) { + return false; + } + rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR, + NS_GET_IID(nsIFile), getter_AddRefs(appDir)); + if (NS_FAILED(rv)) { + return false; + } + bool exists; + rv = appDir->Exists(&exists); + if (NS_FAILED(rv) || !exists) { + return false; + } + + bool isLink; + app->IsSymlink(&isLink); + if (isLink) { + app->GetNativeTarget(aAppPath); + } else { + app->GetNativePath(aAppPath); + } + appBinary->IsSymlink(&isLink); + if (isLink) { + appBinary->GetNativeTarget(aAppBinaryPath); + } else { + appBinary->GetNativePath(aAppBinaryPath); + } + appDir->IsSymlink(&isLink); + if (isLink) { + appDir->GetNativeTarget(aAppDir); + } else { + appDir->GetNativePath(aAppDir); + } + + return true; +} + +static bool +StartMacOSContentSandbox() +{ + int sandboxLevel = Preferences::GetInt("security.sandbox.content.level"); + if (sandboxLevel < 1) { + return false; + } + + nsAutoCString appPath, appBinaryPath, appDir; + if (!GetAppPaths(appPath, appBinaryPath, appDir)) { + MOZ_CRASH("Error resolving child process path"); + } + + // During sandboxed content process startup, before reaching + // this point, NS_OS_TEMP_DIR is modified to refer to a sandbox- + // writable temporary directory + nsCOMPtr<nsIFile> tempDir; + nsresult rv = nsDirectoryService::gService->Get(NS_OS_TEMP_DIR, + NS_GET_IID(nsIFile), getter_AddRefs(tempDir)); + if (NS_FAILED(rv)) { + MOZ_CRASH("Failed to get NS_OS_TEMP_DIR"); + } + + nsAutoCString tempDirPath; + tempDir->Normalize(); + rv = tempDir->GetNativePath(tempDirPath); + if (NS_FAILED(rv)) { + MOZ_CRASH("Failed to get NS_OS_TEMP_DIR path"); + } + + nsCOMPtr<nsIFile> profileDir; + ContentChild::GetSingleton()->GetProfileDir(getter_AddRefs(profileDir)); + nsCString profileDirPath; + if (profileDir) { + rv = profileDir->GetNativePath(profileDirPath); + if (NS_FAILED(rv) || profileDirPath.IsEmpty()) { + MOZ_CRASH("Failed to get profile path"); + } + } + + MacSandboxInfo info; + info.type = MacSandboxType_Content; + info.level = info.level = sandboxLevel; + info.appPath.assign(appPath.get()); + info.appBinaryPath.assign(appBinaryPath.get()); + info.appDir.assign(appDir.get()); + info.appTempDir.assign(tempDirPath.get()); + + if (profileDir) { + info.hasSandboxedProfile = true; + info.profileDir.assign(profileDirPath.get()); + } else { + info.hasSandboxedProfile = false; + } + + std::string err; + if (!mozilla::StartMacSandbox(info, err)) { + NS_WARNING(err.c_str()); + MOZ_CRASH("sandbox_init() failed"); + } + + return true; +} +#endif + +bool +ContentChild::RecvSetProcessSandbox(const MaybeFileDesc& aBroker) +{ + // We may want to move the sandbox initialization somewhere else + // at some point; see bug 880808. +#if defined(MOZ_CONTENT_SANDBOX) + bool sandboxEnabled = true; +#if defined(XP_LINUX) +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19 + // For B2G >= KitKat, sandboxing is mandatory; this has already + // been enforced by ContentParent::StartUp(). + MOZ_ASSERT(SandboxInfo::Get().CanSandboxContent()); +#else + // Otherwise, sandboxing is best-effort. + if (!SandboxInfo::Get().CanSandboxContent()) { + sandboxEnabled = false; + } else { + // This triggers the initialization of cubeb, which needs to happen + // before seccomp is enabled (Bug 1259508). It also increases the startup + // time of the content process, because cubeb is usually initialized + // when it is actually needed. This call here is no longer required + // once Bug 1104619 (remoting audio) is resolved. + Unused << CubebUtils::GetCubebContext(); + } + +#endif /* MOZ_WIDGET_GONK && ANDROID_VERSION >= 19 */ + if (sandboxEnabled) { + int brokerFd = -1; + if (aBroker.type() == MaybeFileDesc::TFileDescriptor) { + auto fd = aBroker.get_FileDescriptor().ClonePlatformHandle(); + brokerFd = fd.release(); + // brokerFd < 0 means to allow direct filesystem access, so + // make absolutely sure that doesn't happen if the parent + // didn't intend it. + MOZ_RELEASE_ASSERT(brokerFd >= 0); + } + sandboxEnabled = SetContentProcessSandbox(brokerFd); + } +#elif defined(XP_WIN) + mozilla::SandboxTarget::Instance()->StartSandbox(); +#elif defined(XP_MACOSX) + sandboxEnabled = StartMacOSContentSandbox(); +#endif + +#if defined(MOZ_CRASHREPORTER) + CrashReporter::AnnotateCrashReport( + NS_LITERAL_CSTRING("ContentSandboxEnabled"), + sandboxEnabled? NS_LITERAL_CSTRING("1") : NS_LITERAL_CSTRING("0")); +#if defined(XP_LINUX) && !defined(OS_ANDROID) + nsAutoCString flagsString; + flagsString.AppendInt(SandboxInfo::Get().AsInteger()); + + CrashReporter::AnnotateCrashReport( + NS_LITERAL_CSTRING("ContentSandboxCapabilities"), flagsString); +#endif /* XP_LINUX && !OS_ANDROID */ +#endif /* MOZ_CRASHREPORTER */ +#endif /* MOZ_CONTENT_SANDBOX */ + + return true; +} + +bool +ContentChild::RecvNotifyLayerAllocated(const dom::TabId& aTabId, const uint64_t& aLayersId) +{ + if (!CompositorBridgeChild::Get()->IPCOpen()) { + return true; + } + + APZChild* apz = ContentProcessController::Create(aTabId); + return CompositorBridgeChild::Get()->SendPAPZConstructor(apz, aLayersId); +} + +bool +ContentChild::RecvSpeakerManagerNotify() +{ +#ifdef MOZ_WIDGET_GONK + // Only notify the process which has the SpeakerManager instance. + RefPtr<SpeakerManagerService> service = + SpeakerManagerService::GetSpeakerManagerService(); + if (service) { + service->Notify(); + } + return true; +#endif + return false; +} + +bool +ContentChild::RecvBidiKeyboardNotify(const bool& aIsLangRTL, + const bool& aHaveBidiKeyboards) +{ + // bidi is always of type PuppetBidiKeyboard* (because in the child, the only + // possible implementation of nsIBidiKeyboard is PuppetBidiKeyboard). + PuppetBidiKeyboard* bidi = static_cast<PuppetBidiKeyboard*>(nsContentUtils::GetBidiKeyboard()); + if (bidi) { + bidi->SetBidiKeyboardInfo(aIsLangRTL, aHaveBidiKeyboards); + } + return true; +} + +static CancelableRunnable* sFirstIdleTask; + +static void FirstIdle(void) +{ + MOZ_ASSERT(sFirstIdleTask); + sFirstIdleTask = nullptr; + ContentChild::GetSingleton()->SendFirstIdle(); +} + +mozilla::jsipc::PJavaScriptChild * +ContentChild::AllocPJavaScriptChild() +{ + MOZ_ASSERT(ManagedPJavaScriptChild().IsEmpty()); + + return nsIContentChild::AllocPJavaScriptChild(); +} + +bool +ContentChild::DeallocPJavaScriptChild(PJavaScriptChild *aChild) +{ + return nsIContentChild::DeallocPJavaScriptChild(aChild); +} + +PBrowserChild* +ContentChild::AllocPBrowserChild(const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) +{ + return nsIContentChild::AllocPBrowserChild(aTabId, + aContext, + aChromeFlags, + aCpID, + aIsForApp, + aIsForBrowser); +} + +bool +ContentChild::SendPBrowserConstructor(PBrowserChild* aActor, + const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) +{ + if (IsShuttingDown()) { + return false; + } + + return PContentChild::SendPBrowserConstructor(aActor, + aTabId, + aContext, + aChromeFlags, + aCpID, + aIsForApp, + aIsForBrowser); +} + +bool +ContentChild::RecvPBrowserConstructor(PBrowserChild* aActor, + const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) +{ + MOZ_ASSERT(!IsShuttingDown()); + + // This runs after AllocPBrowserChild() returns and the IPC machinery for this + // PBrowserChild has been set up. + + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); + if (os) { + nsITabChild* tc = + static_cast<nsITabChild*>(static_cast<TabChild*>(aActor)); + os->NotifyObservers(tc, "tab-child-created", nullptr); + } + + static bool hasRunOnce = false; + if (!hasRunOnce) { + hasRunOnce = true; + + MOZ_ASSERT(!sFirstIdleTask); + RefPtr<CancelableRunnable> firstIdleTask = NewCancelableRunnableFunction(FirstIdle); + sFirstIdleTask = firstIdleTask; + MessageLoop::current()->PostIdleTask(firstIdleTask.forget()); + + // Redo InitProcessAttributes() when the app or browser is really + // launching so the attributes will be correct. + mID = aCpID; + mIsForApp = aIsForApp; + mIsForBrowser = aIsForBrowser; + InitProcessAttributes(); + } + + return true; +} + +void +ContentChild::GetAvailableDictionaries(InfallibleTArray<nsString>& aDictionaries) +{ + aDictionaries = mAvailableDictionaries; +} + +PFileDescriptorSetChild* +ContentChild::SendPFileDescriptorSetConstructor(const FileDescriptor& aFD) +{ + if (IsShuttingDown()) { + return nullptr; + } + + return PContentChild::SendPFileDescriptorSetConstructor(aFD); +} + +PFileDescriptorSetChild* +ContentChild::AllocPFileDescriptorSetChild(const FileDescriptor& aFD) +{ + return nsIContentChild::AllocPFileDescriptorSetChild(aFD); +} + +bool +ContentChild::DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor) +{ + return nsIContentChild::DeallocPFileDescriptorSetChild(aActor); +} + +bool +ContentChild::DeallocPBrowserChild(PBrowserChild* aIframe) +{ + return nsIContentChild::DeallocPBrowserChild(aIframe); +} + +PBlobChild* +ContentChild::AllocPBlobChild(const BlobConstructorParams& aParams) +{ + return nsIContentChild::AllocPBlobChild(aParams); +} + +mozilla::PRemoteSpellcheckEngineChild * +ContentChild::AllocPRemoteSpellcheckEngineChild() +{ + MOZ_CRASH("Default Constructor for PRemoteSpellcheckEngineChild should never be called"); + return nullptr; +} + +bool +ContentChild::DeallocPRemoteSpellcheckEngineChild(PRemoteSpellcheckEngineChild *child) +{ + delete child; + return true; +} + +bool +ContentChild::DeallocPBlobChild(PBlobChild* aActor) +{ + return nsIContentChild::DeallocPBlobChild(aActor); +} + +PBlobChild* +ContentChild::SendPBlobConstructor(PBlobChild* aActor, + const BlobConstructorParams& aParams) +{ + if (IsShuttingDown()) { + return nullptr; + } + + return PContentChild::SendPBlobConstructor(aActor, aParams); +} + +PPresentationChild* +ContentChild::AllocPPresentationChild() +{ + MOZ_CRASH("We should never be manually allocating PPresentationChild actors"); + return nullptr; +} + +bool +ContentChild::DeallocPPresentationChild(PPresentationChild* aActor) +{ + delete aActor; + return true; +} + +PFlyWebPublishedServerChild* +ContentChild::AllocPFlyWebPublishedServerChild(const nsString& name, + const FlyWebPublishOptions& params) +{ + MOZ_CRASH("We should never be manually allocating PFlyWebPublishedServerChild actors"); + return nullptr; +} + +bool +ContentChild::DeallocPFlyWebPublishedServerChild(PFlyWebPublishedServerChild* aActor) +{ + RefPtr<FlyWebPublishedServerChild> actor = + dont_AddRef(static_cast<FlyWebPublishedServerChild*>(aActor)); + return true; +} + +bool +ContentChild::RecvNotifyPresentationReceiverLaunched(PBrowserChild* aIframe, + const nsString& aSessionId) +{ + nsCOMPtr<nsIDocShell> docShell = + do_GetInterface(static_cast<TabChild*>(aIframe)->WebNavigation()); + NS_WARNING_ASSERTION(docShell, "WebNavigation failed"); + + nsCOMPtr<nsIPresentationService> service = + do_GetService(PRESENTATION_SERVICE_CONTRACTID); + NS_WARNING_ASSERTION(service, "presentation service is missing"); + + Unused << NS_WARN_IF(NS_FAILED(static_cast<PresentationIPCService*>(service.get())->MonitorResponderLoading(aSessionId, docShell))); + + return true; +} + +bool +ContentChild::RecvNotifyPresentationReceiverCleanUp(const nsString& aSessionId) +{ + nsCOMPtr<nsIPresentationService> service = + do_GetService(PRESENTATION_SERVICE_CONTRACTID); + NS_WARNING_ASSERTION(service, "presentation service is missing"); + + Unused << NS_WARN_IF(NS_FAILED(service->UntrackSessionInfo(aSessionId, nsIPresentationService::ROLE_RECEIVER))); + + return true; +} + +bool +ContentChild::RecvNotifyEmptyHTTPCache() +{ + MOZ_ASSERT(NS_IsMainThread()); + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + obs->NotifyObservers(nullptr, "cacheservice:empty-cache", nullptr); + return true; +} + +PCrashReporterChild* +ContentChild::AllocPCrashReporterChild(const mozilla::dom::NativeThreadId& id, + const uint32_t& processType) +{ +#ifdef MOZ_CRASHREPORTER + return new CrashReporterChild(); +#else + return nullptr; +#endif +} + +bool +ContentChild::DeallocPCrashReporterChild(PCrashReporterChild* crashreporter) +{ + delete crashreporter; + return true; +} + +PHalChild* +ContentChild::AllocPHalChild() +{ + return CreateHalChild(); +} + +bool +ContentChild::DeallocPHalChild(PHalChild* aHal) +{ + delete aHal; + return true; +} + +devtools::PHeapSnapshotTempFileHelperChild* +ContentChild::AllocPHeapSnapshotTempFileHelperChild() +{ + return devtools::HeapSnapshotTempFileHelperChild::Create(); +} + +bool +ContentChild::DeallocPHeapSnapshotTempFileHelperChild( + devtools::PHeapSnapshotTempFileHelperChild* aHeapSnapshotHelper) +{ + delete aHeapSnapshotHelper; + return true; +} + +PTestShellChild* +ContentChild::AllocPTestShellChild() +{ + return new TestShellChild(); +} + +bool +ContentChild::DeallocPTestShellChild(PTestShellChild* shell) +{ + delete shell; + return true; +} + +jsipc::CPOWManager* +ContentChild::GetCPOWManager() +{ + if (PJavaScriptChild* c = LoneManagedOrNullAsserts(ManagedPJavaScriptChild())) { + return CPOWManagerFor(c); + } + return CPOWManagerFor(SendPJavaScriptConstructor()); +} + +bool +ContentChild::RecvPTestShellConstructor(PTestShellChild* actor) +{ + return true; +} + +PNeckoChild* +ContentChild::AllocPNeckoChild() +{ + return new NeckoChild(); +} + +bool +ContentChild::DeallocPNeckoChild(PNeckoChild* necko) +{ + delete necko; + return true; +} + +PPrintingChild* +ContentChild::AllocPPrintingChild() +{ + // The ContentParent should never attempt to allocate the nsPrintingProxy, + // which implements PPrintingChild. Instead, the nsPrintingProxy service is + // requested and instantiated via XPCOM, and the constructor of + // nsPrintingProxy sets up the IPC connection. + MOZ_CRASH("Should never get here!"); + return nullptr; +} + +bool +ContentChild::DeallocPPrintingChild(PPrintingChild* printing) +{ + return true; +} + +PSendStreamChild* +ContentChild::SendPSendStreamConstructor(PSendStreamChild* aActor) +{ + if (IsShuttingDown()) { + return nullptr; + } + + return PContentChild::SendPSendStreamConstructor(aActor); +} + +PSendStreamChild* +ContentChild::AllocPSendStreamChild() +{ + return nsIContentChild::AllocPSendStreamChild(); +} + +bool +ContentChild::DeallocPSendStreamChild(PSendStreamChild* aActor) +{ + return nsIContentChild::DeallocPSendStreamChild(aActor); +} + +PScreenManagerChild* +ContentChild::AllocPScreenManagerChild(uint32_t* aNumberOfScreens, + float* aSystemDefaultScale, + bool* aSuccess) +{ + // The ContentParent should never attempt to allocate the + // nsScreenManagerProxy. Instead, the nsScreenManagerProxy + // service is requested and instantiated via XPCOM, and the + // constructor of nsScreenManagerProxy sets up the IPC connection. + MOZ_CRASH("Should never get here!"); + return nullptr; +} + +bool +ContentChild::DeallocPScreenManagerChild(PScreenManagerChild* aService) +{ + // nsScreenManagerProxy is AddRef'd in its constructor. + nsScreenManagerProxy *child = static_cast<nsScreenManagerProxy*>(aService); + child->Release(); + return true; +} + +PPSMContentDownloaderChild* +ContentChild::AllocPPSMContentDownloaderChild(const uint32_t& aCertType) +{ + // NB: We don't need aCertType in the child actor. + RefPtr<PSMContentDownloaderChild> child = new PSMContentDownloaderChild(); + return child.forget().take(); +} + +bool +ContentChild::DeallocPPSMContentDownloaderChild(PPSMContentDownloaderChild* aListener) +{ + auto* listener = static_cast<PSMContentDownloaderChild*>(aListener); + RefPtr<PSMContentDownloaderChild> child = dont_AddRef(listener); + return true; +} + +PExternalHelperAppChild* +ContentChild::AllocPExternalHelperAppChild(const OptionalURIParams& uri, + const nsCString& aMimeContentType, + const nsCString& aContentDisposition, + const uint32_t& aContentDispositionHint, + const nsString& aContentDispositionFilename, + const bool& aForceSave, + const int64_t& aContentLength, + const bool& aWasFileChannel, + const OptionalURIParams& aReferrer, + PBrowserChild* aBrowser) +{ + ExternalHelperAppChild *child = new ExternalHelperAppChild(); + child->AddRef(); + return child; +} + +bool +ContentChild::DeallocPExternalHelperAppChild(PExternalHelperAppChild* aService) +{ + ExternalHelperAppChild *child = static_cast<ExternalHelperAppChild*>(aService); + child->Release(); + return true; +} + +PHandlerServiceChild* +ContentChild::AllocPHandlerServiceChild() +{ + HandlerServiceChild* actor = new HandlerServiceChild(); + actor->AddRef(); + return actor; +} + +bool ContentChild::DeallocPHandlerServiceChild(PHandlerServiceChild* aHandlerServiceChild) +{ + static_cast<HandlerServiceChild*>(aHandlerServiceChild)->Release(); + return true; +} + +media::PMediaChild* +ContentChild::AllocPMediaChild() +{ + return media::AllocPMediaChild(); +} + +bool +ContentChild::DeallocPMediaChild(media::PMediaChild *aActor) +{ + return media::DeallocPMediaChild(aActor); +} + +PStorageChild* +ContentChild::AllocPStorageChild() +{ + MOZ_CRASH("We should never be manually allocating PStorageChild actors"); + return nullptr; +} + +bool +ContentChild::DeallocPStorageChild(PStorageChild* aActor) +{ + DOMStorageDBChild* child = static_cast<DOMStorageDBChild*>(aActor); + child->ReleaseIPDLReference(); + return true; +} + +PSpeechSynthesisChild* +ContentChild::AllocPSpeechSynthesisChild() +{ +#ifdef MOZ_WEBSPEECH + MOZ_CRASH("No one should be allocating PSpeechSynthesisChild actors"); +#else + return nullptr; +#endif +} + +bool +ContentChild::DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor) +{ +#ifdef MOZ_WEBSPEECH + delete aActor; + return true; +#else + return false; +#endif +} + +PWebrtcGlobalChild * +ContentChild::AllocPWebrtcGlobalChild() +{ +#ifdef MOZ_WEBRTC + WebrtcGlobalChild *child = new WebrtcGlobalChild(); + return child; +#else + return nullptr; +#endif +} + +bool +ContentChild::DeallocPWebrtcGlobalChild(PWebrtcGlobalChild *aActor) +{ +#ifdef MOZ_WEBRTC + delete static_cast<WebrtcGlobalChild*>(aActor); + return true; +#else + return false; +#endif +} + + +bool +ContentChild::RecvRegisterChrome(InfallibleTArray<ChromePackage>&& packages, + InfallibleTArray<SubstitutionMapping>&& resources, + InfallibleTArray<OverrideMapping>&& overrides, + const nsCString& locale, + const bool& reset) +{ + nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService(); + nsChromeRegistryContent* chromeRegistry = + static_cast<nsChromeRegistryContent*>(registrySvc.get()); + chromeRegistry->RegisterRemoteChrome(packages, resources, overrides, + locale, reset); + return true; +} + +bool +ContentChild::RecvRegisterChromeItem(const ChromeRegistryItem& item) +{ + nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService(); + nsChromeRegistryContent* chromeRegistry = + static_cast<nsChromeRegistryContent*>(registrySvc.get()); + switch (item.type()) { + case ChromeRegistryItem::TChromePackage: + chromeRegistry->RegisterPackage(item.get_ChromePackage()); + break; + + case ChromeRegistryItem::TOverrideMapping: + chromeRegistry->RegisterOverride(item.get_OverrideMapping()); + break; + + case ChromeRegistryItem::TSubstitutionMapping: + chromeRegistry->RegisterSubstitution(item.get_SubstitutionMapping()); + break; + + default: + MOZ_ASSERT(false, "bad chrome item"); + return false; + } + + return true; +} + +bool +ContentChild::RecvClearImageCache(const bool& privateLoader, const bool& chrome) +{ + imgLoader* loader = privateLoader ? imgLoader::PrivateBrowsingLoader() : + imgLoader::NormalLoader(); + + loader->ClearCache(chrome); + return true; +} + +bool +ContentChild::RecvSetOffline(const bool& offline) +{ + nsCOMPtr<nsIIOService> io (do_GetIOService()); + NS_ASSERTION(io, "IO Service can not be null"); + + io->SetOffline(offline); + + return true; +} + +bool +ContentChild::RecvSetConnectivity(const bool& connectivity) +{ + nsCOMPtr<nsIIOService> io(do_GetIOService()); + nsCOMPtr<nsIIOServiceInternal> ioInternal(do_QueryInterface(io)); + NS_ASSERTION(ioInternal, "IO Service can not be null"); + + ioInternal->SetConnectivity(connectivity); + + return true; +} + +bool +ContentChild::RecvSetCaptivePortalState(const int32_t& aState) +{ + nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID); + if (!cps) { + return true; + } + + mozilla::net::CaptivePortalService *portal = + static_cast<mozilla::net::CaptivePortalService*>(cps.get()); + portal->SetStateInChild(aState); + + return true; +} + +void +ContentChild::ActorDestroy(ActorDestroyReason why) +{ + if (mForceKillTimer) { + mForceKillTimer->Cancel(); + mForceKillTimer = nullptr; + } + + if (AbnormalShutdown == why) { + NS_WARNING("shutting down early because of crash!"); + ProcessChild::QuickExit(); + } + +#ifndef NS_FREE_PERMANENT_DATA + // In release builds, there's no point in the content process + // going through the full XPCOM shutdown path, because it doesn't + // keep persistent state. + ProcessChild::QuickExit(); +#else + if (sFirstIdleTask) { + sFirstIdleTask->Cancel(); + } + + nsHostObjectProtocolHandler::RemoveDataEntries(); + + mAlertObservers.Clear(); + + mIdleObservers.Clear(); + + nsCOMPtr<nsIConsoleService> svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID)); + if (svc) { + svc->UnregisterListener(mConsoleListener); + mConsoleListener->mChild = nullptr; + } + mIsAlive = false; + + XRE_ShutdownChildProcess(); +#endif // NS_FREE_PERMANENT_DATA +} + +void +ContentChild::ProcessingError(Result aCode, const char* aReason) +{ + switch (aCode) { + case MsgDropped: + NS_WARNING("MsgDropped in ContentChild"); + return; + + case MsgNotKnown: + case MsgNotAllowed: + case MsgPayloadError: + case MsgProcessingError: + case MsgRouteError: + case MsgValueError: + break; + + default: + NS_RUNTIMEABORT("not reached"); + } + +#if defined(MOZ_CRASHREPORTER) && !defined(MOZ_B2G) + if (PCrashReporterChild* c = LoneManagedOrNullAsserts(ManagedPCrashReporterChild())) { + CrashReporterChild* crashReporter = + static_cast<CrashReporterChild*>(c); + nsDependentCString reason(aReason); + crashReporter->SendAnnotateCrashReport( + NS_LITERAL_CSTRING("ipc_channel_error"), + reason); + } +#endif + NS_RUNTIMEABORT("Content child abort due to IPC error"); +} + +nsresult +ContentChild::AddRemoteAlertObserver(const nsString& aData, + nsIObserver* aObserver) +{ + NS_ASSERTION(aObserver, "Adding a null observer?"); + mAlertObservers.AppendElement(new AlertObserver(aObserver, aData)); + return NS_OK; +} + +bool +ContentChild::RecvPreferenceUpdate(const PrefSetting& aPref) +{ + Preferences::SetPreference(aPref); + return true; +} + +bool +ContentChild::RecvVarUpdate(const GfxVarUpdate& aVar) +{ + gfx::gfxVars::ApplyUpdate(aVar); + return true; +} + +bool +ContentChild::RecvDataStoragePut(const nsString& aFilename, + const DataStorageItem& aItem) +{ + RefPtr<DataStorage> storage = DataStorage::GetIfExists(aFilename); + if (storage) { + storage->Put(aItem.key(), aItem.value(), aItem.type()); + } + return true; +} + +bool +ContentChild::RecvDataStorageRemove(const nsString& aFilename, + const nsCString& aKey, + const DataStorageType& aType) +{ + RefPtr<DataStorage> storage = DataStorage::GetIfExists(aFilename); + if (storage) { + storage->Remove(aKey, aType); + } + return true; +} + +bool +ContentChild::RecvDataStorageClear(const nsString& aFilename) +{ + RefPtr<DataStorage> storage = DataStorage::GetIfExists(aFilename); + if (storage) { + storage->Clear(); + } + return true; +} + +bool +ContentChild::RecvNotifyAlertsObserver(const nsCString& aType, const nsString& aData) +{ + for (uint32_t i = 0; i < mAlertObservers.Length(); + /*we mutate the array during the loop; ++i iff no mutation*/) { + AlertObserver* observer = mAlertObservers[i]; + if (observer->Observes(aData) && observer->Notify(aType)) { + // if aType == alertfinished, this alert is done. we can + // remove the observer. + if (aType.Equals(nsDependentCString("alertfinished"))) { + mAlertObservers.RemoveElementAt(i); + continue; + } + } + ++i; + } + return true; +} + +bool +ContentChild::RecvNotifyVisited(const URIParams& aURI) +{ + nsCOMPtr<nsIURI> newURI = DeserializeURI(aURI); + if (!newURI) { + return false; + } + nsCOMPtr<IHistory> history = services::GetHistoryService(); + if (history) { + history->NotifyVisited(newURI); + } + return true; +} + +bool +ContentChild::RecvLoadProcessScript(const nsString& aURL) +{ + ProcessGlobal* global = ProcessGlobal::Get(); + global->LoadScript(aURL); + return true; +} + +bool +ContentChild::RecvAsyncMessage(const nsString& aMsg, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData) +{ + RefPtr<nsFrameMessageManager> cpm = + nsFrameMessageManager::GetChildProcessManager(); + if (cpm) { + StructuredCloneData data; + ipc::UnpackClonedMessageDataForChild(aData, data); + CrossProcessCpowHolder cpows(this, aCpows); + cpm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(cpm.get()), + nullptr, aMsg, false, &data, &cpows, aPrincipal, + nullptr); + } + return true; +} + +bool +ContentChild::RecvGeolocationUpdate(const GeoPosition& somewhere) +{ + nsCOMPtr<nsIGeolocationUpdate> gs = + do_GetService("@mozilla.org/geolocation/service;1"); + if (!gs) { + return true; + } + nsCOMPtr<nsIDOMGeoPosition> position = somewhere; + gs->Update(position); + return true; +} + +bool +ContentChild::RecvGeolocationError(const uint16_t& errorCode) +{ + nsCOMPtr<nsIGeolocationUpdate> gs = + do_GetService("@mozilla.org/geolocation/service;1"); + if (!gs) { + return true; + } + gs->NotifyError(errorCode); + return true; +} + +bool +ContentChild::RecvUpdateDictionaryList(InfallibleTArray<nsString>&& aDictionaries) +{ + mAvailableDictionaries = aDictionaries; + mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking(); + return true; +} + +bool +ContentChild::RecvAddPermission(const IPC::Permission& permission) +{ +#if MOZ_PERMISSIONS + nsCOMPtr<nsIPermissionManager> permissionManagerIface = + services::GetPermissionManager(); + nsPermissionManager* permissionManager = + static_cast<nsPermissionManager*>(permissionManagerIface.get()); + MOZ_ASSERT(permissionManager, + "We have no permissionManager in the Content process !"); + + // note we do not need to force mUserContextId to the default here because + // the permission manager does that internally. + nsAutoCString originNoSuffix; + PrincipalOriginAttributes attrs; + bool success = attrs.PopulateFromOrigin(permission.origin, originNoSuffix); + NS_ENSURE_TRUE(success, false); + + nsCOMPtr<nsIURI> uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix); + NS_ENSURE_SUCCESS(rv, true); + + nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs); + + // child processes don't care about modification time. + int64_t modificationTime = 0; + + permissionManager->AddInternal(principal, + nsCString(permission.type), + permission.capability, + 0, + permission.expireType, + permission.expireTime, + modificationTime, + nsPermissionManager::eNotify, + nsPermissionManager::eNoDBOperation); +#endif + + return true; +} + +bool +ContentChild::RecvFlushMemory(const nsString& reason) +{ + nsCOMPtr<nsIObserverService> os = + mozilla::services::GetObserverService(); + if (os) { + os->NotifyObservers(nullptr, "memory-pressure", reason.get()); + } + return true; +} + +bool +ContentChild::RecvActivateA11y(const uint32_t& aMsaaID) +{ +#ifdef ACCESSIBILITY +#ifdef XP_WIN + MOZ_ASSERT(aMsaaID != 0); + mMsaaID = aMsaaID; +#endif // XP_WIN + + // Start accessibility in content process if it's running in chrome + // process. + GetOrCreateAccService(nsAccessibilityService::eMainProcess); +#endif // ACCESSIBILITY + return true; +} + +bool +ContentChild::RecvShutdownA11y() +{ +#ifdef ACCESSIBILITY + // Try to shutdown accessibility in content process if it's shutting down in + // chrome process. + MaybeShutdownAccService(nsAccessibilityService::eMainProcess); +#endif + return true; +} + +bool +ContentChild::RecvGarbageCollect() +{ + // Rebroadcast the "child-gc-request" so that workers will GC. + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nullptr, "child-gc-request", nullptr); + } + nsJSContext::GarbageCollectNow(JS::gcreason::DOM_IPC); + return true; +} + +bool +ContentChild::RecvCycleCollect() +{ + // Rebroadcast the "child-cc-request" so that workers will CC. + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nullptr, "child-cc-request", nullptr); + } + nsJSContext::CycleCollectNow(); + return true; +} + +static void +PreloadSlowThings() +{ + // This fetches and creates all the built-in stylesheets. + // + // XXXheycam In the future we might want to preload the Servo-flavoured + // UA sheets too, but for now that will be a waste of time. + nsLayoutStylesheetCache::For(StyleBackendType::Gecko)->UserContentSheet(); + + TabChild::PreloadSlowThings(); + +} + +bool +ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID, + const nsCString& name, const nsCString& UAName, + const nsCString& ID, const nsCString& vendor) +{ + mAppInfo.version.Assign(version); + mAppInfo.buildID.Assign(buildID); + mAppInfo.name.Assign(name); + mAppInfo.UAName.Assign(UAName); + mAppInfo.ID.Assign(ID); + mAppInfo.vendor.Assign(vendor); + + return true; +} + +bool +ContentChild::RecvAppInit() +{ + if (!Preferences::GetBool("dom.ipc.processPrelaunch.enabled", false)) { + return true; + } + + // If we're part of the mozbrowser machinery, go ahead and start + // preloading things. We can only do this for mozbrowser because + // PreloadSlowThings() may set the docshell of the first TabChild + // inactive, and we can only safely restore it to active from + // BrowserElementChild.js. + if (mIsForApp || mIsForBrowser) { + PreloadSlowThings(); + } + + return true; +} + +bool +ContentChild::RecvInitServiceWorkers(const ServiceWorkerConfiguration& aConfig) +{ + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + if (!swm) { + // browser shutdown began + return true; + } + swm->LoadRegistrations(aConfig.serviceWorkerRegistrations()); + return true; +} + +bool +ContentChild::RecvInitBlobURLs(nsTArray<BlobURLRegistrationData>&& aRegistrations) +{ + for (uint32_t i = 0; i < aRegistrations.Length(); ++i) { + BlobURLRegistrationData& registration = aRegistrations[i]; + RefPtr<BlobImpl> blobImpl = + static_cast<BlobChild*>(registration.blobChild())->GetBlobImpl(); + MOZ_ASSERT(blobImpl); + + nsHostObjectProtocolHandler::AddDataEntry(registration.url(), + registration.principal(), + blobImpl); + } + + return true; +} + +bool +ContentChild::RecvLastPrivateDocShellDestroyed() +{ + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr); + return true; +} + +bool +ContentChild::RecvVolumes(nsTArray<VolumeInfo>&& aVolumes) +{ +#ifdef MOZ_WIDGET_GONK + RefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton(); + if (vs) { + vs->RecvVolumesFromParent(aVolumes); + } +#endif + return true; +} + +bool +ContentChild::RecvFileSystemUpdate(const nsString& aFsName, + const nsString& aVolumeName, + const int32_t& aState, + const int32_t& aMountGeneration, + const bool& aIsMediaPresent, + const bool& aIsSharing, + const bool& aIsFormatting, + const bool& aIsFake, + const bool& aIsUnmounting, + const bool& aIsRemovable, + const bool& aIsHotSwappable) +{ +#ifdef MOZ_WIDGET_GONK + RefPtr<nsVolume> volume = new nsVolume(aFsName, aVolumeName, aState, + aMountGeneration, aIsMediaPresent, + aIsSharing, aIsFormatting, aIsFake, + aIsUnmounting, aIsRemovable, aIsHotSwappable); + + RefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton(); + if (vs) { + vs->UpdateVolume(volume); + } +#else + // Remove warnings about unused arguments + Unused << aFsName; + Unused << aVolumeName; + Unused << aState; + Unused << aMountGeneration; + Unused << aIsMediaPresent; + Unused << aIsSharing; + Unused << aIsFormatting; + Unused << aIsFake; + Unused << aIsUnmounting; + Unused << aIsRemovable; + Unused << aIsHotSwappable; +#endif + return true; +} + +bool +ContentChild::RecvVolumeRemoved(const nsString& aFsName) +{ +#ifdef MOZ_WIDGET_GONK + RefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton(); + if (vs) { + vs->RemoveVolumeByName(aFsName); + } +#else + // Remove warnings about unused arguments + Unused << aFsName; +#endif + return true; +} + +bool +ContentChild::RecvNotifyProcessPriorityChanged( + const hal::ProcessPriority& aPriority) +{ + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); + NS_ENSURE_TRUE(os, true); + + RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag(); + props->SetPropertyAsInt32(NS_LITERAL_STRING("priority"), + static_cast<int32_t>(aPriority)); + + os->NotifyObservers(static_cast<nsIPropertyBag2*>(props), + "ipc:process-priority-changed", nullptr); + return true; +} + +bool +ContentChild::RecvMinimizeMemoryUsage() +{ + nsCOMPtr<nsIMemoryReporterManager> mgr = + do_GetService("@mozilla.org/memory-reporter-manager;1"); + NS_ENSURE_TRUE(mgr, true); + + Unused << mgr->MinimizeMemoryUsage(/* callback = */ nullptr); + return true; +} + +bool +ContentChild::RecvNotifyPhoneStateChange(const nsString& aState) +{ + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); + if (os) { + os->NotifyObservers(nullptr, "phone-state-changed", aState.get()); + } + return true; +} + +void +ContentChild::AddIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS) +{ + MOZ_ASSERT(aObserver, "null idle observer"); + // Make sure aObserver isn't released while we wait for the parent + aObserver->AddRef(); + SendAddIdleObserver(reinterpret_cast<uint64_t>(aObserver), aIdleTimeInS); + mIdleObservers.PutEntry(aObserver); +} + +void +ContentChild::RemoveIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS) +{ + MOZ_ASSERT(aObserver, "null idle observer"); + SendRemoveIdleObserver(reinterpret_cast<uint64_t>(aObserver), aIdleTimeInS); + aObserver->Release(); + mIdleObservers.RemoveEntry(aObserver); +} + +bool +ContentChild::RecvNotifyIdleObserver(const uint64_t& aObserver, + const nsCString& aTopic, + const nsString& aTimeStr) +{ + nsIObserver* observer = reinterpret_cast<nsIObserver*>(aObserver); + if (mIdleObservers.Contains(observer)) { + observer->Observe(nullptr, aTopic.get(), aTimeStr.get()); + } else { + NS_WARNING("Received notification for an idle observer that was removed."); + } + return true; +} + +bool +ContentChild::RecvLoadAndRegisterSheet(const URIParams& aURI, const uint32_t& aType) +{ + nsCOMPtr<nsIURI> uri = DeserializeURI(aURI); + if (!uri) { + return true; + } + + nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance(); + if (sheetService) { + sheetService->LoadAndRegisterSheet(uri, aType); + } + + return true; +} + +bool +ContentChild::RecvUnregisterSheet(const URIParams& aURI, const uint32_t& aType) +{ + nsCOMPtr<nsIURI> uri = DeserializeURI(aURI); + if (!uri) { + return true; + } + + nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance(); + if (sheetService) { + sheetService->UnregisterSheet(uri, aType); + } + + return true; +} + +POfflineCacheUpdateChild* +ContentChild::AllocPOfflineCacheUpdateChild(const URIParams& manifestURI, + const URIParams& documentURI, + const PrincipalInfo& aLoadingPrincipalInfo, + const bool& stickDocument) +{ + NS_RUNTIMEABORT("unused"); + return nullptr; +} + +bool +ContentChild::DeallocPOfflineCacheUpdateChild(POfflineCacheUpdateChild* actor) +{ + OfflineCacheUpdateChild* offlineCacheUpdate = + static_cast<OfflineCacheUpdateChild*>(actor); + NS_RELEASE(offlineCacheUpdate); + return true; +} + +bool +ContentChild::RecvStartProfiler(const ProfilerInitParams& params) +{ + nsTArray<const char*> featureArray; + for (size_t i = 0; i < params.features().Length(); ++i) { + featureArray.AppendElement(params.features()[i].get()); + } + + nsTArray<const char*> threadNameFilterArray; + for (size_t i = 0; i < params.threadFilters().Length(); ++i) { + threadNameFilterArray.AppendElement(params.threadFilters()[i].get()); + } + + profiler_start(params.entries(), params.interval(), + featureArray.Elements(), featureArray.Length(), + threadNameFilterArray.Elements(), + threadNameFilterArray.Length()); + + return true; +} + +bool +ContentChild::RecvStopProfiler() +{ + profiler_stop(); + return true; +} + +bool +ContentChild::RecvPauseProfiler(const bool& aPause) +{ + if (aPause) { + profiler_pause(); + } else { + profiler_resume(); + } + + return true; +} + +bool +ContentChild::RecvGatherProfile() +{ + nsCString profileCString; + UniquePtr<char[]> profile = profiler_get_profile(); + if (profile) { + profileCString = nsCString(profile.get(), strlen(profile.get())); + } else { + profileCString = EmptyCString(); + } + + Unused << SendProfile(profileCString); + return true; +} + +bool +ContentChild::RecvLoadPluginResult(const uint32_t& aPluginId, + const bool& aResult) +{ + nsresult rv; + bool finalResult = aResult && SendConnectPluginBridge(aPluginId, &rv) && + NS_SUCCEEDED(rv); + plugins::PluginModuleContentParent::OnLoadPluginResult(aPluginId, + finalResult); + return true; +} + +bool +ContentChild::RecvAssociatePluginId(const uint32_t& aPluginId, + const base::ProcessId& aProcessId) +{ + plugins::PluginModuleContentParent::AssociatePluginId(aPluginId, aProcessId); + return true; +} + +bool +ContentChild::RecvDomainSetChanged(const uint32_t& aSetType, + const uint32_t& aChangeType, + const OptionalURIParams& aDomain) +{ + if (aChangeType == ACTIVATE_POLICY) { + if (mPolicy) { + return true; + } + nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); + MOZ_ASSERT(ssm); + ssm->ActivateDomainPolicyInternal(getter_AddRefs(mPolicy)); + return !!mPolicy; + } else if (!mPolicy) { + MOZ_ASSERT_UNREACHABLE("If the domain policy is not active yet," + " the first message should be ACTIVATE_POLICY"); + return false; + } + + NS_ENSURE_TRUE(mPolicy, false); + + if (aChangeType == DEACTIVATE_POLICY) { + mPolicy->Deactivate(); + mPolicy = nullptr; + return true; + } + + nsCOMPtr<nsIDomainSet> set; + switch(aSetType) { + case BLACKLIST: + mPolicy->GetBlacklist(getter_AddRefs(set)); + break; + case SUPER_BLACKLIST: + mPolicy->GetSuperBlacklist(getter_AddRefs(set)); + break; + case WHITELIST: + mPolicy->GetWhitelist(getter_AddRefs(set)); + break; + case SUPER_WHITELIST: + mPolicy->GetSuperWhitelist(getter_AddRefs(set)); + break; + default: + NS_NOTREACHED("Unexpected setType"); + return false; + } + + MOZ_ASSERT(set); + + nsCOMPtr<nsIURI> uri = DeserializeURI(aDomain); + + switch(aChangeType) { + case ADD_DOMAIN: + NS_ENSURE_TRUE(uri, false); + set->Add(uri); + break; + case REMOVE_DOMAIN: + NS_ENSURE_TRUE(uri, false); + set->Remove(uri); + break; + case CLEAR_DOMAINS: + set->Clear(); + break; + default: + NS_NOTREACHED("Unexpected changeType"); + return false; + } + + return true; +} + +void +ContentChild::StartForceKillTimer() +{ + if (mForceKillTimer) { + 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(ContentChild::ForceKillTimerCallback, + this, + timeoutSecs * 1000, + nsITimer::TYPE_ONE_SHOT); + } +} + +/* static */ void +ContentChild::ForceKillTimerCallback(nsITimer* aTimer, void* aClosure) +{ + ProcessChild::QuickExit(); +} + +bool +ContentChild::RecvShutdown() +{ + // If we receive the shutdown message from within a nested event loop, we want + // to wait for that event loop to finish. Otherwise we could prematurely + // terminate an "unload" or "pagehide" event handler (which might be doing a + // sync XHR, for example). +#if defined(MOZ_CRASHREPORTER) + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCShutdownState"), + NS_LITERAL_CSTRING("RecvShutdown")); +#endif + nsCOMPtr<nsIThread> thread; + nsresult rv = NS_GetMainThread(getter_AddRefs(thread)); + if (NS_SUCCEEDED(rv) && thread) { + RefPtr<nsThread> mainThread(thread.forget().downcast<nsThread>()); + if (mainThread->RecursionDepth() > 1) { + // We're in a nested event loop. Let's delay for an arbitrary period of + // time (100ms) in the hopes that the event loop will have finished by + // then. + MessageLoop::current()->PostDelayedTask( + NewRunnableMethod(this, &ContentChild::RecvShutdown), 100); + return true; + } + } + + mShuttingDown = true; + + if (mPolicy) { + mPolicy->Deactivate(); + mPolicy = nullptr; + } + + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); + if (os) { + os->NotifyObservers(static_cast<nsIContentChild*>(this), + "content-child-shutdown", nullptr); + } + +#if defined(XP_WIN) + mozilla::widget::StopAudioSession(); +#endif + + GetIPCChannel()->SetAbortOnError(false); + +#ifdef MOZ_ENABLE_PROFILER_SPS + if (profiler_is_active()) { + // We're shutting down while we were profiling. Send the + // profile up to the parent so that we don't lose this + // information. + Unused << RecvGatherProfile(); + } +#endif + + // Start a timer that will insure we quickly exit after a reasonable + // period of time. Prevents shutdown hangs after our connection to the + // parent closes. + StartForceKillTimer(); + +#if defined(MOZ_CRASHREPORTER) + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCShutdownState"), + NS_LITERAL_CSTRING("SendFinishShutdown")); +#endif + // Ignore errors here. If this fails, the parent will kill us after a + // timeout. + Unused << SendFinishShutdown(); + return true; +} + +PBrowserOrId +ContentChild::GetBrowserOrId(TabChild* aTabChild) +{ + if (!aTabChild || + this == aTabChild->Manager()) { + return PBrowserOrId(aTabChild); + } + else { + return PBrowserOrId(aTabChild->GetTabId()); + } +} + +bool +ContentChild::RecvUpdateWindow(const uintptr_t& aChildId) +{ +#if defined(XP_WIN) + NS_ASSERTION(aChildId, "Expected child hwnd value for remote plugin instance."); + mozilla::plugins::PluginInstanceParent* parentInstance = + mozilla::plugins::PluginInstanceParent::LookupPluginInstanceByID(aChildId); + if (parentInstance) { + // sync! update call to the plugin instance that forces the + // plugin to paint its child window. + parentInstance->CallUpdateWindow(); + } + return true; +#else + MOZ_ASSERT(false, "ContentChild::RecvUpdateWindow calls unexpected on this platform."); + return false; +#endif +} + +PContentPermissionRequestChild* +ContentChild::AllocPContentPermissionRequestChild(const InfallibleTArray<PermissionRequest>& aRequests, + const IPC::Principal& aPrincipal, + const TabId& aTabId) +{ + NS_RUNTIMEABORT("unused"); + return nullptr; +} + +bool +ContentChild::DeallocPContentPermissionRequestChild(PContentPermissionRequestChild* actor) +{ + nsContentPermissionUtils::NotifyRemoveContentPermissionRequestChild(actor); + auto child = static_cast<RemotePermissionRequest*>(actor); + child->IPDLRelease(); + return true; +} + +PWebBrowserPersistDocumentChild* +ContentChild::AllocPWebBrowserPersistDocumentChild(PBrowserChild* aBrowser, + const uint64_t& aOuterWindowID) +{ + return new WebBrowserPersistDocumentChild(); +} + +bool +ContentChild::RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor, + PBrowserChild* aBrowser, + const uint64_t& aOuterWindowID) +{ + if (NS_WARN_IF(!aBrowser)) { + return false; + } + nsCOMPtr<nsIDocument> rootDoc = + static_cast<TabChild*>(aBrowser)->GetDocument(); + nsCOMPtr<nsIDocument> foundDoc; + if (aOuterWindowID) { + foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc, aOuterWindowID); + } else { + foundDoc = rootDoc; + } + + if (!foundDoc) { + aActor->SendInitFailure(NS_ERROR_NO_CONTENT); + } else { + static_cast<WebBrowserPersistDocumentChild*>(aActor)->Start(foundDoc); + } + return true; +} + +bool +ContentChild::DeallocPWebBrowserPersistDocumentChild(PWebBrowserPersistDocumentChild* aActor) +{ + delete aActor; + return true; +} + +bool +ContentChild::RecvSetAudioSessionData(const nsID& aId, + const nsString& aDisplayName, + const nsString& aIconPath) +{ +#if defined(XP_WIN) + if (NS_FAILED(mozilla::widget::RecvAudioSessionData(aId, aDisplayName, + aIconPath))) { + return true; + } + + // Ignore failures here; we can't really do anything about them + mozilla::widget::StartAudioSession(); + return true; +#else + NS_RUNTIMEABORT("Not Reached!"); + return false; +#endif +} + +// This code goes here rather than nsGlobalWindow.cpp because nsGlobalWindow.cpp +// can't include ContentChild.h since it includes windows.h. + +static uint64_t gNextWindowID = 0; + +// We use only 53 bits for the window ID so that it can be converted to and from +// a JS value without loss of precision. The upper bits of the window ID hold the +// process ID. The lower bits identify the window. +static const uint64_t kWindowIDTotalBits = 53; +static const uint64_t kWindowIDProcessBits = 22; +static const uint64_t kWindowIDWindowBits = kWindowIDTotalBits - kWindowIDProcessBits; + +// Try to return a window ID that is unique across processes and that will never +// be recycled. +uint64_t +NextWindowID() +{ + uint64_t processID = 0; + if (XRE_IsContentProcess()) { + ContentChild* cc = ContentChild::GetSingleton(); + processID = cc->GetID(); + } + + MOZ_RELEASE_ASSERT(processID < (uint64_t(1) << kWindowIDProcessBits)); + uint64_t processBits = processID & ((uint64_t(1) << kWindowIDProcessBits) - 1); + + // Make sure no actual window ends up with mWindowID == 0. + uint64_t windowID = ++gNextWindowID; + + MOZ_RELEASE_ASSERT(windowID < (uint64_t(1) << kWindowIDWindowBits)); + uint64_t windowBits = windowID & ((uint64_t(1) << kWindowIDWindowBits) - 1); + + return (processBits << kWindowIDWindowBits) | windowBits; +} + +bool +ContentChild::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers, + const uint32_t& aAction) +{ + nsCOMPtr<nsIDragService> dragService = + do_GetService("@mozilla.org/widget/dragservice;1"); + if (dragService) { + dragService->StartDragSession(); + nsCOMPtr<nsIDragSession> session; + dragService->GetCurrentSession(getter_AddRefs(session)); + if (session) { + session->SetDragAction(aAction); + // Check if we are receiving any file objects. If we are we will want + // to hide any of the other objects coming in from content. + bool hasFiles = false; + for (uint32_t i = 0; i < aTransfers.Length() && !hasFiles; ++i) { + auto& items = aTransfers[i].items(); + for (uint32_t j = 0; j < items.Length() && !hasFiles; ++j) { + if (items[j].data().type() == IPCDataTransferData::TPBlobChild) { + hasFiles = true; + } + } + } + + // Add the entries from the IPC to the new DataTransfer + nsCOMPtr<DataTransfer> dataTransfer = + new DataTransfer(nullptr, eDragStart, false, -1); + for (uint32_t i = 0; i < aTransfers.Length(); ++i) { + auto& items = aTransfers[i].items(); + for (uint32_t j = 0; j < items.Length(); ++j) { + const IPCDataTransferItem& item = items[j]; + RefPtr<nsVariantCC> variant = new nsVariantCC(); + if (item.data().type() == IPCDataTransferData::TnsString) { + const nsString& data = item.data().get_nsString(); + variant->SetAsAString(data); + } else if (item.data().type() == IPCDataTransferData::TShmem) { + Shmem data = item.data().get_Shmem(); + variant->SetAsACString(nsDependentCString(data.get<char>(), data.Size<char>())); + Unused << DeallocShmem(data); + } else if (item.data().type() == IPCDataTransferData::TPBlobChild) { + BlobChild* blob = static_cast<BlobChild*>(item.data().get_PBlobChild()); + RefPtr<BlobImpl> blobImpl = blob->GetBlobImpl(); + variant->SetAsISupports(blobImpl); + } else { + continue; + } + // We should hide this data from content if we have a file, and we aren't a file. + bool hidden = hasFiles && item.data().type() != IPCDataTransferData::TPBlobChild; + dataTransfer->SetDataWithPrincipalFromOtherProcess( + NS_ConvertUTF8toUTF16(item.flavor()), variant, i, + nsContentUtils::GetSystemPrincipal(), hidden); + } + } + session->SetDataTransfer(dataTransfer); + } + } + return true; +} + +bool +ContentChild::RecvEndDragSession(const bool& aDoneDrag, + const bool& aUserCancelled, + const LayoutDeviceIntPoint& aDragEndPoint) +{ + nsCOMPtr<nsIDragService> dragService = + do_GetService("@mozilla.org/widget/dragservice;1"); + if (dragService) { + if (aUserCancelled) { + nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession(); + if (dragSession) { + dragSession->UserCancelled(); + } + } + static_cast<nsBaseDragService*>(dragService.get())->SetDragEndPoint(aDragEndPoint); + dragService->EndDragSession(aDoneDrag); + } + return true; +} + +bool +ContentChild::RecvPush(const nsCString& aScope, + const IPC::Principal& aPrincipal, + const nsString& aMessageId) +{ + PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing()); + Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers())); + return true; +} + +bool +ContentChild::RecvPushWithData(const nsCString& aScope, + const IPC::Principal& aPrincipal, + const nsString& aMessageId, + InfallibleTArray<uint8_t>&& aData) +{ + PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Some(aData)); + Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers())); + return true; +} + +bool +ContentChild::RecvPushSubscriptionChange(const nsCString& aScope, + const IPC::Principal& aPrincipal) +{ + PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal); + Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers())); + return true; +} + +bool +ContentChild::RecvPushError(const nsCString& aScope, const IPC::Principal& aPrincipal, + const nsString& aMessage, const uint32_t& aFlags) +{ + PushErrorDispatcher dispatcher(aScope, aPrincipal, aMessage, aFlags); + Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers())); + return true; +} + +bool +ContentChild::RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aScope, + const IPC::Principal& aPrincipal) +{ + PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal); + Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers())); + return true; +} + +bool +ContentChild::RecvBlobURLRegistration(const nsCString& aURI, PBlobChild* aBlobChild, + const IPC::Principal& aPrincipal) +{ + RefPtr<BlobImpl> blobImpl = static_cast<BlobChild*>(aBlobChild)->GetBlobImpl(); + MOZ_ASSERT(blobImpl); + + nsHostObjectProtocolHandler::AddDataEntry(aURI, aPrincipal, blobImpl); + return true; +} + +bool +ContentChild::RecvBlobURLUnregistration(const nsCString& aURI) +{ + nsHostObjectProtocolHandler::RemoveDataEntry(aURI); + return true; +} + +#if defined(XP_WIN) && defined(ACCESSIBILITY) +bool +ContentChild::SendGetA11yContentId() +{ + return PContentChild::SendGetA11yContentId(&mMsaaID); +} +#endif // defined(XP_WIN) && defined(ACCESSIBILITY) + +void +ContentChild::CreateGetFilesRequest(const nsAString& aDirectoryPath, + bool aRecursiveFlag, + nsID& aUUID, + GetFilesHelperChild* aChild) +{ + MOZ_ASSERT(aChild); + MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aUUID)); + + Unused << SendGetFilesRequest(aUUID, nsString(aDirectoryPath), + aRecursiveFlag); + mGetFilesPendingRequests.Put(aUUID, aChild); +} + +void +ContentChild::DeleteGetFilesRequest(nsID& aUUID, GetFilesHelperChild* aChild) +{ + MOZ_ASSERT(aChild); + MOZ_ASSERT(mGetFilesPendingRequests.GetWeak(aUUID)); + + Unused << SendDeleteGetFilesRequest(aUUID); + mGetFilesPendingRequests.Remove(aUUID); +} + +bool +ContentChild::RecvGetFilesResponse(const nsID& aUUID, + const GetFilesResponseResult& aResult) +{ + GetFilesHelperChild* child = mGetFilesPendingRequests.GetWeak(aUUID); + // This object can already been deleted in case DeleteGetFilesRequest has + // been called when the response was sending by the parent. + if (!child) { + return true; + } + + if (aResult.type() == GetFilesResponseResult::TGetFilesResponseFailure) { + child->Finished(aResult.get_GetFilesResponseFailure().errorCode()); + } else { + MOZ_ASSERT(aResult.type() == GetFilesResponseResult::TGetFilesResponseSuccess); + + const nsTArray<PBlobChild*>& blobs = + aResult.get_GetFilesResponseSuccess().blobsChild(); + + bool succeeded = true; + for (uint32_t i = 0; succeeded && i < blobs.Length(); ++i) { + RefPtr<BlobImpl> impl = static_cast<BlobChild*>(blobs[i])->GetBlobImpl(); + succeeded = child->AppendBlobImpl(impl); + } + + child->Finished(succeeded ? NS_OK : NS_ERROR_OUT_OF_MEMORY); + } + + mGetFilesPendingRequests.Remove(aUUID); + return true; +} + +/* static */ void +ContentChild::FatalErrorIfNotUsingGPUProcess(const char* const aProtocolName, + const char* const aErrorMsg, + base::ProcessId aOtherPid) +{ + // If we're communicating with the same process or the UI process then we + // want to crash normally. Otherwise we want to just warn as the other end + // must be the GPU process and it crashing shouldn't be fatal for us. + if (aOtherPid == base::GetCurrentProcId() || + (GetSingleton() && GetSingleton()->OtherPid() == aOtherPid)) { + mozilla::ipc::FatalError(aProtocolName, aErrorMsg, false); + } else { + nsAutoCString formattedMessage("IPDL error ["); + formattedMessage.AppendASCII(aProtocolName); + formattedMessage.AppendLiteral("]: \""); + formattedMessage.AppendASCII(aErrorMsg); + formattedMessage.AppendLiteral("\"."); + NS_WARNING(formattedMessage.get()); + } +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h new file mode 100644 index 000000000..cb718aff9 --- /dev/null +++ b/dom/ipc/ContentChild.h @@ -0,0 +1,702 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_ContentChild_h +#define mozilla_dom_ContentChild_h + +#include "mozilla/Attributes.h" +#include "mozilla/dom/ContentBridgeParent.h" +#include "mozilla/dom/nsIContentChild.h" +#include "mozilla/dom/PBrowserOrId.h" +#include "mozilla/dom/PContentChild.h" +#include "nsAutoPtr.h" +#include "nsHashKeys.h" +#include "nsIObserver.h" +#include "nsTHashtable.h" +#include "nsRefPtrHashtable.h" + +#include "nsWeakPtr.h" +#include "nsIWindowProvider.h" + +#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) +#include "nsIFile.h" +#endif + +struct ChromePackage; +class nsIObserver; +struct SubstitutionMapping; +struct OverrideMapping; +class nsIDomainPolicy; + +namespace mozilla { +class RemoteSpellcheckEngineChild; + +namespace ipc { +class OptionalURIParams; +class URIParams; +}// namespace ipc + +namespace dom { + +class AlertObserver; +class ConsoleListener; +class PStorageChild; +class ClonedMessageData; +class TabChild; +class GetFilesHelperChild; + +class ContentChild final : public PContentChild + , public nsIWindowProvider + , public nsIContentChild +{ + typedef mozilla::dom::ClonedMessageData ClonedMessageData; + typedef mozilla::ipc::FileDescriptor FileDescriptor; + typedef mozilla::ipc::OptionalURIParams OptionalURIParams; + typedef mozilla::ipc::PFileDescriptorSetChild PFileDescriptorSetChild; + typedef mozilla::ipc::URIParams URIParams; + +public: + NS_DECL_NSIWINDOWPROVIDER + + ContentChild(); + virtual ~ContentChild(); + NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override; + NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override { return 1; } + NS_IMETHOD_(MozExternalRefCountType) Release(void) override { return 1; } + + struct AppInfo + { + nsCString version; + nsCString buildID; + nsCString name; + nsCString UAName; + nsCString ID; + nsCString vendor; + }; + + nsresult + ProvideWindowCommon(TabChild* aTabOpener, + mozIDOMWindowProxy* aOpener, + bool aIframeMoz, + uint32_t aChromeFlags, + bool aCalledFromJS, + bool aPositionSpecified, + bool aSizeSpecified, + nsIURI* aURI, + const nsAString& aName, + const nsACString& aFeatures, + bool aForceNoOpener, + bool* aWindowIsNew, + mozIDOMWindowProxy** aReturn); + + bool Init(MessageLoop* aIOLoop, + base::ProcessId aParentPid, + IPC::Channel* aChannel); + + void InitProcessAttributes(); + + void InitXPCOM(); + + void InitGraphicsDeviceData(); + + static ContentChild* GetSingleton() + { + return sSingleton; + } + + const AppInfo& GetAppInfo() + { + return mAppInfo; + } + + void SetProcessName(const nsAString& aName, bool aDontOverride = false); + + void GetProcessName(nsAString& aName) const; + + void GetProcessName(nsACString& aName) const; + +#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) + void GetProfileDir(nsIFile** aProfileDir) const + { + *aProfileDir = mProfileDir; + NS_IF_ADDREF(*aProfileDir); + } + + void SetProfileDir(nsIFile* aProfileDir) + { + mProfileDir = aProfileDir; + } +#endif + + bool IsAlive() const; + + bool IsShuttingDown() const; + + static void AppendProcessId(nsACString& aName); + + ContentBridgeParent* GetLastBridge() + { + MOZ_ASSERT(mLastBridge); + ContentBridgeParent* parent = mLastBridge; + mLastBridge = nullptr; + return parent; + } + + RefPtr<ContentBridgeParent> mLastBridge; + + PPluginModuleParent * + AllocPPluginModuleParent(mozilla::ipc::Transport* transport, + base::ProcessId otherProcess) override; + + PContentBridgeParent* + AllocPContentBridgeParent(mozilla::ipc::Transport* transport, + base::ProcessId otherProcess) override; + PContentBridgeChild* + AllocPContentBridgeChild(mozilla::ipc::Transport* transport, + base::ProcessId otherProcess) override; + + PGMPServiceChild* + AllocPGMPServiceChild(mozilla::ipc::Transport* transport, + base::ProcessId otherProcess) override; + + bool + RecvGMPsChanged(nsTArray<GMPCapabilityData>&& capabilities) override; + + bool + RecvInitRendering( + Endpoint<PCompositorBridgeChild>&& aCompositor, + Endpoint<PImageBridgeChild>&& aImageBridge, + Endpoint<PVRManagerChild>&& aVRBridge, + Endpoint<PVideoDecoderManagerChild>&& aVideoManager) override; + + bool + RecvReinitRendering( + Endpoint<PCompositorBridgeChild>&& aCompositor, + Endpoint<PImageBridgeChild>&& aImageBridge, + Endpoint<PVRManagerChild>&& aVRBridge, + Endpoint<PVideoDecoderManagerChild>&& aVideoManager) override; + + PProcessHangMonitorChild* + AllocPProcessHangMonitorChild(Transport* aTransport, + ProcessId aOtherProcess) override; + + virtual bool RecvSetProcessSandbox(const MaybeFileDesc& aBroker) override; + + PBackgroundChild* + AllocPBackgroundChild(Transport* aTransport, ProcessId aOtherProcess) + override; + + virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) override; + + virtual bool DeallocPBrowserChild(PBrowserChild*) override; + + virtual PBlobChild* + AllocPBlobChild(const BlobConstructorParams& aParams) override; + + virtual bool DeallocPBlobChild(PBlobChild* aActor) override; + + virtual PCrashReporterChild* + AllocPCrashReporterChild(const mozilla::dom::NativeThreadId& id, + const uint32_t& processType) override; + + virtual bool + DeallocPCrashReporterChild(PCrashReporterChild*) override; + + virtual PHalChild* AllocPHalChild() override; + virtual bool DeallocPHalChild(PHalChild*) override; + + virtual PHeapSnapshotTempFileHelperChild* + AllocPHeapSnapshotTempFileHelperChild() override; + + virtual bool + DeallocPHeapSnapshotTempFileHelperChild(PHeapSnapshotTempFileHelperChild*) override; + + virtual PMemoryReportRequestChild* + AllocPMemoryReportRequestChild(const uint32_t& aGeneration, + const bool& aAnonymize, + const bool& aMinimizeMemoryUsage, + const MaybeFileDesc& aDMDFile) override; + + virtual bool + DeallocPMemoryReportRequestChild(PMemoryReportRequestChild* actor) override; + + virtual bool + RecvPMemoryReportRequestConstructor(PMemoryReportRequestChild* aChild, + const uint32_t& aGeneration, + const bool& aAnonymize, + const bool &aMinimizeMemoryUsage, + const MaybeFileDesc &aDMDFile) override; + + virtual PCycleCollectWithLogsChild* + AllocPCycleCollectWithLogsChild(const bool& aDumpAllTraces, + const FileDescriptor& aGCLog, + const FileDescriptor& aCCLog) override; + + virtual bool + DeallocPCycleCollectWithLogsChild(PCycleCollectWithLogsChild* aActor) override; + + virtual bool + RecvPCycleCollectWithLogsConstructor(PCycleCollectWithLogsChild* aChild, + const bool& aDumpAllTraces, + const FileDescriptor& aGCLog, + const FileDescriptor& aCCLog) override; + + virtual PWebBrowserPersistDocumentChild* + AllocPWebBrowserPersistDocumentChild(PBrowserChild* aBrowser, + const uint64_t& aOuterWindowID) override; + + virtual bool + RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor, + PBrowserChild *aBrowser, + const uint64_t& aOuterWindowID) override; + + virtual bool + DeallocPWebBrowserPersistDocumentChild(PWebBrowserPersistDocumentChild* aActor) override; + + virtual PTestShellChild* AllocPTestShellChild() override; + + virtual bool DeallocPTestShellChild(PTestShellChild*) override; + + virtual bool RecvPTestShellConstructor(PTestShellChild*) override; + + jsipc::CPOWManager* GetCPOWManager() override; + + virtual PNeckoChild* AllocPNeckoChild() override; + + virtual bool DeallocPNeckoChild(PNeckoChild*) override; + + virtual PPrintingChild* AllocPPrintingChild() override; + + virtual bool DeallocPPrintingChild(PPrintingChild*) override; + + virtual PSendStreamChild* + SendPSendStreamConstructor(PSendStreamChild*) override; + + virtual PSendStreamChild* AllocPSendStreamChild() override; + virtual bool DeallocPSendStreamChild(PSendStreamChild*) override; + + virtual PScreenManagerChild* + AllocPScreenManagerChild(uint32_t* aNumberOfScreens, + float* aSystemDefaultScale, + bool* aSuccess) override; + + virtual bool DeallocPScreenManagerChild(PScreenManagerChild*) override; + + virtual PPSMContentDownloaderChild* + AllocPPSMContentDownloaderChild( const uint32_t& aCertType) override; + + virtual bool + DeallocPPSMContentDownloaderChild(PPSMContentDownloaderChild* aDownloader) override; + + virtual PExternalHelperAppChild* + AllocPExternalHelperAppChild(const OptionalURIParams& uri, + const nsCString& aMimeContentType, + const nsCString& aContentDisposition, + const uint32_t& aContentDispositionHint, + const nsString& aContentDispositionFilename, + const bool& aForceSave, + const int64_t& aContentLength, + const bool& aWasFileChannel, + const OptionalURIParams& aReferrer, + PBrowserChild* aBrowser) override; + + virtual bool + DeallocPExternalHelperAppChild(PExternalHelperAppChild *aService) override; + + virtual PHandlerServiceChild* AllocPHandlerServiceChild() override; + + virtual bool DeallocPHandlerServiceChild(PHandlerServiceChild*) override; + + virtual PMediaChild* AllocPMediaChild() override; + + virtual bool DeallocPMediaChild(PMediaChild* aActor) override; + + virtual PStorageChild* AllocPStorageChild() override; + + virtual bool DeallocPStorageChild(PStorageChild* aActor) override; + + virtual PPresentationChild* AllocPPresentationChild() override; + + virtual bool DeallocPPresentationChild(PPresentationChild* aActor) override; + + virtual PFlyWebPublishedServerChild* + AllocPFlyWebPublishedServerChild(const nsString& name, + const FlyWebPublishOptions& params) override; + + virtual bool DeallocPFlyWebPublishedServerChild(PFlyWebPublishedServerChild* aActor) override; + + virtual bool + RecvNotifyPresentationReceiverLaunched(PBrowserChild* aIframe, + const nsString& aSessionId) override; + + virtual bool + RecvNotifyPresentationReceiverCleanUp(const nsString& aSessionId) override; + + virtual bool RecvNotifyEmptyHTTPCache() override; + + virtual PSpeechSynthesisChild* AllocPSpeechSynthesisChild() override; + + virtual bool DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor) override; + + virtual bool RecvRegisterChrome(InfallibleTArray<ChromePackage>&& packages, + InfallibleTArray<SubstitutionMapping>&& resources, + InfallibleTArray<OverrideMapping>&& overrides, + const nsCString& locale, + const bool& reset) override; + virtual bool RecvRegisterChromeItem(const ChromeRegistryItem& item) override; + + virtual bool RecvClearImageCache(const bool& privateLoader, + const bool& chrome) override; + + virtual mozilla::jsipc::PJavaScriptChild* AllocPJavaScriptChild() override; + + virtual bool DeallocPJavaScriptChild(mozilla::jsipc::PJavaScriptChild*) override; + + virtual PRemoteSpellcheckEngineChild* AllocPRemoteSpellcheckEngineChild() override; + + virtual bool DeallocPRemoteSpellcheckEngineChild(PRemoteSpellcheckEngineChild*) override; + + virtual bool RecvSetOffline(const bool& offline) override; + + virtual bool RecvSetConnectivity(const bool& connectivity) override; + virtual bool RecvSetCaptivePortalState(const int32_t& state) override; + + virtual bool RecvNotifyLayerAllocated(const dom::TabId& aTabId, const uint64_t& aLayersId) override; + + virtual bool RecvSpeakerManagerNotify() override; + + virtual bool RecvBidiKeyboardNotify(const bool& isLangRTL, + const bool& haveBidiKeyboards) override; + + virtual bool RecvNotifyVisited(const URIParams& aURI) override; + + // auto remove when alertfinished is received. + nsresult AddRemoteAlertObserver(const nsString& aData, nsIObserver* aObserver); + + virtual bool RecvPreferenceUpdate(const PrefSetting& aPref) override; + virtual bool RecvVarUpdate(const GfxVarUpdate& pref) override; + + virtual bool RecvDataStoragePut(const nsString& aFilename, + const DataStorageItem& aItem) override; + + virtual bool RecvDataStorageRemove(const nsString& aFilename, + const nsCString& aKey, + const DataStorageType& aType) override; + + virtual bool RecvDataStorageClear(const nsString& aFilename) override; + + virtual bool RecvNotifyAlertsObserver(const nsCString& aType, + const nsString& aData) override; + + virtual bool RecvLoadProcessScript(const nsString& aURL) override; + + virtual bool RecvAsyncMessage(const nsString& aMsg, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData) override; + + virtual bool RecvGeolocationUpdate(const GeoPosition& somewhere) override; + + virtual bool RecvGeolocationError(const uint16_t& errorCode) override; + + virtual bool RecvUpdateDictionaryList(InfallibleTArray<nsString>&& aDictionaries) override; + + virtual bool RecvAddPermission(const IPC::Permission& permission) override; + + virtual bool RecvFlushMemory(const nsString& reason) override; + + virtual bool RecvActivateA11y(const uint32_t& aMsaaID) override; + virtual bool RecvShutdownA11y() override; + + virtual bool RecvGarbageCollect() override; + virtual bool RecvCycleCollect() override; + + virtual bool RecvAppInfo(const nsCString& version, const nsCString& buildID, + const nsCString& name, const nsCString& UAName, + const nsCString& ID, const nsCString& vendor) override; + + virtual bool RecvAppInit() override; + + virtual bool + RecvInitServiceWorkers(const ServiceWorkerConfiguration& aConfig) override; + + virtual bool + RecvInitBlobURLs(nsTArray<BlobURLRegistrationData>&& aRegistations) override; + + virtual bool RecvLastPrivateDocShellDestroyed() override; + + virtual bool RecvVolumes(InfallibleTArray<VolumeInfo>&& aVolumes) override; + + virtual bool RecvFileSystemUpdate(const nsString& aFsName, + const nsString& aVolumeName, + const int32_t& aState, + const int32_t& aMountGeneration, + const bool& aIsMediaPresent, + const bool& aIsSharing, + const bool& aIsFormatting, + const bool& aIsFake, + const bool& aIsUnmounting, + const bool& aIsRemovable, + const bool& aIsHotSwappable) override; + + virtual bool RecvVolumeRemoved(const nsString& aFsName) override; + + virtual bool + RecvNotifyProcessPriorityChanged(const hal::ProcessPriority& aPriority) override; + + virtual bool RecvMinimizeMemoryUsage() override; + + virtual bool RecvLoadAndRegisterSheet(const URIParams& aURI, + const uint32_t& aType) override; + + virtual bool RecvUnregisterSheet(const URIParams& aURI, + const uint32_t& aType) override; + + virtual bool RecvNotifyPhoneStateChange(const nsString& aState) override; + + void AddIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS); + + void RemoveIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS); + + virtual bool RecvNotifyIdleObserver(const uint64_t& aObserver, + const nsCString& aTopic, + const nsString& aData) override; + + virtual bool RecvAssociatePluginId(const uint32_t& aPluginId, + const base::ProcessId& aProcessId) override; + + virtual bool RecvLoadPluginResult(const uint32_t& aPluginId, + const bool& aResult) override; + + virtual bool RecvUpdateWindow(const uintptr_t& aChildId) override; + + virtual bool RecvStartProfiler(const ProfilerInitParams& params) override; + + virtual bool RecvPauseProfiler(const bool& aPause) override; + + virtual bool RecvStopProfiler() override; + + virtual bool RecvGatherProfile() override; + + virtual bool RecvDomainSetChanged(const uint32_t& aSetType, + const uint32_t& aChangeType, + const OptionalURIParams& aDomain) override; + + virtual bool RecvShutdown() override; + + virtual bool + RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers, + const uint32_t& aAction) override; + + virtual bool RecvEndDragSession(const bool& aDoneDrag, + const bool& aUserCancelled, + const mozilla::LayoutDeviceIntPoint& aEndDragPoint) override; + + virtual bool + RecvPush(const nsCString& aScope, + const IPC::Principal& aPrincipal, + const nsString& aMessageId) override; + + virtual bool + RecvPushWithData(const nsCString& aScope, + const IPC::Principal& aPrincipal, + const nsString& aMessageId, + InfallibleTArray<uint8_t>&& aData) override; + + virtual bool + RecvPushSubscriptionChange(const nsCString& aScope, + const IPC::Principal& aPrincipal) override; + + virtual bool + RecvPushError(const nsCString& aScope, const IPC::Principal& aPrincipal, + const nsString& aMessage, const uint32_t& aFlags) override; + + virtual bool + RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aScope, + const IPC::Principal& aPrincipal) override; + + // Get the directory for IndexedDB files. We query the parent for this and + // cache the value + nsString &GetIndexedDBPath(); + + ContentParentId GetID() const { return mID; } + +#if defined(XP_WIN) && defined(ACCESSIBILITY) + uint32_t GetMsaaID() const { return mMsaaID; } +#endif + + bool IsForApp() const { return mIsForApp; } + bool IsForBrowser() const { return mIsForBrowser; } + + virtual PBlobChild* + SendPBlobConstructor(PBlobChild* actor, + const BlobConstructorParams& params) override; + + virtual PFileDescriptorSetChild* + SendPFileDescriptorSetConstructor(const FileDescriptor&) override; + + virtual PFileDescriptorSetChild* + AllocPFileDescriptorSetChild(const FileDescriptor&) override; + + virtual bool + DeallocPFileDescriptorSetChild(PFileDescriptorSetChild*) override; + + virtual bool SendPBrowserConstructor(PBrowserChild* actor, + const TabId& aTabId, + const IPCTabContext& context, + const uint32_t& chromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) override; + + virtual bool RecvPBrowserConstructor(PBrowserChild* aCctor, + const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) override; + + FORWARD_SHMEM_ALLOCATOR_TO(PContentChild) + + void GetAvailableDictionaries(InfallibleTArray<nsString>& aDictionaries); + + PBrowserOrId + GetBrowserOrId(TabChild* aTabChild); + + virtual POfflineCacheUpdateChild* + AllocPOfflineCacheUpdateChild(const URIParams& manifestURI, + const URIParams& documentURI, + const PrincipalInfo& aLoadingPrincipalInfo, + const bool& stickDocument) override; + + virtual bool + DeallocPOfflineCacheUpdateChild(POfflineCacheUpdateChild* offlineCacheUpdate) override; + + virtual PWebrtcGlobalChild* AllocPWebrtcGlobalChild() override; + + virtual bool DeallocPWebrtcGlobalChild(PWebrtcGlobalChild *aActor) override; + + virtual PContentPermissionRequestChild* + AllocPContentPermissionRequestChild(const InfallibleTArray<PermissionRequest>& aRequests, + const IPC::Principal& aPrincipal, + const TabId& aTabId) override; + virtual bool + DeallocPContentPermissionRequestChild(PContentPermissionRequestChild* actor) override; + + // Windows specific - set up audio session + virtual bool + RecvSetAudioSessionData(const nsID& aId, + const nsString& aDisplayName, + const nsString& aIconPath) override; + + + // GetFiles for WebKit/Blink FileSystem API and Directory API must run on the + // parent process. + void + CreateGetFilesRequest(const nsAString& aDirectoryPath, bool aRecursiveFlag, + nsID& aUUID, GetFilesHelperChild* aChild); + + void + DeleteGetFilesRequest(nsID& aUUID, GetFilesHelperChild* aChild); + + virtual bool + RecvGetFilesResponse(const nsID& aUUID, + const GetFilesResponseResult& aResult) override; + + virtual bool + RecvBlobURLRegistration(const nsCString& aURI, PBlobChild* aBlobChild, + const IPC::Principal& aPrincipal) override; + + virtual bool + RecvBlobURLUnregistration(const nsCString& aURI) override; + +#if defined(XP_WIN) && defined(ACCESSIBILITY) + bool + SendGetA11yContentId(); +#endif // defined(XP_WIN) && defined(ACCESSIBILITY) + + /** + * Helper function for protocols that use the GPU process when available. + * Overrides FatalError to just be a warning when communicating with the + * GPU process since we don't want to crash the content process when the + * GPU process crashes. + */ + static void FatalErrorIfNotUsingGPUProcess(const char* const aProtocolName, + const char* const aErrorMsg, + base::ProcessId aOtherPid); + +private: + static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure); + void StartForceKillTimer(); + + virtual void ActorDestroy(ActorDestroyReason why) override; + + virtual void ProcessingError(Result aCode, const char* aReason) override; + + InfallibleTArray<nsAutoPtr<AlertObserver> > mAlertObservers; + RefPtr<ConsoleListener> mConsoleListener; + + nsTHashtable<nsPtrHashKey<nsIObserver>> mIdleObservers; + + InfallibleTArray<nsString> mAvailableDictionaries; + + /** + * An ID unique to the process containing our corresponding + * content parent. + * + * We expect our content parent to set this ID immediately after opening a + * channel to us. + */ + ContentParentId mID; + +#if defined(XP_WIN) && defined(ACCESSIBILITY) + /** + * This is an a11y-specific unique id for the content process that is + * generated by the chrome process. + */ + uint32_t mMsaaID; +#endif + + AppInfo mAppInfo; + + bool mIsForApp; + bool mIsForBrowser; + bool mCanOverrideProcessName; + bool mIsAlive; + nsString mProcessName; + + static ContentChild* sSingleton; + + nsCOMPtr<nsIDomainPolicy> mPolicy; + nsCOMPtr<nsITimer> mForceKillTimer; + +#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) + nsCOMPtr<nsIFile> mProfileDir; +#endif + + // Hashtable to keep track of the pending GetFilesHelper objects. + // This GetFilesHelperChild objects are removed when RecvGetFilesResponse is + // received. + nsRefPtrHashtable<nsIDHashKey, GetFilesHelperChild> mGetFilesPendingRequests; + + bool mShuttingDown; + + DISALLOW_EVIL_CONSTRUCTORS(ContentChild); +}; + +uint64_t +NextWindowID(); + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_ContentChild_h diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp new file mode 100644 index 000000000..ff40db8d7 --- /dev/null +++ b/dom/ipc/ContentParent.cpp @@ -0,0 +1,5185 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/DebugOnly.h" + +#include "base/basictypes.h" + +#include "ContentParent.h" +#include "TabParent.h" + +#if defined(ANDROID) || defined(LINUX) +# include <sys/time.h> +# include <sys/resource.h> +#endif + +#ifdef MOZ_WIDGET_GONK +#include <sys/types.h> +#include <sys/wait.h> +#endif + +#include "chrome/common/process_watcher.h" + +#include "mozilla/a11y/PDocAccessible.h" +#include "AppProcessChecker.h" +#include "AudioChannelService.h" +#include "BlobParent.h" +#include "CrashReporterParent.h" +#include "GMPServiceParent.h" +#include "HandlerServiceParent.h" +#include "IHistory.h" +#include "imgIContainer.h" +#include "mozIApplication.h" +#if defined(XP_WIN) && defined(ACCESSIBILITY) +#include "mozilla/a11y/AccessibleWrap.h" +#endif +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/StyleSheetInlines.h" +#include "mozilla/DataStorage.h" +#include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h" +#include "mozilla/docshell/OfflineCacheUpdateParent.h" +#include "mozilla/dom/DataTransfer.h" +#include "mozilla/dom/DOMStorageIPC.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/FileSystemSecurity.h" +#include "mozilla/dom/ExternalHelperAppParent.h" +#include "mozilla/dom/GetFilesHelper.h" +#include "mozilla/dom/GeolocationBinding.h" +#include "mozilla/dom/Notification.h" +#include "mozilla/dom/PContentBridgeParent.h" +#include "mozilla/dom/PContentPermissionRequestParent.h" +#include "mozilla/dom/PCycleCollectWithLogsParent.h" +#include "mozilla/dom/PMemoryReportRequestParent.h" +#include "mozilla/dom/ServiceWorkerRegistrar.h" +#include "mozilla/dom/power/PowerManagerService.h" +#include "mozilla/dom/Permissions.h" +#include "mozilla/dom/PresentationParent.h" +#include "mozilla/dom/PPresentationParent.h" +#include "mozilla/dom/PushNotifier.h" +#include "mozilla/dom/FlyWebPublishedServerIPC.h" +#include "mozilla/dom/quota/QuotaManagerService.h" +#include "mozilla/dom/time/DateCacheCleaner.h" +#include "mozilla/embedding/printingui/PrintingParent.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/hal_sandbox/PHalParent.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/BackgroundParent.h" +#include "mozilla/ipc/FileDescriptorUtils.h" +#include "mozilla/ipc/PSendStreamParent.h" +#include "mozilla/ipc/TestShellParent.h" +#include "mozilla/ipc/InputStreamUtils.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" +#include "mozilla/layers/PAPZParent.h" +#include "mozilla/layers/CompositorThread.h" +#include "mozilla/layers/ImageBridgeParent.h" +#include "mozilla/layers/LayerTreeOwnerTracker.h" +#include "mozilla/layout/RenderFrameParent.h" +#include "mozilla/LookAndFeel.h" +#include "mozilla/media/MediaParent.h" +#include "mozilla/Move.h" +#include "mozilla/net/NeckoParent.h" +#include "mozilla/plugins/PluginBridge.h" +#include "mozilla/Preferences.h" +#include "mozilla/ProcessHangMonitor.h" +#include "mozilla/ProcessHangMonitorIPC.h" +#ifdef MOZ_ENABLE_PROFILER_SPS +#include "mozilla/ProfileGatherer.h" +#endif +#include "mozilla/ScopeExit.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/Telemetry.h" +#include "mozilla/WebBrowserPersistDocumentParent.h" +#include "mozilla/Unused.h" +#include "nsAnonymousTemporaryFile.h" +#include "nsAppRunner.h" +#include "nsCDefaultURIFixup.h" +#include "nsCExternalHandlerService.h" +#include "nsCOMPtr.h" +#include "nsChromeRegistryChrome.h" +#include "nsConsoleMessage.h" +#include "nsConsoleService.h" +#include "nsContentUtils.h" +#include "nsDebugImpl.h" +#include "nsFrameMessageManager.h" +#include "nsHashPropertyBag.h" +#include "nsIAlertsService.h" +#include "nsIAppsService.h" +#include "nsIClipboard.h" +#include "nsContentPermissionHelper.h" +#include "nsICycleCollectorListener.h" +#include "nsIDocShellTreeOwner.h" +#include "nsIDocument.h" +#include "nsIDOMGeoGeolocation.h" +#include "nsIDOMGeoPositionError.h" +#include "nsIDragService.h" +#include "mozilla/dom/WakeLock.h" +#include "nsIDOMWindow.h" +#include "nsIExternalProtocolService.h" +#include "nsIFormProcessor.h" +#include "nsIGfxInfo.h" +#include "nsIIdleService.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIMemoryInfoDumper.h" +#include "nsIMemoryReporter.h" +#include "nsIMozBrowserFrame.h" +#include "nsIMutable.h" +#include "nsINSSU2FToken.h" +#include "nsIObserverService.h" +#include "nsIParentChannel.h" +#include "nsIPresShell.h" +#include "nsIRemoteWindowContext.h" +#include "nsIScriptError.h" +#include "nsIScriptSecurityManager.h" +#include "nsISiteSecurityService.h" +#include "nsISpellChecker.h" +#include "nsISupportsPrimitives.h" +#include "nsITimer.h" +#include "nsIURIFixup.h" +#include "nsIDocShellTreeOwner.h" +#include "nsIXULWindow.h" +#include "nsIDOMChromeWindow.h" +#include "nsIWindowWatcher.h" +#include "nsPIWindowWatcher.h" +#include "nsWindowWatcher.h" +#include "nsIXULRuntime.h" +#include "mozilla/dom/nsMixedContentBlocker.h" +#include "nsMemoryInfoDumper.h" +#include "nsMemoryReporterManager.h" +#include "nsServiceManagerUtils.h" +#include "nsStyleSheetService.h" +#include "nsThreadUtils.h" +#include "nsToolkitCompsCID.h" +#include "nsWidgetsCID.h" +#include "PreallocatedProcessManager.h" +#include "ProcessPriorityManager.h" +#include "SandboxHal.h" +#include "ScreenManagerParent.h" +#include "SourceSurfaceRawData.h" +#include "TabParent.h" +#include "URIUtils.h" +#include "nsIWebBrowserChrome.h" +#include "nsIDocShell.h" +#include "nsDocShell.h" +#include "nsOpenURIInFrameParams.h" +#include "mozilla/net/NeckoMessageUtils.h" +#include "gfxPrefs.h" +#include "prio.h" +#include "private/pprio.h" +#include "ContentProcessManager.h" +#include "mozilla/dom/ipc/StructuredCloneData.h" +#include "mozilla/psm/PSMContentListener.h" +#include "nsPluginHost.h" +#include "nsPluginTags.h" +#include "nsIBlocklistService.h" +#include "mozilla/StyleSheet.h" +#include "mozilla/StyleSheetInlines.h" +#include "nsHostObjectProtocolHandler.h" +#include "nsICaptivePortalService.h" + +#include "nsIBidiKeyboard.h" + +#include "nsLayoutStylesheetCache.h" + +#ifdef MOZ_WEBRTC +#include "signaling/src/peerconnection/WebrtcGlobalParent.h" +#endif + +#if defined(ANDROID) || defined(LINUX) +#include "nsSystemInfo.h" +#endif + +#if defined(XP_LINUX) +#include "mozilla/Hal.h" +#endif + +#ifdef ANDROID +# include "gfxAndroidPlatform.h" +#endif + +#ifdef MOZ_PERMISSIONS +# include "nsPermissionManager.h" +#endif + +#ifdef MOZ_WIDGET_ANDROID +# include "AndroidBridge.h" +#endif + +#ifdef MOZ_WIDGET_GONK +#include "nsIVolume.h" +#include "nsVolumeService.h" +#include "nsIVolumeService.h" +#include "SpeakerManagerService.h" +using namespace mozilla::system; +#endif + +#ifdef MOZ_WIDGET_GTK +#include <gdk/gdk.h> +#endif + +#include "mozilla/RemoteSpellCheckEngineParent.h" + +#include "Crypto.h" + +#ifdef MOZ_WEBSPEECH +#include "mozilla/dom/SpeechSynthesisParent.h" +#endif + +#if defined(MOZ_CONTENT_SANDBOX) && defined(XP_LINUX) +#include "mozilla/SandboxInfo.h" +#include "mozilla/SandboxBroker.h" +#include "mozilla/SandboxBrokerPolicyFactory.h" +#endif + +#ifdef MOZ_TOOLKIT_SEARCH +#include "nsIBrowserSearchService.h" +#endif + +#ifdef MOZ_ENABLE_PROFILER_SPS +#include "nsIProfiler.h" +#include "nsIProfileSaveEvent.h" +#endif + +#ifdef XP_WIN +#include "mozilla/widget/AudioSession.h" +#endif + +#ifdef MOZ_CRASHREPORTER +#include "nsThread.h" +#endif + +#ifdef ACCESSIBILITY +#include "nsAccessibilityService.h" +#endif + +// For VP9Benchmark::sBenchmarkFpsPref +#include "Benchmark.h" + +static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); + +#if defined(XP_WIN) +// e10s forced enable pref, defined in nsAppRunner.cpp +extern const char* kForceEnableE10sPref; +#endif + +using base::ChildPrivileges; +using base::KillProcess; +#ifdef MOZ_ENABLE_PROFILER_SPS +using mozilla::ProfileGatherer; +#endif + +#ifdef MOZ_CRASHREPORTER +using namespace CrashReporter; +#endif +using namespace mozilla::dom::power; +using namespace mozilla::media; +using namespace mozilla::embedding; +using namespace mozilla::gfx; +using namespace mozilla::gmp; +using namespace mozilla::hal; +using namespace mozilla::ipc; +using namespace mozilla::layers; +using namespace mozilla::layout; +using namespace mozilla::net; +using namespace mozilla::jsipc; +using namespace mozilla::psm; +using namespace mozilla::widget; + +// XXX Workaround for bug 986973 to maintain the existing broken semantics +template<> +struct nsIConsoleService::COMTypeInfo<nsConsoleService, void> { + static const nsIID kIID; +}; +const nsIID nsIConsoleService::COMTypeInfo<nsConsoleService, void>::kIID = NS_ICONSOLESERVICE_IID; + +namespace mozilla { +namespace dom { + +#define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline" +#define NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC "ipc:network:set-connectivity" + +class MemoryReportRequestParent : public PMemoryReportRequestParent +{ +public: + explicit MemoryReportRequestParent(uint32_t aGeneration); + + virtual ~MemoryReportRequestParent(); + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + virtual bool RecvReport(const MemoryReport& aReport) override; + virtual bool Recv__delete__() override; + +private: + const uint32_t mGeneration; + // Non-null if we haven't yet called EndProcessReport() on it. + RefPtr<nsMemoryReporterManager> mReporterManager; + + ContentParent* Owner() + { + return static_cast<ContentParent*>(Manager()); + } +}; + +MemoryReportRequestParent::MemoryReportRequestParent(uint32_t aGeneration) + : mGeneration(aGeneration) +{ + MOZ_COUNT_CTOR(MemoryReportRequestParent); + mReporterManager = nsMemoryReporterManager::GetOrCreate(); + NS_WARNING_ASSERTION(mReporterManager, "GetOrCreate failed"); +} + +bool +MemoryReportRequestParent::RecvReport(const MemoryReport& aReport) +{ + if (mReporterManager) { + mReporterManager->HandleChildReport(mGeneration, aReport); + } + return true; +} + +bool +MemoryReportRequestParent::Recv__delete__() +{ + // Notifying the reporter manager is done in ActorDestroy, because + // it needs to happen even if the child process exits mid-report. + // (The reporter manager will time out eventually, but let's avoid + // that if possible.) + return true; +} + +void +MemoryReportRequestParent::ActorDestroy(ActorDestroyReason aWhy) +{ + if (mReporterManager) { + mReporterManager->EndProcessReport(mGeneration, aWhy == Deletion); + mReporterManager = nullptr; + } +} + +MemoryReportRequestParent::~MemoryReportRequestParent() +{ + MOZ_ASSERT(!mReporterManager); + MOZ_COUNT_DTOR(MemoryReportRequestParent); +} + +// IPC receiver for remote GC/CC logging. +class CycleCollectWithLogsParent final : public PCycleCollectWithLogsParent +{ +public: + ~CycleCollectWithLogsParent() + { + MOZ_COUNT_DTOR(CycleCollectWithLogsParent); + } + + static bool AllocAndSendConstructor(ContentParent* aManager, + bool aDumpAllTraces, + nsICycleCollectorLogSink* aSink, + nsIDumpGCAndCCLogsCallback* aCallback) + { + CycleCollectWithLogsParent *actor; + FILE* gcLog; + FILE* ccLog; + nsresult rv; + + actor = new CycleCollectWithLogsParent(aSink, aCallback); + rv = actor->mSink->Open(&gcLog, &ccLog); + if (NS_WARN_IF(NS_FAILED(rv))) { + delete actor; + return false; + } + + return aManager-> + SendPCycleCollectWithLogsConstructor(actor, + aDumpAllTraces, + FILEToFileDescriptor(gcLog), + FILEToFileDescriptor(ccLog)); + } + +private: + virtual bool RecvCloseGCLog() override + { + Unused << mSink->CloseGCLog(); + return true; + } + + virtual bool RecvCloseCCLog() override + { + Unused << mSink->CloseCCLog(); + return true; + } + + virtual bool Recv__delete__() override + { + // Report completion to mCallback only on successful + // completion of the protocol. + nsCOMPtr<nsIFile> gcLog, ccLog; + mSink->GetGcLog(getter_AddRefs(gcLog)); + mSink->GetCcLog(getter_AddRefs(ccLog)); + Unused << mCallback->OnDump(gcLog, ccLog, /* parent = */ false); + return true; + } + + virtual void ActorDestroy(ActorDestroyReason aReason) override + { + // If the actor is unexpectedly destroyed, we deliberately + // don't call Close[GC]CLog on the sink, because the logs may + // be incomplete. See also the nsCycleCollectorLogSinkToFile + // implementaiton of those methods, and its destructor. + } + + CycleCollectWithLogsParent(nsICycleCollectorLogSink *aSink, + nsIDumpGCAndCCLogsCallback *aCallback) + : mSink(aSink), mCallback(aCallback) + { + MOZ_COUNT_CTOR(CycleCollectWithLogsParent); + } + + nsCOMPtr<nsICycleCollectorLogSink> mSink; + nsCOMPtr<nsIDumpGCAndCCLogsCallback> mCallback; +}; + +// A memory reporter for ContentParent objects themselves. +class ContentParentsMemoryReporter final : public nsIMemoryReporter +{ + ~ContentParentsMemoryReporter() {} +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIMEMORYREPORTER +}; + +NS_IMPL_ISUPPORTS(ContentParentsMemoryReporter, nsIMemoryReporter) + +NS_IMETHODIMP +ContentParentsMemoryReporter::CollectReports( + nsIHandleReportCallback* aHandleReport, + nsISupports* aData, + bool aAnonymize) +{ + AutoTArray<ContentParent*, 16> cps; + ContentParent::GetAllEvenIfDead(cps); + + for (uint32_t i = 0; i < cps.Length(); i++) { + ContentParent* cp = cps[i]; + MessageChannel* channel = cp->GetIPCChannel(); + + nsString friendlyName; + cp->FriendlyName(friendlyName, aAnonymize); + + cp->AddRef(); + nsrefcnt refcnt = cp->Release(); + + const char* channelStr = "no channel"; + uint32_t numQueuedMessages = 0; + if (channel) { + if (channel->Unsound_IsClosed()) { + channelStr = "closed channel"; + } else { + channelStr = "open channel"; + } + numQueuedMessages = channel->Unsound_NumQueuedMessages(); + } + + nsPrintfCString path("queued-ipc-messages/content-parent" + "(%s, pid=%d, %s, 0x%p, refcnt=%d)", + NS_ConvertUTF16toUTF8(friendlyName).get(), + cp->Pid(), channelStr, + static_cast<nsIContentParent*>(cp), refcnt); + + NS_NAMED_LITERAL_CSTRING(desc, + "The number of unset IPC messages held in this ContentParent's " + "channel. A large value here might indicate that we're leaking " + "messages. Similarly, a ContentParent object for a process that's no " + "longer running could indicate that we're leaking ContentParents."); + + aHandleReport->Callback(/* process */ EmptyCString(), path, + KIND_OTHER, UNITS_COUNT, + numQueuedMessages, desc, aData); + } + + return NS_OK; +} + +nsDataHashtable<nsStringHashKey, ContentParent*>* ContentParent::sAppContentParents; +nsTArray<ContentParent*>* ContentParent::sNonAppContentParents; +nsTArray<ContentParent*>* ContentParent::sLargeAllocationContentParents; +nsTArray<ContentParent*>* ContentParent::sPrivateContent; +StaticAutoPtr<LinkedList<ContentParent> > ContentParent::sContentParents; +#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX) +UniquePtr<SandboxBrokerPolicyFactory> ContentParent::sSandboxBrokerPolicyFactory; +#endif + +// This is true when subprocess launching is enabled. This is the +// case between StartUp() and ShutDown() or JoinAllSubprocesses(). +static bool sCanLaunchSubprocesses; + +// Set to true if the DISABLE_UNSAFE_CPOW_WARNINGS environment variable is +// set. +static bool sDisableUnsafeCPOWWarnings = false; + +// The first content child has ID 1, so the chrome process can have ID 0. +static uint64_t gContentChildID = 1; + +// We want the prelaunched process to know that it's for apps, but not +// actually for any app in particular. Use a magic manifest URL. +// Can't be a static constant. +#define MAGIC_PREALLOCATED_APP_MANIFEST_URL NS_LITERAL_STRING("{{template}}") + +static const char* sObserverTopics[] = { + "xpcom-shutdown", + "profile-before-change", + NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC, + NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC, + NS_IPC_CAPTIVE_PORTAL_SET_STATE, + "memory-pressure", + "child-gc-request", + "child-cc-request", + "child-mmu-request", + "last-pb-context-exited", + "file-watcher-update", +#ifdef MOZ_WIDGET_GONK + NS_VOLUME_STATE_CHANGED, + NS_VOLUME_REMOVED, + "phone-state-changed", +#endif +#ifdef ACCESSIBILITY + "a11y-init-or-shutdown", +#endif +#ifdef MOZ_ENABLE_PROFILER_SPS + "profiler-started", + "profiler-stopped", + "profiler-paused", + "profiler-resumed", + "profiler-subprocess-gather", + "profiler-subprocess", +#endif + "cacheservice:empty-cache", +}; + +// PreallocateAppProcess is called by the PreallocatedProcessManager. +// ContentParent then takes this process back within +// GetNewOrPreallocatedAppProcess. +/*static*/ already_AddRefed<ContentParent> +ContentParent::PreallocateAppProcess() +{ + RefPtr<ContentParent> process = + new ContentParent(/* app = */ nullptr, + /* aOpener = */ nullptr, + /* isForBrowserElement = */ false, + /* isForPreallocated = */ true); + + if (!process->LaunchSubprocess(PROCESS_PRIORITY_PREALLOC)) { + return nullptr; + } + + process->Init(); + return process.forget(); +} + +/*static*/ already_AddRefed<ContentParent> +ContentParent::GetNewOrPreallocatedAppProcess(mozIApplication* aApp, + ProcessPriority aInitialPriority, + ContentParent* aOpener, + /*out*/ bool* aTookPreAllocated) +{ + MOZ_ASSERT(aApp); + RefPtr<ContentParent> process = PreallocatedProcessManager::Take(); + + if (process) { + if (!process->SetPriorityAndCheckIsAlive(aInitialPriority)) { + // Kill the process just in case it's not actually dead; we don't want + // to "leak" this process! + process->KillHard("GetNewOrPreallocatedAppProcess"); + } + else { + nsAutoString manifestURL; + if (NS_FAILED(aApp->GetManifestURL(manifestURL))) { + NS_ERROR("Failed to get manifest URL"); + return nullptr; + } + process->TransformPreallocatedIntoApp(aOpener, manifestURL); + process->ForwardKnownInfo(); + + if (aTookPreAllocated) { + *aTookPreAllocated = true; + } + return process.forget(); + } + } + + NS_WARNING("Unable to use pre-allocated app process"); + process = new ContentParent(aApp, + /* aOpener = */ aOpener, + /* isForBrowserElement = */ false, + /* isForPreallocated = */ false); + + if (!process->LaunchSubprocess(aInitialPriority)) { + return nullptr; + } + + process->Init(); + process->ForwardKnownInfo(); + + if (aTookPreAllocated) { + *aTookPreAllocated = false; + } + + return process.forget(); +} + +/*static*/ void +ContentParent::StartUp() +{ + // We could launch sub processes from content process + // FIXME Bug 1023701 - Stop using ContentParent static methods in + // child process + sCanLaunchSubprocesses = true; + + if (!XRE_IsParentProcess()) { + return; + } + +#if defined(MOZ_CONTENT_SANDBOX) && defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19 + // Require sandboxing on B2G >= KitKat. This condition must stay + // in sync with ContentChild::RecvSetProcessSandbox. + if (!SandboxInfo::Get().CanSandboxContent()) { + // MOZ_CRASH strings are only for debug builds; make sure the + // message is clear on non-debug builds as well: + printf_stderr("Sandboxing support is required on this platform. " + "Recompile kernel with CONFIG_SECCOMP_FILTER=y\n"); + MOZ_CRASH("Sandboxing support is required on this platform."); + } +#endif + + // Note: This reporter measures all ContentParents. + RegisterStrongMemoryReporter(new ContentParentsMemoryReporter()); + + mozilla::dom::time::InitializeDateCacheCleaner(); + + BlobParent::Startup(BlobParent::FriendKey()); + + BackgroundChild::Startup(); + + // Try to preallocate a process that we can transform into an app later. + PreallocatedProcessManager::AllocateAfterDelay(); + + sDisableUnsafeCPOWWarnings = PR_GetEnv("DISABLE_UNSAFE_CPOW_WARNINGS"); + +#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX) + sSandboxBrokerPolicyFactory = MakeUnique<SandboxBrokerPolicyFactory>(); +#endif +} + +/*static*/ void +ContentParent::ShutDown() +{ + // No-op for now. We rely on normal process shutdown and + // ClearOnShutdown() to clean up our state. + sCanLaunchSubprocesses = false; + +#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX) + sSandboxBrokerPolicyFactory = nullptr; +#endif +} + +/*static*/ void +ContentParent::JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses, + Monitor* aMonitor, bool* aDone) +{ + const nsTArray<ContentParent*>& processes = *aProcesses; + for (uint32_t i = 0; i < processes.Length(); ++i) { + if (GeckoChildProcessHost* process = processes[i]->mSubprocess) { + process->Join(); + } + } + { + MonitorAutoLock lock(*aMonitor); + *aDone = true; + lock.Notify(); + } + // Don't touch any arguments to this function from now on. +} + +/*static*/ void +ContentParent::JoinAllSubprocesses() +{ + MOZ_ASSERT(NS_IsMainThread()); + + AutoTArray<ContentParent*, 8> processes; + GetAll(processes); + if (processes.IsEmpty()) { + printf_stderr("There are no live subprocesses."); + return; + } + + printf_stderr("Subprocesses are still alive. Doing emergency join.\n"); + + bool done = false; + Monitor monitor("mozilla.dom.ContentParent.JoinAllSubprocesses"); + XRE_GetIOMessageLoop()->PostTask(NewRunnableFunction( + &ContentParent::JoinProcessesIOThread, + &processes, &monitor, &done)); + { + MonitorAutoLock lock(monitor); + while (!done) { + lock.Wait(); + } + } + + sCanLaunchSubprocesses = false; +} + +/*static*/ already_AddRefed<ContentParent> +ContentParent::GetNewOrUsedBrowserProcess(bool aForBrowserElement, + ProcessPriority aPriority, + ContentParent* aOpener, + bool aLargeAllocationProcess) +{ + nsTArray<ContentParent*>* contentParents; + int32_t maxContentParents; + + // Decide which pool of content parents we are going to be pulling from based + // on the aLargeAllocationProcess flag. + if (aLargeAllocationProcess) { + if (!sLargeAllocationContentParents) { + sLargeAllocationContentParents = new nsTArray<ContentParent*>(); + } + contentParents = sLargeAllocationContentParents; + + maxContentParents = Preferences::GetInt("dom.ipc.dedicatedProcessCount", 2); + } else { + if (!sNonAppContentParents) { + sNonAppContentParents = new nsTArray<ContentParent*>(); + } + contentParents = sNonAppContentParents; + + maxContentParents = Preferences::GetInt("dom.ipc.processCount", 1); + } + + if (maxContentParents < 1) { + maxContentParents = 1; + } + + if (contentParents->Length() >= uint32_t(maxContentParents)) { + uint32_t maxSelectable = std::min(static_cast<uint32_t>(contentParents->Length()), + static_cast<uint32_t>(maxContentParents)); + uint32_t startIdx = rand() % maxSelectable; + uint32_t currIdx = startIdx; + do { + RefPtr<ContentParent> p = (*contentParents)[currIdx]; + NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sNonAppContntParents?"); + if (p->mOpener == aOpener) { + return p.forget(); + } + currIdx = (currIdx + 1) % maxSelectable; + } while (currIdx != startIdx); + } + + // Try to take and transform the preallocated process into browser. + RefPtr<ContentParent> p = PreallocatedProcessManager::Take(); + if (p) { + p->TransformPreallocatedIntoBrowser(aOpener); + } else { + // Failed in using the preallocated process: fork from the chrome process. + p = new ContentParent(/* app = */ nullptr, + aOpener, + aForBrowserElement, + /* isForPreallocated = */ false); + + if (!p->LaunchSubprocess(aPriority)) { + return nullptr; + } + + p->Init(); + } + + p->mLargeAllocationProcess = aLargeAllocationProcess; + + p->ForwardKnownInfo(); + + contentParents->AppendElement(p); + return p.forget(); +} + +/*static*/ ProcessPriority +ContentParent::GetInitialProcessPriority(Element* aFrameElement) +{ + // Frames with mozapptype == critical which are expecting a system message + // get FOREGROUND_HIGH priority. + + if (!aFrameElement) { + return PROCESS_PRIORITY_FOREGROUND; + } + + if (aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype, + NS_LITERAL_STRING("inputmethod"), eCaseMatters)) { + return PROCESS_PRIORITY_FOREGROUND_KEYBOARD; + } else if (!aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype, + NS_LITERAL_STRING("critical"), eCaseMatters)) { + return PROCESS_PRIORITY_FOREGROUND; + } + + nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(aFrameElement); + if (!browserFrame) { + return PROCESS_PRIORITY_FOREGROUND; + } + + return PROCESS_PRIORITY_FOREGROUND; +} + +#if defined(XP_WIN) +extern const wchar_t* kPluginWidgetContentParentProperty; + +/*static*/ void +ContentParent::SendAsyncUpdate(nsIWidget* aWidget) +{ + if (!aWidget || aWidget->Destroyed()) { + return; + } + // Fire off an async request to the plugin to paint its window + HWND hwnd = (HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW); + NS_ASSERTION(hwnd, "Expected valid hwnd value."); + ContentParent* cp = reinterpret_cast<ContentParent*>( + ::GetPropW(hwnd, kPluginWidgetContentParentProperty)); + if (cp && !cp->IsDestroyed()) { + Unused << cp->SendUpdateWindow((uintptr_t)hwnd); + } +} +#endif // defined(XP_WIN) + +bool +ContentParent::PreallocatedProcessReady() +{ + return true; +} + +bool +ContentParent::RecvCreateChildProcess(const IPCTabContext& aContext, + const hal::ProcessPriority& aPriority, + const TabId& aOpenerTabId, + ContentParentId* aCpId, + bool* aIsForApp, + bool* aIsForBrowser, + TabId* aTabId) +{ +#if 0 + if (!CanOpenBrowser(aContext)) { + return false; + } +#endif + RefPtr<ContentParent> cp; + MaybeInvalidTabContext tc(aContext); + if (!tc.IsValid()) { + NS_ERROR(nsPrintfCString("Received an invalid TabContext from " + "the child process. (%s)", + tc.GetInvalidReason()).get()); + return false; + } + + nsCOMPtr<mozIApplication> ownApp = tc.GetTabContext().GetOwnApp(); + if (ownApp) { + cp = GetNewOrPreallocatedAppProcess(ownApp, aPriority, this); + } + else { + cp = GetNewOrUsedBrowserProcess(/* isBrowserElement = */ true, + aPriority, this); + } + + if (!cp) { + *aCpId = 0; + *aIsForApp = false; + *aIsForBrowser = false; + return true; + } + + *aCpId = cp->ChildID(); + *aIsForApp = cp->IsForApp(); + *aIsForBrowser = cp->IsForBrowser(); + + ContentProcessManager *cpm = ContentProcessManager::GetSingleton(); + cpm->AddContentProcess(cp, this->ChildID()); + + if (cpm->AddGrandchildProcess(this->ChildID(), cp->ChildID())) { + // Pre-allocate a TabId here to save one time IPC call at app startup. + *aTabId = AllocateTabId(aOpenerTabId, aContext, cp->ChildID()); + return (*aTabId != 0); + } + + return false; +} + +bool +ContentParent::RecvBridgeToChildProcess(const ContentParentId& aCpId) +{ + ContentProcessManager *cpm = ContentProcessManager::GetSingleton(); + ContentParent* cp = cpm->GetContentProcessById(aCpId); + + if (cp) { + ContentParentId parentId; + if (cpm->GetParentProcessId(cp->ChildID(), &parentId) && + parentId == this->ChildID()) { + return NS_SUCCEEDED(PContentBridge::Bridge(this, cp)); + } + } + + // You can't bridge to a process you didn't open! + KillHard("BridgeToChildProcess"); + return false; +} + +static nsIDocShell* GetOpenerDocShellHelper(Element* aFrameElement) +{ + // Propagate the private-browsing status of the element's parent + // docshell to the remote docshell, via the chrome flags. + nsCOMPtr<Element> frameElement = do_QueryInterface(aFrameElement); + MOZ_ASSERT(frameElement); + nsPIDOMWindowOuter* win = frameElement->OwnerDoc()->GetWindow(); + if (!win) { + NS_WARNING("Remote frame has no window"); + return nullptr; + } + nsIDocShell* docShell = win->GetDocShell(); + if (!docShell) { + NS_WARNING("Remote frame has no docshell"); + return nullptr; + } + + return docShell; +} + +bool +ContentParent::RecvCreateGMPService() +{ + return PGMPService::Open(this); +} + +bool +ContentParent::RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv, uint32_t* aRunID) +{ + *aRv = NS_OK; + return mozilla::plugins::SetupBridge(aPluginId, this, false, aRv, aRunID); +} + +bool +ContentParent::RecvUngrabPointer(const uint32_t& aTime) +{ +#if !defined(MOZ_WIDGET_GTK) + NS_RUNTIMEABORT("This message only makes sense on GTK platforms"); + return false; +#else + gdk_pointer_ungrab(aTime); + return true; +#endif +} + +bool +ContentParent::RecvRemovePermission(const IPC::Principal& aPrincipal, + const nsCString& aPermissionType, + nsresult* aRv) { + *aRv = Permissions::RemovePermission(aPrincipal, aPermissionType.get()); + return true; +} + +bool +ContentParent::RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv) +{ + *aRv = NS_OK; + // We don't need to get the run ID for the plugin, since we already got it + // in the first call to SetupBridge in RecvLoadPlugin, so we pass in a dummy + // pointer and just throw it away. + uint32_t dummy = 0; + return mozilla::plugins::SetupBridge(aPluginId, this, true, aRv, &dummy); +} + +bool +ContentParent::RecvGetBlocklistState(const uint32_t& aPluginId, + uint32_t* aState) +{ + *aState = nsIBlocklistService::STATE_BLOCKED; + + RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst(); + if (!pluginHost) { + NS_WARNING("Plugin host not found"); + return false; + } + nsPluginTag* tag = pluginHost->PluginWithId(aPluginId); + + if (!tag) { + // Default state is blocked anyway + NS_WARNING("Plugin tag not found. This should never happen, but to avoid a crash we're forcibly blocking it"); + return true; + } + + return NS_SUCCEEDED(tag->GetBlocklistState(aState)); +} + +bool +ContentParent::RecvFindPlugins(const uint32_t& aPluginEpoch, + nsresult* aRv, + nsTArray<PluginTag>* aPlugins, + uint32_t* aNewPluginEpoch) +{ + *aRv = mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch); + return true; +} + +/*static*/ TabParent* +ContentParent::CreateBrowserOrApp(const TabContext& aContext, + Element* aFrameElement, + ContentParent* aOpenerContentParent, + bool aFreshProcess) +{ + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); + + if (!sCanLaunchSubprocesses) { + return nullptr; + } + + if (TabParent* parent = TabParent::GetNextTabParent()) { + parent->SetOwnerElement(aFrameElement); + return parent; + } + + ProcessPriority initialPriority = GetInitialProcessPriority(aFrameElement); + bool isInContentProcess = !XRE_IsParentProcess(); + TabId tabId; + + nsIDocShell* docShell = GetOpenerDocShellHelper(aFrameElement); + TabId openerTabId; + if (docShell) { + openerTabId = TabParent::GetTabIdFrom(docShell); + } + + if (aContext.IsMozBrowserElement() || !aContext.HasOwnApp()) { + RefPtr<nsIContentParent> constructorSender; + if (isInContentProcess) { + MOZ_ASSERT(aContext.IsMozBrowserElement()); + constructorSender = CreateContentBridgeParent(aContext, initialPriority, + openerTabId, &tabId); + } else { + if (aOpenerContentParent) { + constructorSender = aOpenerContentParent; + } else { + constructorSender = + GetNewOrUsedBrowserProcess(aContext.IsMozBrowserElement(), + initialPriority, + nullptr, + aFreshProcess); + if (!constructorSender) { + return nullptr; + } + } + tabId = AllocateTabId(openerTabId, + aContext.AsIPCTabContext(), + constructorSender->ChildID()); + } + if (constructorSender) { + nsCOMPtr<nsIDocShellTreeOwner> treeOwner; + docShell->GetTreeOwner(getter_AddRefs(treeOwner)); + if (!treeOwner) { + return nullptr; + } + + nsCOMPtr<nsIWebBrowserChrome> wbc = do_GetInterface(treeOwner); + if (!wbc) { + return nullptr; + } + uint32_t chromeFlags = 0; + wbc->GetChromeFlags(&chromeFlags); + + nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell); + if (loadContext && loadContext->UsePrivateBrowsing()) { + chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW; + } + if (docShell->GetAffectPrivateSessionLifetime()) { + chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME; + } + + if (tabId == 0) { + return nullptr; + } + RefPtr<TabParent> tp(new TabParent(constructorSender, tabId, + aContext, chromeFlags)); + tp->SetInitedByParent(); + + PBrowserParent* browser = + constructorSender->SendPBrowserConstructor( + // DeallocPBrowserParent() releases this ref. + tp.forget().take(), tabId, + aContext.AsIPCTabContext(), + chromeFlags, + constructorSender->ChildID(), + constructorSender->IsForApp(), + constructorSender->IsForBrowser()); + + if (aFreshProcess) { + Unused << browser->SendSetFreshProcess(); + } + + if (browser) { + RefPtr<TabParent> constructedTabParent = TabParent::GetFrom(browser); + constructedTabParent->SetOwnerElement(aFrameElement); + return constructedTabParent; + } + } + return nullptr; + } + + // If we got here, we have an app and we're not a browser element. ownApp + // shouldn't be null, because we otherwise would have gone into the + // !HasOwnApp() branch above. + RefPtr<nsIContentParent> parent; + bool reused = false; + bool tookPreallocated = false; + nsAutoString manifestURL; + + if (isInContentProcess) { + parent = CreateContentBridgeParent(aContext, + initialPriority, + openerTabId, + &tabId); + } + else { + nsCOMPtr<mozIApplication> ownApp = aContext.GetOwnApp(); + + if (!sAppContentParents) { + sAppContentParents = + new nsDataHashtable<nsStringHashKey, ContentParent*>(); + } + + // Each app gets its own ContentParent instance unless it shares it with + // a parent app. + if (NS_FAILED(ownApp->GetManifestURL(manifestURL))) { + NS_ERROR("Failed to get manifest URL"); + return nullptr; + } + + RefPtr<ContentParent> p = sAppContentParents->Get(manifestURL); + + if (!p && Preferences::GetBool("dom.ipc.reuse_parent_app")) { + nsAutoString parentAppManifestURL; + aFrameElement->GetAttr(kNameSpaceID_None, + nsGkAtoms::parentapp, parentAppManifestURL); + nsAdoptingString systemAppManifestURL = + Preferences::GetString("b2g.system_manifest_url"); + nsCOMPtr<nsIAppsService> appsService = + do_GetService(APPS_SERVICE_CONTRACTID); + if (!parentAppManifestURL.IsEmpty() && + !parentAppManifestURL.Equals(systemAppManifestURL) && + appsService) { + nsCOMPtr<mozIApplication> parentApp; + nsCOMPtr<mozIApplication> app; + appsService->GetAppByManifestURL(parentAppManifestURL, + getter_AddRefs(parentApp)); + appsService->GetAppByManifestURL(manifestURL, + getter_AddRefs(app)); + + // Only let certified apps re-use the same process. + unsigned short parentAppStatus = 0; + unsigned short appStatus = 0; + if (app && + NS_SUCCEEDED(app->GetAppStatus(&appStatus)) && + appStatus == nsIPrincipal::APP_STATUS_CERTIFIED && + parentApp && + NS_SUCCEEDED(parentApp->GetAppStatus(&parentAppStatus)) && + parentAppStatus == nsIPrincipal::APP_STATUS_CERTIFIED) { + // Check if we can re-use the process of the parent app. + p = sAppContentParents->Get(parentAppManifestURL); + } + } + } + + if (p) { + // Check that the process is still alive and set its priority. + // Hopefully the process won't die after this point, if this call + // succeeds. + if (!p->SetPriorityAndCheckIsAlive(initialPriority)) { + p = nullptr; + } + } + + reused = !!p; + if (!p) { + p = GetNewOrPreallocatedAppProcess(ownApp, initialPriority, nullptr, + &tookPreallocated); + MOZ_ASSERT(p); + sAppContentParents->Put(manifestURL, p); + } + tabId = AllocateTabId(openerTabId, aContext.AsIPCTabContext(), + p->ChildID()); + parent = static_cast<nsIContentParent*>(p); + } + + if (!parent || (tabId == 0)) { + return nullptr; + } + + uint32_t chromeFlags = 0; + + RefPtr<TabParent> tp = new TabParent(parent, tabId, aContext, chromeFlags); + tp->SetInitedByParent(); + PBrowserParent* browser = parent->SendPBrowserConstructor( + // DeallocPBrowserParent() releases this ref. + RefPtr<TabParent>(tp).forget().take(), + tabId, + aContext.AsIPCTabContext(), + chromeFlags, + parent->ChildID(), + parent->IsForApp(), + parent->IsForBrowser()); + + if (aFreshProcess) { + Unused << browser->SendSetFreshProcess(); + } + + if (browser) { + RefPtr<TabParent> constructedTabParent = TabParent::GetFrom(browser); + constructedTabParent->SetOwnerElement(aFrameElement); + } + + if (isInContentProcess) { + // Just return directly without the following check in content process. + return TabParent::GetFrom(browser); + } + + if (!browser) { + // We failed to actually start the PBrowser. This can happen if the + // other process has already died. + if (!reused) { + // Don't leave a broken ContentParent in the hashtable. + parent->AsContentParent()->KillHard("CreateBrowserOrApp"); + sAppContentParents->Remove(manifestURL); + parent = nullptr; + } + + // If we took the preallocated process and it was already dead, try + // again with a non-preallocated process. We can be sure this won't + // loop forever, because the next time through there will be no + // preallocated process to take. + if (tookPreallocated) { + return ContentParent::CreateBrowserOrApp(aContext, aFrameElement, + aOpenerContentParent); + } + + // Otherwise just give up. + return nullptr; + } + + return TabParent::GetFrom(browser); +} + +/*static*/ ContentBridgeParent* +ContentParent::CreateContentBridgeParent(const TabContext& aContext, + const hal::ProcessPriority& aPriority, + const TabId& aOpenerTabId, + /*out*/ TabId* aTabId) +{ + MOZ_ASSERT(aTabId); + + ContentChild* child = ContentChild::GetSingleton(); + ContentParentId cpId; + bool isForApp; + bool isForBrowser; + if (!child->SendCreateChildProcess(aContext.AsIPCTabContext(), + aPriority, + aOpenerTabId, + &cpId, + &isForApp, + &isForBrowser, + aTabId)) { + return nullptr; + } + if (cpId == 0) { + return nullptr; + } + if (!child->SendBridgeToChildProcess(cpId)) { + return nullptr; + } + ContentBridgeParent* parent = child->GetLastBridge(); + parent->SetChildID(cpId); + parent->SetIsForApp(isForApp); + parent->SetIsForBrowser(isForBrowser); + return parent; +} + +void +ContentParent::GetAll(nsTArray<ContentParent*>& aArray) +{ + aArray.Clear(); + + for (auto* cp : AllProcesses(eLive)) { + aArray.AppendElement(cp); + } +} + +void +ContentParent::GetAllEvenIfDead(nsTArray<ContentParent*>& aArray) +{ + aArray.Clear(); + + for (auto* cp : AllProcesses(eAll)) { + aArray.AppendElement(cp); + } +} + +void +ContentParent::Init() +{ + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + size_t length = ArrayLength(sObserverTopics); + for (size_t i = 0; i < length; ++i) { + obs->AddObserver(this, sObserverTopics[i], false); + } + } + Preferences::AddStrongObserver(this, ""); + if (obs) { + nsAutoString cpId; + cpId.AppendInt(static_cast<uint64_t>(this->ChildID())); + obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-created", cpId.get()); + } + +#ifdef ACCESSIBILITY + // If accessibility is running in chrome process then start it in content + // process. + if (nsIPresShell::IsAccessibilityActive()) { +#if !defined(XP_WIN) + Unused << SendActivateA11y(0); +#else + // On Windows we currently only enable a11y in the content process + // for testing purposes. + if (Preferences::GetBool(kForceEnableE10sPref, false)) { + Unused << SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID())); + } +#endif + } +#endif + +#ifdef MOZ_ENABLE_PROFILER_SPS + nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1")); + bool profilerActive = false; + DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + if (profilerActive) { + nsCOMPtr<nsIProfilerStartParams> currentProfilerParams; + rv = profiler->GetStartParams(getter_AddRefs(currentProfilerParams)); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + nsCOMPtr<nsISupports> gatherer; + rv = profiler->GetProfileGatherer(getter_AddRefs(gatherer)); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + mGatherer = static_cast<ProfileGatherer*>(gatherer.get()); + + StartProfiler(currentProfilerParams); + } +#endif + + RefPtr<GeckoMediaPluginServiceParent> gmps(GeckoMediaPluginServiceParent::GetSingleton()); + gmps->UpdateContentProcessGMPCapabilities(); +} + +void +ContentParent::ForwardKnownInfo() +{ + MOZ_ASSERT(mMetamorphosed); + if (!mMetamorphosed) { + return; + } +#ifdef MOZ_WIDGET_GONK + InfallibleTArray<VolumeInfo> volumeInfo; + RefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton(); + if (vs) { + vs->GetVolumesForIPC(&volumeInfo); + Unused << SendVolumes(volumeInfo); + } +#endif /* MOZ_WIDGET_GONK */ +} + +namespace { + +class RemoteWindowContext final : public nsIRemoteWindowContext + , public nsIInterfaceRequestor +{ +public: + explicit RemoteWindowContext(TabParent* aTabParent) + : mTabParent(aTabParent) + { + } + + NS_DECL_ISUPPORTS + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIREMOTEWINDOWCONTEXT + +private: + ~RemoteWindowContext(); + RefPtr<TabParent> mTabParent; +}; + +NS_IMPL_ISUPPORTS(RemoteWindowContext, nsIRemoteWindowContext, nsIInterfaceRequestor) + +RemoteWindowContext::~RemoteWindowContext() +{ +} + +NS_IMETHODIMP +RemoteWindowContext::GetInterface(const nsIID& aIID, void** aSink) +{ + return QueryInterface(aIID, aSink); +} + +NS_IMETHODIMP +RemoteWindowContext::OpenURI(nsIURI* aURI) +{ + mTabParent->LoadURL(aURI); + return NS_OK; +} + +} // namespace + +bool +ContentParent::SetPriorityAndCheckIsAlive(ProcessPriority aPriority) +{ + ProcessPriorityManager::SetProcessPriority(this, aPriority); + + // Now that we've set this process's priority, check whether the process is + // still alive. Hopefully we've set the priority to FOREGROUND*, so the + // process won't unexpectedly crash after this point! + // + // Bug 943174: use waitid() with WNOWAIT so that, if the process + // did exit, we won't consume its zombie and confuse the + // GeckoChildProcessHost dtor. +#ifdef MOZ_WIDGET_GONK + siginfo_t info; + info.si_pid = 0; + if (waitid(P_PID, Pid(), &info, WNOWAIT | WNOHANG | WEXITED) == 0 + && info.si_pid != 0) { + return false; + } +#endif + + return true; +} + +// Helper for ContentParent::TransformPreallocatedIntoApp. +static void +TryGetNameFromManifestURL(const nsAString& aManifestURL, + nsAString& aName) +{ + aName.Truncate(); + if (aManifestURL.IsEmpty() || + aManifestURL == MAGIC_PREALLOCATED_APP_MANIFEST_URL) { + return; + } + + nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID); + NS_ENSURE_TRUE_VOID(appsService); + + nsCOMPtr<mozIApplication> app; + appsService->GetAppByManifestURL(aManifestURL, getter_AddRefs(app)); + + if (!app) { + return; + } + + app->GetName(aName); +} + +void +ContentParent::TransformPreallocatedIntoApp(ContentParent* aOpener, + const nsAString& aAppManifestURL) +{ + MOZ_ASSERT(IsPreallocated()); + mMetamorphosed = true; + mOpener = aOpener; + mAppManifestURL = aAppManifestURL; + TryGetNameFromManifestURL(aAppManifestURL, mAppName); +} + +void +ContentParent::TransformPreallocatedIntoBrowser(ContentParent* aOpener) +{ + // Reset mAppManifestURL, mIsForBrowser and mOSPrivileges for browser. + mMetamorphosed = true; + mOpener = aOpener; + mAppManifestURL.Truncate(); + mIsForBrowser = true; +} + +void +ContentParent::ShutDownProcess(ShutDownMethod aMethod) +{ + // Shutting down by sending a shutdown message works differently than the + // other methods. We first call Shutdown() in the child. After the child is + // ready, it calls FinishShutdown() on us. Then we close the channel. + if (aMethod == SEND_SHUTDOWN_MESSAGE) { + if (mIPCOpen && !mShutdownPending && SendShutdown()) { + mShutdownPending = true; + // Start the force-kill timer if we haven't already. + StartForceKillTimer(); + } + + // If call was not successful, the channel must have been broken + // somehow, and we will clean up the error in ActorDestroy. + return; + } + + using mozilla::dom::quota::QuotaManagerService; + + if (QuotaManagerService* quotaManagerService = QuotaManagerService::Get()) { + quotaManagerService->AbortOperationsForProcess(mChildID); + } + + // If Close() fails with an error, we'll end up back in this function, but + // with aMethod = CLOSE_CHANNEL_WITH_ERROR. + + if (aMethod == CLOSE_CHANNEL && !mCalledClose) { + // Close() can only be called once: It kicks off the destruction + // sequence. + mCalledClose = true; + Close(); + } + + const ManagedContainer<POfflineCacheUpdateParent>& ocuParents = + ManagedPOfflineCacheUpdateParent(); + for (auto iter = ocuParents.ConstIter(); !iter.Done(); iter.Next()) { + RefPtr<mozilla::docshell::OfflineCacheUpdateParent> ocuParent = + static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(iter.Get()->GetKey()); + ocuParent->StopSendingMessagesToChild(); + } + + // NB: must MarkAsDead() here so that this isn't accidentally + // returned from Get*() while in the midst of shutdown. + MarkAsDead(); + + // A ContentParent object might not get freed until after XPCOM shutdown has + // shut down the cycle collector. But by then it's too late to release any + // CC'ed objects, so we need to null them out here, while we still can. See + // bug 899761. + ShutDownMessageManager(); +} + +bool +ContentParent::RecvFinishShutdown() +{ + // At this point, we already called ShutDownProcess once with + // SEND_SHUTDOWN_MESSAGE. To actually close the channel, we call + // ShutDownProcess again with CLOSE_CHANNEL. + MOZ_ASSERT(mShutdownPending); + ShutDownProcess(CLOSE_CHANNEL); + return true; +} + +void +ContentParent::ShutDownMessageManager() +{ + if (!mMessageManager) { + return; + } + + mMessageManager->ReceiveMessage( + static_cast<nsIContentFrameMessageManager*>(mMessageManager.get()), nullptr, + CHILD_PROCESS_SHUTDOWN_MESSAGE, false, + nullptr, nullptr, nullptr, nullptr); + + mMessageManager->Disconnect(); + mMessageManager = nullptr; +} + +void +ContentParent::MarkAsDead() +{ + if (!mAppManifestURL.IsEmpty()) { + if (sAppContentParents) { + sAppContentParents->Remove(mAppManifestURL); + if (!sAppContentParents->Count()) { + delete sAppContentParents; + sAppContentParents = nullptr; + } + } + } else { + if (sNonAppContentParents) { + sNonAppContentParents->RemoveElement(this); + if (!sNonAppContentParents->Length()) { + delete sNonAppContentParents; + sNonAppContentParents = nullptr; + } + } + + if (sLargeAllocationContentParents) { + sLargeAllocationContentParents->RemoveElement(this); + if (!sLargeAllocationContentParents->Length()) { + delete sLargeAllocationContentParents; + sLargeAllocationContentParents = nullptr; + } + } + } + + if (sPrivateContent) { + sPrivateContent->RemoveElement(this); + if (!sPrivateContent->Length()) { + delete sPrivateContent; + sPrivateContent = nullptr; + } + } + + mIsAlive = false; +} + +void +ContentParent::OnChannelError() +{ + RefPtr<ContentParent> content(this); + PContentParent::OnChannelError(); +} + +void +ContentParent::OnChannelConnected(int32_t pid) +{ + SetOtherProcessId(pid); + +#if defined(ANDROID) || defined(LINUX) + // Check nice preference + int32_t nice = Preferences::GetInt("dom.ipc.content.nice", 0); + + // Environment variable overrides preference + char* relativeNicenessStr = getenv("MOZ_CHILD_PROCESS_RELATIVE_NICENESS"); + if (relativeNicenessStr) { + nice = atoi(relativeNicenessStr); + } + + /* make the GUI thread have higher priority on single-cpu devices */ + nsCOMPtr<nsIPropertyBag2> infoService = do_GetService(NS_SYSTEMINFO_CONTRACTID); + if (infoService) { + int32_t cpus; + nsresult rv = infoService->GetPropertyAsInt32(NS_LITERAL_STRING("cpucount"), &cpus); + if (NS_FAILED(rv)) { + cpus = 1; + } + if (nice != 0 && cpus == 1) { + setpriority(PRIO_PROCESS, pid, getpriority(PRIO_PROCESS, pid) + nice); + } + } +#endif +} + +void +ContentParent::ProcessingError(Result aCode, const char* aReason) +{ + if (MsgDropped == aCode) { + return; + } + // Other errors are big deals. + KillHard(aReason); +} + +/* static */ +bool +ContentParent::AllocateLayerTreeId(TabParent* aTabParent, uint64_t* aId) +{ + return AllocateLayerTreeId(aTabParent->Manager()->AsContentParent(), + aTabParent, aTabParent->GetTabId(), aId); +} + +/* static */ +bool +ContentParent::AllocateLayerTreeId(ContentParent* aContent, + TabParent* aTopLevel, const TabId& aTabId, + uint64_t* aId) +{ + GPUProcessManager* gpu = GPUProcessManager::Get(); + + *aId = gpu->AllocateLayerTreeId(); + + if (!aContent || !aTopLevel) { + return false; + } + + gpu->MapLayerTreeId(*aId, aContent->OtherPid()); + + if (!gfxPlatform::AsyncPanZoomEnabled()) { + return true; + } + + return aContent->SendNotifyLayerAllocated(aTabId, *aId); +} + +bool +ContentParent::RecvAllocateLayerTreeId(const ContentParentId& aCpId, + const TabId& aTabId, uint64_t* aId) +{ + // Protect against spoofing by a compromised child. aCpId must either + // correspond to the process that this ContentParent represents or be a + // child of it. + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + if (ChildID() != aCpId) { + ContentParentId parent; + if (!cpm->GetParentProcessId(aCpId, &parent) || + ChildID() != parent) { + return false; + } + } + + // GetTopLevelTabParentByProcessAndTabId will make sure that aTabId + // lives in the process for aCpId. + RefPtr<ContentParent> contentParent = cpm->GetContentProcessById(aCpId); + RefPtr<TabParent> browserParent = + cpm->GetTopLevelTabParentByProcessAndTabId(aCpId, aTabId); + MOZ_ASSERT(contentParent && browserParent); + + return AllocateLayerTreeId(contentParent, browserParent, aTabId, aId); +} + +bool +ContentParent::RecvDeallocateLayerTreeId(const uint64_t& aId) +{ + GPUProcessManager* gpu = GPUProcessManager::Get(); + + if (!gpu->IsLayerTreeIdMapped(aId, OtherPid())) + { + // You can't deallocate layer tree ids that you didn't allocate + KillHard("DeallocateLayerTreeId"); + } + + gpu->UnmapLayerTreeId(aId, OtherPid()); + + return true; +} + +namespace { + +void +DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess) +{ + RefPtr<DeleteTask<GeckoChildProcessHost>> task = new DeleteTask<GeckoChildProcessHost>(aSubprocess); + XRE_GetIOMessageLoop()->PostTask(task.forget()); +} + +// This runnable only exists to delegate ownership of the +// ContentParent to this runnable, until it's deleted by the event +// system. +struct DelayedDeleteContentParentTask : public Runnable +{ + explicit DelayedDeleteContentParentTask(ContentParent* aObj) : mObj(aObj) { } + + // No-op + NS_IMETHOD Run() override { return NS_OK; } + + RefPtr<ContentParent> mObj; +}; + +} // namespace + +void +ContentParent::ActorDestroy(ActorDestroyReason why) +{ + if (mForceKillTimer) { + mForceKillTimer->Cancel(); + mForceKillTimer = nullptr; + } + + // Signal shutdown completion regardless of error state, so we can + // finish waiting in the xpcom-shutdown/profile-before-change observer. + mIPCOpen = false; + + if (mHangMonitorActor) { + ProcessHangMonitor::RemoveProcess(mHangMonitorActor); + mHangMonitorActor = nullptr; + } + + RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get(); + if (fss) { + fss->Forget(ChildID()); + } + + if (why == NormalShutdown && !mCalledClose) { + // If we shut down normally but haven't called Close, assume somebody + // else called Close on us. In that case, we still need to call + // ShutDownProcess below to perform other necessary clean up. + mCalledClose = true; + } + + // Make sure we always clean up. + ShutDownProcess(why == NormalShutdown ? CLOSE_CHANNEL + : CLOSE_CHANNEL_WITH_ERROR); + + RefPtr<ContentParent> kungFuDeathGrip(this); + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + size_t length = ArrayLength(sObserverTopics); + for (size_t i = 0; i < length; ++i) { + obs->RemoveObserver(static_cast<nsIObserver*>(this), + sObserverTopics[i]); + } + } + + // remove the global remote preferences observers + Preferences::RemoveObserver(this, ""); + gfxVars::RemoveReceiver(this); + + if (GPUProcessManager* gpu = GPUProcessManager::Get()) { + // Note: the manager could have shutdown already. + gpu->RemoveListener(this); + } + + RecvRemoveGeolocationListener(); + + mConsoleService = nullptr; + +#ifdef MOZ_ENABLE_PROFILER_SPS + if (mGatherer && !mProfile.IsEmpty()) { + mGatherer->OOPExitProfile(mProfile); + } +#endif + + if (obs) { + RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag(); + + props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), mChildID); + + if (AbnormalShutdown == why) { + Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT, + NS_LITERAL_CSTRING("content"), 1); + + props->SetPropertyAsBool(NS_LITERAL_STRING("abnormal"), true); + +#ifdef MOZ_CRASHREPORTER + // There's a window in which child processes can crash + // after IPC is established, but before a crash reporter + // is created. + if (PCrashReporterParent* p = LoneManagedOrNullAsserts(ManagedPCrashReporterParent())) { + CrashReporterParent* crashReporter = + static_cast<CrashReporterParent*>(p); + + // If we're an app process, always stomp the latest URI + // loaded in the child process with our manifest URL. We + // would rather associate the crashes with apps than + // random child windows loaded in them. + // + // XXX would be nice if we could get both ... + if (!mAppManifestURL.IsEmpty()) { + crashReporter->AnnotateCrashReport(NS_LITERAL_CSTRING("URL"), + NS_ConvertUTF16toUTF8(mAppManifestURL)); + } + + // if mCreatedPairedMinidumps is true, we've already generated + // parent/child dumps for dekstop crashes. + if (!mCreatedPairedMinidumps) { + crashReporter->GenerateCrashReport(this, nullptr); + } + + nsAutoString dumpID(crashReporter->ChildDumpID()); + props->SetPropertyAsAString(NS_LITERAL_STRING("dumpID"), dumpID); + } +#endif + } + nsAutoString cpId; + cpId.AppendInt(static_cast<uint64_t>(this->ChildID())); + obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", cpId.get()); + } + + // Remove any and all idle listeners. + nsCOMPtr<nsIIdleService> idleService = + do_GetService("@mozilla.org/widget/idleservice;1"); + MOZ_ASSERT(idleService); + RefPtr<ParentIdleListener> listener; + for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) { + listener = static_cast<ParentIdleListener*>(mIdleListeners[i].get()); + idleService->RemoveIdleObserver(listener, listener->mTime); + } + mIdleListeners.Clear(); + + MessageLoop::current()-> + PostTask(NewRunnableFunction(DelayedDeleteSubprocess, mSubprocess)); + mSubprocess = nullptr; + + // IPDL rules require actors to live on past ActorDestroy, but it + // may be that the kungFuDeathGrip above is the last reference to + // |this|. If so, when we go out of scope here, we're deleted and + // all hell breaks loose. + // + // This runnable ensures that a reference to |this| lives on at + // least until after the current task finishes running. + NS_DispatchToCurrentThread(new DelayedDeleteContentParentTask(this)); + + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + nsTArray<ContentParentId> childIDArray = + cpm->GetAllChildProcessById(this->ChildID()); + + // Destroy any processes created by this ContentParent + for(uint32_t i = 0; i < childIDArray.Length(); i++) { + ContentParent* cp = cpm->GetContentProcessById(childIDArray[i]); + MessageLoop::current()->PostTask(NewRunnableMethod + <ShutDownMethod>(cp, + &ContentParent::ShutDownProcess, + SEND_SHUTDOWN_MESSAGE)); + } + cpm->RemoveContentProcess(this->ChildID()); + + if (mDriverCrashGuard) { + mDriverCrashGuard->NotifyCrashed(); + } + + // Unregister all the BlobURLs registered by the ContentChild. + for (uint32_t i = 0; i < mBlobURLs.Length(); ++i) { + nsHostObjectProtocolHandler::RemoveDataEntry(mBlobURLs[i]); + } + + mBlobURLs.Clear(); + +#if defined(XP_WIN32) && defined(ACCESSIBILITY) + a11y::AccessibleWrap::ReleaseContentProcessIdFor(ChildID()); +#endif +} + +void +ContentParent::NotifyTabDestroying(const TabId& aTabId, + const ContentParentId& aCpId) +{ + if (XRE_IsParentProcess()) { + // There can be more than one PBrowser for a given app process + // because of popup windows. PBrowsers can also destroy + // concurrently. When all the PBrowsers are destroying, kick off + // another task to ensure the child process *really* shuts down, + // even if the PBrowsers themselves never finish destroying. + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + ContentParent* cp = cpm->GetContentProcessById(aCpId); + if (!cp) { + return; + } + ++cp->mNumDestroyingTabs; + nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(aCpId); + if (static_cast<size_t>(cp->mNumDestroyingTabs) != tabIds.Length()) { + return; + } + + uint32_t numberOfParents = sNonAppContentParents ? sNonAppContentParents->Length() : 0; + int32_t processesToKeepAlive = Preferences::GetInt("dom.ipc.keepProcessesAlive", 0); + if (!cp->mLargeAllocationProcess && static_cast<int32_t>(numberOfParents) <= processesToKeepAlive) { + return; + } + + // We're dying now, so prevent this content process from being + // recycled during its shutdown procedure. + cp->MarkAsDead(); + cp->StartForceKillTimer(); + } else { + ContentChild::GetSingleton()->SendNotifyTabDestroying(aTabId, aCpId); + } +} + +void +ContentParent::StartForceKillTimer() +{ + if (mForceKillTimer || !mIPCOpen) { + return; + } + + int32_t timeoutSecs = Preferences::GetInt("dom.ipc.tabs.shutdownTimeoutSecs", 5); + if (timeoutSecs > 0) { + mForceKillTimer = do_CreateInstance("@mozilla.org/timer;1"); + MOZ_ASSERT(mForceKillTimer); + mForceKillTimer->InitWithFuncCallback(ContentParent::ForceKillTimerCallback, + this, + timeoutSecs * 1000, + nsITimer::TYPE_ONE_SHOT); + } +} + +void +ContentParent::NotifyTabDestroyed(const TabId& aTabId, + bool aNotifiedDestroying) +{ + if (aNotifiedDestroying) { + --mNumDestroyingTabs; + } + + nsTArray<PContentPermissionRequestParent*> parentArray = + nsContentPermissionUtils::GetContentPermissionRequestParentById(aTabId); + + // Need to close undeleted ContentPermissionRequestParents before tab is closed. + for (auto& permissionRequestParent : parentArray) { + Unused << PContentPermissionRequestParent::Send__delete__(permissionRequestParent); + } + + // There can be more than one PBrowser for a given app process + // because of popup windows. When the last one closes, shut + // us down. + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(this->ChildID()); + + // We might want to keep alive some content processes for testing, because of performance + // reasons, but we don't want to alter behavior if the pref is not set. + uint32_t numberOfParents = sNonAppContentParents ? sNonAppContentParents->Length() : 0; + int32_t processesToKeepAlive = Preferences::GetInt("dom.ipc.keepProcessesAlive", 0); + bool shouldKeepAliveAny = !mLargeAllocationProcess && processesToKeepAlive > 0; + bool shouldKeepAliveThis = shouldKeepAliveAny && static_cast<int32_t>(numberOfParents) <= processesToKeepAlive; + + if (tabIds.Length() == 1 && !shouldKeepAliveThis) { + // In the case of normal shutdown, send a shutdown message to child to + // allow it to perform shutdown tasks. + MessageLoop::current()->PostTask(NewRunnableMethod + <ShutDownMethod>(this, + &ContentParent::ShutDownProcess, + SEND_SHUTDOWN_MESSAGE)); + } +} + +jsipc::CPOWManager* +ContentParent::GetCPOWManager() +{ + if (PJavaScriptParent* p = LoneManagedOrNullAsserts(ManagedPJavaScriptParent())) { + return CPOWManagerFor(p); + } + return nullptr; +} + +TestShellParent* +ContentParent::CreateTestShell() +{ + return static_cast<TestShellParent*>(SendPTestShellConstructor()); +} + +bool +ContentParent::DestroyTestShell(TestShellParent* aTestShell) +{ + return PTestShellParent::Send__delete__(aTestShell); +} + +TestShellParent* +ContentParent::GetTestShellSingleton() +{ + PTestShellParent* p = LoneManagedOrNullAsserts(ManagedPTestShellParent()); + return static_cast<TestShellParent*>(p); +} + +void +ContentParent::InitializeMembers() +{ + mSubprocess = nullptr; + mChildID = gContentChildID++; + mGeolocationWatchID = -1; + mNumDestroyingTabs = 0; + mIsAlive = true; + mMetamorphosed = false; + mSendPermissionUpdates = false; + mCalledClose = false; + mCalledKillHard = false; + mCreatedPairedMinidumps = false; + mShutdownPending = false; + mIPCOpen = true; + mHangMonitorActor = nullptr; +} + +bool +ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */) +{ + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); + + std::vector<std::string> extraArgs; + + if (gSafeMode) { + extraArgs.push_back("-safeMode"); + } + + if (!mSubprocess->LaunchAndWaitForProcessHandle(extraArgs)) { + MarkAsDead(); + return false; + } + + Open(mSubprocess->GetChannel(), + base::GetProcId(mSubprocess->GetChildProcessHandle())); + + InitInternal(aInitialPriority, + true, /* Setup off-main thread compositing */ + true /* Send registered chrome */); + + ContentProcessManager::GetSingleton()->AddContentProcess(this); + + ProcessHangMonitor::AddProcess(this); + + // Set a reply timeout for CPOWs. + SetReplyTimeoutMs(Preferences::GetInt("dom.ipc.cpow.timeout", 0)); + + return true; +} + +ContentParent::ContentParent(mozIApplication* aApp, + ContentParent* aOpener, + bool aIsForBrowser, + bool aIsForPreallocated) + : nsIContentParent() + , mOpener(aOpener) + , mIsForBrowser(aIsForBrowser) + , mLargeAllocationProcess(false) +{ + InitializeMembers(); // Perform common initialization. + + // No more than one of !!aApp, aIsForBrowser, aIsForPreallocated should be + // true. + MOZ_ASSERT(!!aApp + aIsForBrowser + aIsForPreallocated <= 1); + + mMetamorphosed = true; + + // Insert ourselves into the global linked list of ContentParent objects. + if (!sContentParents) { + sContentParents = new LinkedList<ContentParent>(); + } + sContentParents->insertBack(this); + + if (aApp) { + aApp->GetManifestURL(mAppManifestURL); + aApp->GetName(mAppName); + } else if (aIsForPreallocated) { + mAppManifestURL = MAGIC_PREALLOCATED_APP_MANIFEST_URL; + } + + // From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the + // PID along with the warning. + nsDebugImpl::SetMultiprocessMode("Parent"); + +#if defined(XP_WIN) && !defined(MOZ_B2G) + // Request Windows message deferral behavior on our side of the PContent + // channel. Generally only applies to the situation where we get caught in + // a deadlock with the plugin process when sending CPOWs. + GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION); +#endif + + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + ChildPrivileges privs = base::PRIVILEGES_DEFAULT; + mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, privs); +} + +ContentParent::~ContentParent() +{ + if (mForceKillTimer) { + mForceKillTimer->Cancel(); + } + + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + // We should be removed from all these lists in ActorDestroy. + MOZ_ASSERT(!sPrivateContent || !sPrivateContent->Contains(this)); + if (mAppManifestURL.IsEmpty()) { + MOZ_ASSERT((!sNonAppContentParents || + !sNonAppContentParents->Contains(this)) && + (!sLargeAllocationContentParents || + !sLargeAllocationContentParents->Contains(this))); + } else { + // In general, we expect sAppContentParents->Get(mAppManifestURL) to be + // nullptr. But it could be that we created another ContentParent for + // this app after we did this->ActorDestroy(), so the right check is + // that sAppContentParents->Get(mAppManifestURL) != this. + MOZ_ASSERT(!sAppContentParents || + sAppContentParents->Get(mAppManifestURL) != this); + } +} + +void +ContentParent::InitInternal(ProcessPriority aInitialPriority, + bool aSetupOffMainThreadCompositing, + bool aSendRegisteredChrome) +{ + if (aSendRegisteredChrome) { + nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService(); + nsChromeRegistryChrome* chromeRegistry = + static_cast<nsChromeRegistryChrome*>(registrySvc.get()); + chromeRegistry->SendRegisteredChrome(this); + } + + if (gAppData) { + nsCString version(gAppData->version); + nsCString buildID(gAppData->buildID); + nsCString name(gAppData->name); + nsCString UAName(gAppData->UAName); + nsCString ID(gAppData->ID); + nsCString vendor(gAppData->vendor); + + // Sending all information to content process. + Unused << SendAppInfo(version, buildID, name, UAName, ID, vendor); + } + + // Initialize the message manager (and load delayed scripts) now that we + // have established communications with the child. + mMessageManager->InitWithCallback(this); + + // Set the subprocess's priority. We do this early on because we're likely + // /lowering/ the process's CPU and memory priority, which it has inherited + // from this process. + // + // This call can cause us to send IPC messages to the child process, so it + // must come after the Open() call above. + ProcessPriorityManager::SetProcessPriority(this, aInitialPriority); + + if (aSetupOffMainThreadCompositing) { + // NB: internally, this will send an IPC message to the child + // process to get it to create the CompositorBridgeChild. This + // message goes through the regular IPC queue for this + // channel, so delivery will happen-before any other messages + // we send. The CompositorBridgeChild must be created before any + // PBrowsers are created, because they rely on the Compositor + // already being around. (Creation is async, so can't happen + // on demand.) + bool useOffMainThreadCompositing = !!CompositorThreadHolder::Loop(); + if (useOffMainThreadCompositing) { + GPUProcessManager* gpm = GPUProcessManager::Get(); + + Endpoint<PCompositorBridgeChild> compositor; + Endpoint<PImageBridgeChild> imageBridge; + Endpoint<PVRManagerChild> vrBridge; + Endpoint<PVideoDecoderManagerChild> videoManager; + + DebugOnly<bool> opened = gpm->CreateContentBridges( + OtherPid(), + &compositor, + &imageBridge, + &vrBridge, + &videoManager); + MOZ_ASSERT(opened); + + Unused << SendInitRendering( + Move(compositor), + Move(imageBridge), + Move(vrBridge), + Move(videoManager)); + + gpm->AddListener(this); + } + } + + if (gAppData) { + // Sending all information to content process. + Unused << SendAppInit(); + } + + nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance(); + if (sheetService) { + // This looks like a lot of work, but in a normal browser session we just + // send two loads. + + for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) { + URIParams uri; + SerializeURI(sheet->GetSheetURI(), uri); + Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AGENT_SHEET); + } + + for (StyleSheet* sheet : *sheetService->UserStyleSheets()) { + URIParams uri; + SerializeURI(sheet->GetSheetURI(), uri); + Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::USER_SHEET); + } + + for (StyleSheet* sheet : *sheetService->AuthorStyleSheets()) { + URIParams uri; + SerializeURI(sheet->GetSheetURI(), uri); + Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AUTHOR_SHEET); + } + } + +#ifdef MOZ_CONTENT_SANDBOX + bool shouldSandbox = true; + MaybeFileDesc brokerFd = void_t(); +#ifdef XP_LINUX + // XXX: Checking the pref here makes it possible to enable/disable sandboxing + // during an active session. Currently the pref is only used for testing + // purpose. If the decision is made to permanently rely on the pref, this + // should be changed so that it is required to restart firefox for the change + // of value to take effect. + shouldSandbox = (Preferences::GetInt("security.sandbox.content.level") > 0) && + !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX"); + + if (shouldSandbox) { + MOZ_ASSERT(!mSandboxBroker); + UniquePtr<SandboxBroker::Policy> policy = + sSandboxBrokerPolicyFactory->GetContentPolicy(Pid()); + if (policy) { + brokerFd = FileDescriptor(); + mSandboxBroker = SandboxBroker::Create(Move(policy), Pid(), brokerFd); + if (!mSandboxBroker) { + KillHard("SandboxBroker::Create failed"); + return; + } + MOZ_ASSERT(static_cast<const FileDescriptor&>(brokerFd).IsValid()); + } + } +#endif + if (shouldSandbox && !SendSetProcessSandbox(brokerFd)) { + KillHard("SandboxInitFailed"); + } +#endif +#if defined(XP_WIN) + // Send the info needed to join the browser process's audio session. + nsID id; + nsString sessionName; + nsString iconPath; + if (NS_SUCCEEDED(mozilla::widget::GetAudioSessionData(id, sessionName, + iconPath))) { + Unused << SendSetAudioSessionData(id, sessionName, iconPath); + } +#endif + + { + RefPtr<ServiceWorkerRegistrar> swr = ServiceWorkerRegistrar::Get(); + MOZ_ASSERT(swr); + + nsTArray<ServiceWorkerRegistrationData> registrations; + swr->GetRegistrations(registrations); + Unused << SendInitServiceWorkers(ServiceWorkerConfiguration(registrations)); + } + + { + nsTArray<BlobURLRegistrationData> registrations; + if (nsHostObjectProtocolHandler::GetAllBlobURLEntries(registrations, + this)) { + Unused << SendInitBlobURLs(registrations); + } + } +} + +bool +ContentParent::IsAlive() const +{ + return mIsAlive; +} + +bool +ContentParent::IsForApp() const +{ + return !mAppManifestURL.IsEmpty(); +} + +int32_t +ContentParent::Pid() const +{ + if (!mSubprocess || !mSubprocess->GetChildProcessHandle()) { + return -1; + } + return base::GetProcId(mSubprocess->GetChildProcessHandle()); +} + +bool +ContentParent::RecvReadPrefsArray(InfallibleTArray<PrefSetting>* aPrefs) +{ + Preferences::GetPreferences(aPrefs); + return true; +} + +bool +ContentParent::RecvGetGfxVars(InfallibleTArray<GfxVarUpdate>* aVars) +{ + // Ensure gfxVars is initialized (for xpcshell tests). + gfxVars::Initialize(); + + *aVars = gfxVars::FetchNonDefaultVars(); + + // Now that content has initialized gfxVars, we can start listening for + // updates. + gfxVars::AddReceiver(this); + return true; +} + +void +ContentParent::OnCompositorUnexpectedShutdown() +{ + GPUProcessManager* gpm = GPUProcessManager::Get(); + + Endpoint<PCompositorBridgeChild> compositor; + Endpoint<PImageBridgeChild> imageBridge; + Endpoint<PVRManagerChild> vrBridge; + Endpoint<PVideoDecoderManagerChild> videoManager; + + DebugOnly<bool> opened = gpm->CreateContentBridges( + OtherPid(), + &compositor, + &imageBridge, + &vrBridge, + &videoManager); + MOZ_ASSERT(opened); + + Unused << SendReinitRendering( + Move(compositor), + Move(imageBridge), + Move(vrBridge), + Move(videoManager)); +} + +void +ContentParent::OnVarChanged(const GfxVarUpdate& aVar) +{ + if (!mIPCOpen) { + return; + } + Unused << SendVarUpdate(aVar); +} + +bool +ContentParent::RecvReadFontList(InfallibleTArray<FontListEntry>* retValue) +{ +#ifdef ANDROID + gfxAndroidPlatform::GetPlatform()->GetSystemFontList(retValue); +#endif + return true; +} + +bool +ContentParent::RecvReadDataStorageArray(const nsString& aFilename, + InfallibleTArray<DataStorageItem>* aValues) +{ + // If we're shutting down, the DataStorage object may have been cleared + // already, and setting it up is pointless anyways since we're about to die. + if (mShutdownPending) { + return true; + } + + // Ensure the SSS is initialized before we try to use its storage. + nsCOMPtr<nsISiteSecurityService> sss = do_GetService("@mozilla.org/ssservice;1"); + + RefPtr<DataStorage> storage = DataStorage::Get(aFilename); + storage->GetAll(aValues); + return true; +} + +bool +ContentParent::RecvReadPermissions(InfallibleTArray<IPC::Permission>* aPermissions) +{ +#ifdef MOZ_PERMISSIONS + nsCOMPtr<nsIPermissionManager> permissionManagerIface = + services::GetPermissionManager(); + nsPermissionManager* permissionManager = + static_cast<nsPermissionManager*>(permissionManagerIface.get()); + MOZ_ASSERT(permissionManager, + "We have no permissionManager in the Chrome process !"); + + nsCOMPtr<nsISimpleEnumerator> enumerator; + DebugOnly<nsresult> rv = permissionManager->GetEnumerator(getter_AddRefs(enumerator)); + MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not get enumerator!"); + while(1) { + bool hasMore; + enumerator->HasMoreElements(&hasMore); + if (!hasMore) + break; + + nsCOMPtr<nsISupports> supp; + enumerator->GetNext(getter_AddRefs(supp)); + nsCOMPtr<nsIPermission> perm = do_QueryInterface(supp); + + nsCOMPtr<nsIPrincipal> principal; + perm->GetPrincipal(getter_AddRefs(principal)); + nsCString origin; + if (principal) { + principal->GetOrigin(origin); + } + nsCString type; + perm->GetType(type); + uint32_t capability; + perm->GetCapability(&capability); + uint32_t expireType; + perm->GetExpireType(&expireType); + int64_t expireTime; + perm->GetExpireTime(&expireTime); + + aPermissions->AppendElement(IPC::Permission(origin, type, + capability, expireType, + expireTime)); + } + + // Ask for future changes + mSendPermissionUpdates = true; +#endif + + return true; +} + +bool +ContentParent::RecvSetClipboard(const IPCDataTransfer& aDataTransfer, + const bool& aIsPrivateData, + const IPC::Principal& aRequestingPrincipal, + const int32_t& aWhichClipboard) +{ + nsresult rv; + nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv)); + NS_ENSURE_SUCCESS(rv, true); + + nsCOMPtr<nsITransferable> trans = + do_CreateInstance("@mozilla.org/widget/transferable;1", &rv); + NS_ENSURE_SUCCESS(rv, true); + trans->Init(nullptr); + + rv = nsContentUtils::IPCTransferableToTransferable(aDataTransfer, + aIsPrivateData, + aRequestingPrincipal, + trans, this, nullptr); + NS_ENSURE_SUCCESS(rv, true); + + clipboard->SetData(trans, nullptr, aWhichClipboard); + return true; +} + +bool +ContentParent::RecvGetClipboard(nsTArray<nsCString>&& aTypes, + const int32_t& aWhichClipboard, + IPCDataTransfer* aDataTransfer) +{ + nsresult rv; + nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv)); + NS_ENSURE_SUCCESS(rv, true); + + nsCOMPtr<nsITransferable> trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv); + NS_ENSURE_SUCCESS(rv, true); + trans->Init(nullptr); + + for (uint32_t t = 0; t < aTypes.Length(); t++) { + trans->AddDataFlavor(aTypes[t].get()); + } + + clipboard->GetData(trans, aWhichClipboard); + nsContentUtils::TransferableToIPCTransferable(trans, aDataTransfer, + true, nullptr, this); + return true; +} + +bool +ContentParent::RecvEmptyClipboard(const int32_t& aWhichClipboard) +{ + nsresult rv; + nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv)); + NS_ENSURE_SUCCESS(rv, true); + + clipboard->EmptyClipboard(aWhichClipboard); + + return true; +} + +bool +ContentParent::RecvClipboardHasType(nsTArray<nsCString>&& aTypes, + const int32_t& aWhichClipboard, + bool* aHasType) +{ + nsresult rv; + nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv)); + NS_ENSURE_SUCCESS(rv, true); + + const char** typesChrs = new const char *[aTypes.Length()]; + for (uint32_t t = 0; t < aTypes.Length(); t++) { + typesChrs[t] = aTypes[t].get(); + } + + clipboard->HasDataMatchingFlavors(typesChrs, aTypes.Length(), + aWhichClipboard, aHasType); + + delete [] typesChrs; + return true; +} + +bool +ContentParent::RecvGetSystemColors(const uint32_t& colorsCount, + InfallibleTArray<uint32_t>* colors) +{ +#ifdef MOZ_WIDGET_ANDROID + NS_ASSERTION(AndroidBridge::Bridge() != nullptr, "AndroidBridge is not available"); + if (AndroidBridge::Bridge() == nullptr) { + // Do not fail - the colors won't be right, but it's not critical + return true; + } + + colors->AppendElements(colorsCount); + + // The array elements correspond to the members of AndroidSystemColors structure, + // so just pass the pointer to the elements buffer + AndroidBridge::Bridge()->GetSystemColors((AndroidSystemColors*)colors->Elements()); +#endif + return true; +} + +bool +ContentParent::RecvGetIconForExtension(const nsCString& aFileExt, + const uint32_t& aIconSize, + InfallibleTArray<uint8_t>* bits) +{ +#ifdef MOZ_WIDGET_ANDROID + NS_ASSERTION(AndroidBridge::Bridge() != nullptr, "AndroidBridge is not available"); + if (AndroidBridge::Bridge() == nullptr) { + // Do not fail - just no icon will be shown + return true; + } + + bits->AppendElements(aIconSize * aIconSize * 4); + + AndroidBridge::Bridge()->GetIconForExtension(aFileExt, aIconSize, bits->Elements()); +#endif + return true; +} + +bool +ContentParent::RecvGetShowPasswordSetting(bool* showPassword) +{ + // default behavior is to show the last password character + *showPassword = true; +#ifdef MOZ_WIDGET_ANDROID + NS_ASSERTION(AndroidBridge::Bridge() != nullptr, "AndroidBridge is not available"); + + *showPassword = java::GeckoAppShell::GetShowPasswordSetting(); +#endif + return true; +} + +bool +ContentParent::RecvFirstIdle() +{ + // When the ContentChild goes idle, it sends us a FirstIdle message + // which we use as a good time to prelaunch another process. If we + // prelaunch any sooner than this, then we'll be competing with the + // child process and slowing it down. + PreallocatedProcessManager::AllocateAfterDelay(); + return true; +} + +bool +ContentParent::RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel, + const bool& aHidden) +{ + RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate(); + MOZ_ASSERT(service); + service->SetDefaultVolumeControlChannelInternal(aChannel, aHidden, mChildID); + return true; +} + +bool +ContentParent::RecvAudioChannelServiceStatus( + const bool& aTelephonyChannel, + const bool& aContentOrNormalChannel, + const bool& aAnyChannel) +{ + RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate(); + MOZ_ASSERT(service); + + service->ChildStatusReceived(mChildID, aTelephonyChannel, + aContentOrNormalChannel, aAnyChannel); + return true; +} + +// We want ContentParent to show up in CC logs for debugging purposes, but we +// don't actually cycle collect it. +NS_IMPL_CYCLE_COLLECTION_0(ContentParent) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(ContentParent) +NS_IMPL_CYCLE_COLLECTING_RELEASE(ContentParent) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ContentParent) + NS_INTERFACE_MAP_ENTRY(nsIContentParent) + NS_INTERFACE_MAP_ENTRY(nsIObserver) + NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionCallback) + NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionErrorCallback) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver) +NS_INTERFACE_MAP_END + +NS_IMETHODIMP +ContentParent::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + if (mSubprocess && (!strcmp(aTopic, "profile-before-change") || + !strcmp(aTopic, "xpcom-shutdown"))) { + // Okay to call ShutDownProcess multiple times. + ShutDownProcess(SEND_SHUTDOWN_MESSAGE); + + // Wait for shutdown to complete, so that we receive any shutdown + // data (e.g. telemetry) from the child before we quit. + // This loop terminate prematurely based on mForceKillTimer. + while (mIPCOpen && !mCalledKillHard) { + NS_ProcessNextEvent(nullptr, true); + } + NS_ASSERTION(!mSubprocess, "Close should have nulled mSubprocess"); + } + +#ifdef MOZ_ENABLE_PROFILER_SPS + // Need to do this before the mIsAlive check to avoid missing profiles. + if (!strcmp(aTopic, "profiler-subprocess-gather")) { + if (mGatherer) { + mGatherer->WillGatherOOPProfile(); + if (mIsAlive && mSubprocess) { + Unused << SendGatherProfile(); + } + } + } + else if (!strcmp(aTopic, "profiler-subprocess")) { + nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject); + if (pse) { + if (!mProfile.IsEmpty()) { + pse->AddSubProfile(mProfile.get()); + mProfile.Truncate(); + } + } + } +#endif + + if (!mIsAlive || !mSubprocess) + return NS_OK; + + // listening for memory pressure event + if (!strcmp(aTopic, "memory-pressure") && + !StringEndsWith(nsDependentString(aData), + NS_LITERAL_STRING("-no-forward"))) { + Unused << SendFlushMemory(nsDependentString(aData)); + } + // listening for remotePrefs... + else if (!strcmp(aTopic, "nsPref:changed")) { + // We know prefs are ASCII here. + NS_LossyConvertUTF16toASCII strData(aData); + + PrefSetting pref(strData, null_t(), null_t()); + Preferences::GetPreference(&pref); + if (!SendPreferenceUpdate(pref)) { + return NS_ERROR_NOT_AVAILABLE; + } + } + else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC)) { + NS_ConvertUTF16toUTF8 dataStr(aData); + const char *offline = dataStr.get(); + if (!SendSetOffline(!strcmp(offline, "true") ? true : false)) { + return NS_ERROR_NOT_AVAILABLE; + } + } + else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC)) { + if (!SendSetConnectivity(NS_LITERAL_STRING("true").Equals(aData))) { + return NS_ERROR_NOT_AVAILABLE; + } + } else if (!strcmp(aTopic, NS_IPC_CAPTIVE_PORTAL_SET_STATE)) { + nsCOMPtr<nsICaptivePortalService> cps = do_QueryInterface(aSubject); + MOZ_ASSERT(cps, "Should QI to a captive portal service"); + if (!cps) { + return NS_ERROR_FAILURE; + } + int32_t state; + cps->GetState(&state); + if (!SendSetCaptivePortalState(state)) { + return NS_ERROR_NOT_AVAILABLE; + } + } + // listening for alert notifications + else if (!strcmp(aTopic, "alertfinished") || + !strcmp(aTopic, "alertclickcallback") || + !strcmp(aTopic, "alertshow") || + !strcmp(aTopic, "alertdisablecallback") || + !strcmp(aTopic, "alertsettingscallback")) { + if (!SendNotifyAlertsObserver(nsDependentCString(aTopic), + nsDependentString(aData))) + return NS_ERROR_NOT_AVAILABLE; + } + else if (!strcmp(aTopic, "child-gc-request")){ + Unused << SendGarbageCollect(); + } + else if (!strcmp(aTopic, "child-cc-request")){ + Unused << SendCycleCollect(); + } + else if (!strcmp(aTopic, "child-mmu-request")){ + Unused << SendMinimizeMemoryUsage(); + } + else if (!strcmp(aTopic, "last-pb-context-exited")) { + Unused << SendLastPrivateDocShellDestroyed(); + } +#ifdef MOZ_WIDGET_GONK + else if(!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) { + nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject); + if (!vol) { + return NS_ERROR_NOT_AVAILABLE; + } + + nsString volName; + nsString mountPoint; + int32_t state; + int32_t mountGeneration; + bool isMediaPresent; + bool isSharing; + bool isFormatting; + bool isFake; + bool isUnmounting; + bool isRemovable; + bool isHotSwappable; + + vol->GetName(volName); + vol->GetMountPoint(mountPoint); + vol->GetState(&state); + vol->GetMountGeneration(&mountGeneration); + vol->GetIsMediaPresent(&isMediaPresent); + vol->GetIsSharing(&isSharing); + vol->GetIsFormatting(&isFormatting); + vol->GetIsFake(&isFake); + vol->GetIsUnmounting(&isUnmounting); + vol->GetIsRemovable(&isRemovable); + vol->GetIsHotSwappable(&isHotSwappable); + + Unused << SendFileSystemUpdate(volName, mountPoint, state, + mountGeneration, isMediaPresent, + isSharing, isFormatting, isFake, + isUnmounting, isRemovable, isHotSwappable); + } else if (!strcmp(aTopic, "phone-state-changed")) { + nsString state(aData); + Unused << SendNotifyPhoneStateChange(state); + } + else if(!strcmp(aTopic, NS_VOLUME_REMOVED)) { + nsString volName(aData); + Unused << SendVolumeRemoved(volName); + } +#endif +#ifdef ACCESSIBILITY + else if (aData && !strcmp(aTopic, "a11y-init-or-shutdown")) { + if (*aData == '1') { + // Make sure accessibility is running in content process when + // accessibility gets initiated in chrome process. +#if !defined(XP_WIN) + Unused << SendActivateA11y(0); +#else + // On Windows we currently only enable a11y in the content process + // for testing purposes. + if (Preferences::GetBool(kForceEnableE10sPref, false)) { + Unused << SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID())); + } +#endif + } else { + // If possible, shut down accessibility in content process when + // accessibility gets shutdown in chrome process. + Unused << SendShutdownA11y(); + } + } +#endif +#ifdef MOZ_ENABLE_PROFILER_SPS + else if (!strcmp(aTopic, "profiler-started")) { + nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject)); + StartProfiler(params); + } + else if (!strcmp(aTopic, "profiler-stopped")) { + mGatherer = nullptr; + Unused << SendStopProfiler(); + } + else if (!strcmp(aTopic, "profiler-paused")) { + Unused << SendPauseProfiler(true); + } + else if (!strcmp(aTopic, "profiler-resumed")) { + Unused << SendPauseProfiler(false); + } +#endif + else if (!strcmp(aTopic, "cacheservice:empty-cache")) { + Unused << SendNotifyEmptyHTTPCache(); + } + return NS_OK; +} + +PGMPServiceParent* +ContentParent::AllocPGMPServiceParent(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess) +{ + return GMPServiceParent::Create(aTransport, aOtherProcess); +} + +PBackgroundParent* +ContentParent::AllocPBackgroundParent(Transport* aTransport, + ProcessId aOtherProcess) +{ + return BackgroundParent::Alloc(this, aTransport, aOtherProcess); +} + +PProcessHangMonitorParent* +ContentParent::AllocPProcessHangMonitorParent(Transport* aTransport, + ProcessId aOtherProcess) +{ + mHangMonitorActor = CreateHangMonitorParent(this, aTransport, aOtherProcess); + return mHangMonitorActor; +} + +bool +ContentParent::RecvGetProcessAttributes(ContentParentId* aCpId, + bool* aIsForApp, bool* aIsForBrowser) +{ + *aCpId = mChildID; + *aIsForApp = IsForApp(); + *aIsForBrowser = mIsForBrowser; + + return true; +} + +bool +ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline, + bool* aIsConnected, + int32_t* aCaptivePortalState, + bool* aIsLangRTL, + bool* aHaveBidiKeyboards, + InfallibleTArray<nsString>* dictionaries, + ClipboardCapabilities* clipboardCaps, + DomainPolicyClone* domainPolicy, + StructuredCloneData* aInitialData, + OptionalURIParams* aUserContentCSSURL) +{ + nsCOMPtr<nsIIOService> io(do_GetIOService()); + MOZ_ASSERT(io, "No IO service?"); + DebugOnly<nsresult> rv = io->GetOffline(aIsOffline); + MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting offline?"); + + rv = io->GetConnectivity(aIsConnected); + MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting connectivity?"); + + *aCaptivePortalState = nsICaptivePortalService::UNKNOWN; + nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CONTRACTID); + if (cps) { + cps->GetState(aCaptivePortalState); + } + + nsIBidiKeyboard* bidi = nsContentUtils::GetBidiKeyboard(); + + *aIsLangRTL = false; + *aHaveBidiKeyboards = false; + if (bidi) { + bidi->IsLangRTL(aIsLangRTL); + bidi->GetHaveBidiKeyboards(aHaveBidiKeyboards); + } + + nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID)); + MOZ_ASSERT(spellChecker, "No spell checker?"); + + spellChecker->GetDictionaryList(dictionaries); + + nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1")); + MOZ_ASSERT(clipboard, "No clipboard?"); + + rv = clipboard->SupportsSelectionClipboard(&clipboardCaps->supportsSelectionClipboard()); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + rv = clipboard->SupportsFindClipboard(&clipboardCaps->supportsFindClipboard()); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + // Let's copy the domain policy from the parent to the child (if it's active). + nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); + NS_ENSURE_TRUE(ssm, false); + ssm->CloneDomainPolicy(domainPolicy); + + if (nsFrameMessageManager* mm = nsFrameMessageManager::sParentProcessManager) { + AutoJSAPI jsapi; + if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) { + return false; + } + JS::RootedValue init(jsapi.cx()); + nsresult result = mm->GetInitialProcessData(jsapi.cx(), &init); + if (NS_FAILED(result)) { + return false; + } + + ErrorResult rv; + aInitialData->Write(jsapi.cx(), init, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return false; + } + } + + // Content processes have no permission to access profile directory, so we + // send the file URL instead. + StyleSheet* ucs = nsLayoutStylesheetCache::For(StyleBackendType::Gecko)->UserContentSheet(); + if (ucs) { + SerializeURI(ucs->GetSheetURI(), *aUserContentCSSURL); + } else { + SerializeURI(nullptr, *aUserContentCSSURL); + } + + return true; +} + +mozilla::jsipc::PJavaScriptParent * +ContentParent::AllocPJavaScriptParent() +{ + MOZ_ASSERT(ManagedPJavaScriptParent().IsEmpty()); + return nsIContentParent::AllocPJavaScriptParent(); +} + +bool +ContentParent::DeallocPJavaScriptParent(PJavaScriptParent *parent) +{ + return nsIContentParent::DeallocPJavaScriptParent(parent); +} + +PBrowserParent* +ContentParent::AllocPBrowserParent(const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpId, + const bool& aIsForApp, + const bool& aIsForBrowser) +{ + return nsIContentParent::AllocPBrowserParent(aTabId, + aContext, + aChromeFlags, + aCpId, + aIsForApp, + aIsForBrowser); +} + +bool +ContentParent::DeallocPBrowserParent(PBrowserParent* frame) +{ + return nsIContentParent::DeallocPBrowserParent(frame); +} + +PBlobParent* +ContentParent::AllocPBlobParent(const BlobConstructorParams& aParams) +{ + return nsIContentParent::AllocPBlobParent(aParams); +} + +bool +ContentParent::DeallocPBlobParent(PBlobParent* aActor) +{ + return nsIContentParent::DeallocPBlobParent(aActor); +} + +bool +ContentParent::RecvPBlobConstructor(PBlobParent* aActor, + const BlobConstructorParams& aParams) +{ + const ParentBlobConstructorParams& params = aParams.get_ParentBlobConstructorParams(); + if (params.blobParams().type() == AnyBlobConstructorParams::TKnownBlobConstructorParams) { + return aActor->SendCreatedFromKnownBlob(); + } + + return true; +} + +mozilla::PRemoteSpellcheckEngineParent * +ContentParent::AllocPRemoteSpellcheckEngineParent() +{ + mozilla::RemoteSpellcheckEngineParent *parent = new mozilla::RemoteSpellcheckEngineParent(); + return parent; +} + +bool +ContentParent::DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent *parent) +{ + delete parent; + return true; +} + +/* static */ void +ContentParent::ForceKillTimerCallback(nsITimer* aTimer, void* aClosure) +{ + // We don't want to time out the content process during XPCShell tests. This + // is the easiest way to ensure that. + if (PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) { + return; + } + + auto self = static_cast<ContentParent*>(aClosure); + self->KillHard("ShutDownKill"); +} + +// WARNING: aReason appears in telemetry, so any new value passed in requires +// data review. +void +ContentParent::KillHard(const char* aReason) +{ + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); + + // On Windows, calling KillHard multiple times causes problems - the + // process handle becomes invalid on the first call, causing a second call + // to crash our process - more details in bug 890840. + if (mCalledKillHard) { + return; + } + mCalledKillHard = true; + mForceKillTimer = nullptr; + +#if defined(MOZ_CRASHREPORTER) && !defined(MOZ_B2G) + // We're about to kill the child process associated with this content. + // Something has gone wrong to get us here, so we generate a minidump + // of the parent and child for submission to the crash server. + if (PCrashReporterParent* p = LoneManagedOrNullAsserts(ManagedPCrashReporterParent())) { + CrashReporterParent* crashReporter = + static_cast<CrashReporterParent*>(p); + // GeneratePairedMinidump creates two minidumps for us - the main + // one is for the content process we're about to kill, and the other + // one is for the main browser process. That second one is the extra + // minidump tagging along, so we have to tell the crash reporter that + // it exists and is being appended. + nsAutoCString additionalDumps("browser"); + crashReporter->AnnotateCrashReport( + NS_LITERAL_CSTRING("additional_minidumps"), + additionalDumps); + nsDependentCString reason(aReason); + crashReporter->AnnotateCrashReport( + NS_LITERAL_CSTRING("ipc_channel_error"), + reason); + + // Generate the report and insert into the queue for submittal. + mCreatedPairedMinidumps = crashReporter->GenerateCompleteMinidump(this); + + Telemetry::Accumulate(Telemetry::SUBPROCESS_KILL_HARD, reason, 1); + } +#endif + ProcessHandle otherProcessHandle; + if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle)) { + NS_ERROR("Failed to open child process when attempting kill."); + return; + } + + if (!KillProcess(otherProcessHandle, base::PROCESS_END_KILLED_BY_USER, + false)) { + NS_WARNING("failed to kill subprocess!"); + } + + if (mSubprocess) { + mSubprocess->SetAlreadyDead(); + } + + // EnsureProcessTerminated has responsibilty for closing otherProcessHandle. + XRE_GetIOMessageLoop()->PostTask( + NewRunnableFunction(&ProcessWatcher::EnsureProcessTerminated, + otherProcessHandle, /*force=*/true)); +} + +bool +ContentParent::IsPreallocated() const +{ + return mAppManifestURL == MAGIC_PREALLOCATED_APP_MANIFEST_URL; +} + +void +ContentParent::FriendlyName(nsAString& aName, bool aAnonymize) +{ + aName.Truncate(); + if (IsPreallocated()) { + aName.AssignLiteral("(Preallocated)"); + } else if (mIsForBrowser) { + aName.AssignLiteral("Browser"); + } else if (aAnonymize) { + aName.AssignLiteral("<anonymized-name>"); + } else if (!mAppName.IsEmpty()) { + aName = mAppName; + } else if (!mAppManifestURL.IsEmpty()) { + aName.AssignLiteral("Unknown app: "); + aName.Append(mAppManifestURL); + } else { + aName.AssignLiteral("???"); + } +} + +PCrashReporterParent* +ContentParent::AllocPCrashReporterParent(const NativeThreadId& tid, + const uint32_t& processType) +{ +#ifdef MOZ_CRASHREPORTER + return new CrashReporterParent(); +#else + return nullptr; +#endif +} + +bool +ContentParent::RecvPCrashReporterConstructor(PCrashReporterParent* actor, + const NativeThreadId& tid, + const uint32_t& processType) +{ + static_cast<CrashReporterParent*>(actor)->SetChildData(tid, processType); + return true; +} + +bool +ContentParent::DeallocPCrashReporterParent(PCrashReporterParent* crashreporter) +{ + delete crashreporter; + return true; +} + +hal_sandbox::PHalParent* +ContentParent::AllocPHalParent() +{ + return hal_sandbox::CreateHalParent(); +} + +bool +ContentParent::DeallocPHalParent(hal_sandbox::PHalParent* aHal) +{ + delete aHal; + return true; +} + +devtools::PHeapSnapshotTempFileHelperParent* +ContentParent::AllocPHeapSnapshotTempFileHelperParent() +{ + return devtools::HeapSnapshotTempFileHelperParent::Create(); +} + +bool +ContentParent::DeallocPHeapSnapshotTempFileHelperParent( + devtools::PHeapSnapshotTempFileHelperParent* aHeapSnapshotHelper) +{ + delete aHeapSnapshotHelper; + return true; +} + +PMemoryReportRequestParent* +ContentParent::AllocPMemoryReportRequestParent(const uint32_t& aGeneration, + const bool &aAnonymize, + const bool &aMinimizeMemoryUsage, + const MaybeFileDesc &aDMDFile) +{ + MemoryReportRequestParent* parent = + new MemoryReportRequestParent(aGeneration); + return parent; +} + +bool +ContentParent::DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor) +{ + delete actor; + return true; +} + +PCycleCollectWithLogsParent* +ContentParent::AllocPCycleCollectWithLogsParent(const bool& aDumpAllTraces, + const FileDescriptor& aGCLog, + const FileDescriptor& aCCLog) +{ + MOZ_CRASH("Don't call this; use ContentParent::CycleCollectWithLogs"); +} + +bool +ContentParent::DeallocPCycleCollectWithLogsParent(PCycleCollectWithLogsParent* aActor) +{ + delete aActor; + return true; +} + +bool +ContentParent::CycleCollectWithLogs(bool aDumpAllTraces, + nsICycleCollectorLogSink* aSink, + nsIDumpGCAndCCLogsCallback* aCallback) +{ + return CycleCollectWithLogsParent::AllocAndSendConstructor(this, + aDumpAllTraces, + aSink, + aCallback); +} + +PTestShellParent* +ContentParent::AllocPTestShellParent() +{ + return new TestShellParent(); +} + +bool +ContentParent::DeallocPTestShellParent(PTestShellParent* shell) +{ + delete shell; + return true; +} + +PNeckoParent* +ContentParent::AllocPNeckoParent() +{ + return new NeckoParent(); +} + +bool +ContentParent::DeallocPNeckoParent(PNeckoParent* necko) +{ + delete necko; + return true; +} + +PPrintingParent* +ContentParent::AllocPPrintingParent() +{ +#ifdef NS_PRINTING + MOZ_ASSERT(!mPrintingParent, + "Only one PrintingParent should be created per process."); + + // Create the printing singleton for this process. + mPrintingParent = new PrintingParent(); + return mPrintingParent.get(); +#else + MOZ_ASSERT_UNREACHABLE("Should never be created if no printing."); + return nullptr; +#endif +} + +bool +ContentParent::DeallocPPrintingParent(PPrintingParent* printing) +{ +#ifdef NS_PRINTING + MOZ_ASSERT(mPrintingParent == printing, + "Only one PrintingParent should have been created per process."); + + mPrintingParent = nullptr; +#else + MOZ_ASSERT_UNREACHABLE("Should never have been created if no printing."); +#endif + return true; +} + +#ifdef NS_PRINTING +already_AddRefed<embedding::PrintingParent> +ContentParent::GetPrintingParent() +{ + MOZ_ASSERT(mPrintingParent); + + RefPtr<embedding::PrintingParent> printingParent = mPrintingParent; + return printingParent.forget(); +} +#endif + +PSendStreamParent* +ContentParent::AllocPSendStreamParent() +{ + return nsIContentParent::AllocPSendStreamParent(); +} + +bool +ContentParent::DeallocPSendStreamParent(PSendStreamParent* aActor) +{ + return nsIContentParent::DeallocPSendStreamParent(aActor); +} + +PScreenManagerParent* +ContentParent::AllocPScreenManagerParent(uint32_t* aNumberOfScreens, + float* aSystemDefaultScale, + bool* aSuccess) +{ + return new ScreenManagerParent(aNumberOfScreens, aSystemDefaultScale, aSuccess); +} + +bool +ContentParent::DeallocPScreenManagerParent(PScreenManagerParent* aActor) +{ + delete aActor; + return true; +} + +PPSMContentDownloaderParent* +ContentParent::AllocPPSMContentDownloaderParent(const uint32_t& aCertType) +{ + RefPtr<PSMContentDownloaderParent> downloader = + new PSMContentDownloaderParent(aCertType); + return downloader.forget().take(); +} + +bool +ContentParent::DeallocPPSMContentDownloaderParent(PPSMContentDownloaderParent* aListener) +{ + auto* listener = static_cast<PSMContentDownloaderParent*>(aListener); + RefPtr<PSMContentDownloaderParent> downloader = dont_AddRef(listener); + return true; +} + +PExternalHelperAppParent* +ContentParent::AllocPExternalHelperAppParent(const OptionalURIParams& uri, + const nsCString& aMimeContentType, + const nsCString& aContentDisposition, + const uint32_t& aContentDispositionHint, + const nsString& aContentDispositionFilename, + const bool& aForceSave, + const int64_t& aContentLength, + const bool& aWasFileChannel, + const OptionalURIParams& aReferrer, + PBrowserParent* aBrowser) +{ + ExternalHelperAppParent *parent = + new ExternalHelperAppParent(uri, aContentLength, aWasFileChannel); + parent->AddRef(); + parent->Init(this, + aMimeContentType, + aContentDisposition, + aContentDispositionHint, + aContentDispositionFilename, + aForceSave, + aReferrer, + aBrowser); + return parent; +} + +bool +ContentParent::DeallocPExternalHelperAppParent(PExternalHelperAppParent* aService) +{ + ExternalHelperAppParent *parent = static_cast<ExternalHelperAppParent *>(aService); + parent->Release(); + return true; +} + +PHandlerServiceParent* +ContentParent::AllocPHandlerServiceParent() +{ + HandlerServiceParent* actor = new HandlerServiceParent(); + actor->AddRef(); + return actor; +} + +bool +ContentParent::DeallocPHandlerServiceParent(PHandlerServiceParent* aHandlerServiceParent) +{ + static_cast<HandlerServiceParent*>(aHandlerServiceParent)->Release(); + return true; +} + +media::PMediaParent* +ContentParent::AllocPMediaParent() +{ + return media::AllocPMediaParent(); +} + +bool +ContentParent::DeallocPMediaParent(media::PMediaParent *aActor) +{ + return media::DeallocPMediaParent(aActor); +} + +PStorageParent* +ContentParent::AllocPStorageParent() +{ + return new DOMStorageDBParent(); +} + +bool +ContentParent::DeallocPStorageParent(PStorageParent* aActor) +{ + DOMStorageDBParent* child = static_cast<DOMStorageDBParent*>(aActor); + child->ReleaseIPDLReference(); + return true; +} + +PPresentationParent* +ContentParent::AllocPPresentationParent() +{ + RefPtr<PresentationParent> actor = new PresentationParent(); + return actor.forget().take(); +} + +bool +ContentParent::DeallocPPresentationParent(PPresentationParent* aActor) +{ + RefPtr<PresentationParent> actor = + dont_AddRef(static_cast<PresentationParent*>(aActor)); + return true; +} + +bool +ContentParent::RecvPPresentationConstructor(PPresentationParent* aActor) +{ + return static_cast<PresentationParent*>(aActor)->Init(mChildID); +} + +PFlyWebPublishedServerParent* +ContentParent::AllocPFlyWebPublishedServerParent(const nsString& name, + const FlyWebPublishOptions& params) +{ + RefPtr<FlyWebPublishedServerParent> actor = + new FlyWebPublishedServerParent(name, params); + return actor.forget().take(); +} + +bool +ContentParent::DeallocPFlyWebPublishedServerParent(PFlyWebPublishedServerParent* aActor) +{ + RefPtr<FlyWebPublishedServerParent> actor = + dont_AddRef(static_cast<FlyWebPublishedServerParent*>(aActor)); + return true; +} + +PSpeechSynthesisParent* +ContentParent::AllocPSpeechSynthesisParent() +{ +#ifdef MOZ_WEBSPEECH + return new mozilla::dom::SpeechSynthesisParent(); +#else + return nullptr; +#endif +} + +bool +ContentParent::DeallocPSpeechSynthesisParent(PSpeechSynthesisParent* aActor) +{ +#ifdef MOZ_WEBSPEECH + delete aActor; + return true; +#else + return false; +#endif +} + +bool +ContentParent::RecvPSpeechSynthesisConstructor(PSpeechSynthesisParent* aActor) +{ +#ifdef MOZ_WEBSPEECH + return true; +#else + return false; +#endif +} + +bool +ContentParent::RecvSpeakerManagerGetSpeakerStatus(bool* aValue) +{ +#ifdef MOZ_WIDGET_GONK + *aValue = false; + RefPtr<SpeakerManagerService> service = + SpeakerManagerService::GetOrCreateSpeakerManagerService(); + MOZ_ASSERT(service); + + *aValue = service->GetSpeakerStatus(); + return true; +#endif + return false; +} + +bool +ContentParent::RecvSpeakerManagerForceSpeaker(const bool& aEnable) +{ +#ifdef MOZ_WIDGET_GONK + RefPtr<SpeakerManagerService> service = + SpeakerManagerService::GetOrCreateSpeakerManagerService(); + MOZ_ASSERT(service); + service->ForceSpeaker(aEnable, mChildID); + + return true; +#endif + return false; +} + +bool +ContentParent::RecvStartVisitedQuery(const URIParams& aURI) +{ + nsCOMPtr<nsIURI> newURI = DeserializeURI(aURI); + if (!newURI) { + return false; + } + nsCOMPtr<IHistory> history = services::GetHistoryService(); + if (history) { + history->RegisterVisitedCallback(newURI, nullptr); + } + return true; +} + + +bool +ContentParent::RecvVisitURI(const URIParams& uri, + const OptionalURIParams& referrer, + const uint32_t& flags) +{ + nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri); + if (!ourURI) { + return false; + } + nsCOMPtr<nsIURI> ourReferrer = DeserializeURI(referrer); + nsCOMPtr<IHistory> history = services::GetHistoryService(); + if (history) { + history->VisitURI(ourURI, ourReferrer, flags); + } + return true; +} + + +bool +ContentParent::RecvSetURITitle(const URIParams& uri, + const nsString& title) +{ + nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri); + if (!ourURI) { + return false; + } + nsCOMPtr<IHistory> history = services::GetHistoryService(); + if (history) { + history->SetURITitle(ourURI, title); + } + return true; +} + +bool +ContentParent::RecvNSSU2FTokenIsCompatibleVersion(const nsString& aVersion, + bool* aIsCompatible) +{ + MOZ_ASSERT(aIsCompatible); + + nsCOMPtr<nsINSSU2FToken> nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID)); + if (NS_WARN_IF(!nssToken)) { + return false; + } + + nsresult rv = nssToken->IsCompatibleVersion(aVersion, aIsCompatible); + return NS_SUCCEEDED(rv); +} + +bool +ContentParent::RecvNSSU2FTokenIsRegistered(nsTArray<uint8_t>&& aKeyHandle, + bool* aIsValidKeyHandle) +{ + MOZ_ASSERT(aIsValidKeyHandle); + + nsCOMPtr<nsINSSU2FToken> nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID)); + if (NS_WARN_IF(!nssToken)) { + return false; + } + + nsresult rv = nssToken->IsRegistered(aKeyHandle.Elements(), aKeyHandle.Length(), + aIsValidKeyHandle); + return NS_SUCCEEDED(rv); +} + +bool +ContentParent::RecvNSSU2FTokenRegister(nsTArray<uint8_t>&& aApplication, + nsTArray<uint8_t>&& aChallenge, + nsTArray<uint8_t>* aRegistration) +{ + MOZ_ASSERT(aRegistration); + + nsCOMPtr<nsINSSU2FToken> nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID)); + if (NS_WARN_IF(!nssToken)) { + return false; + } + uint8_t* buffer; + uint32_t bufferlen; + nsresult rv = nssToken->Register(aApplication.Elements(), aApplication.Length(), + aChallenge.Elements(), aChallenge.Length(), + &buffer, &bufferlen); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + MOZ_ASSERT(buffer); + aRegistration->ReplaceElementsAt(0, aRegistration->Length(), buffer, bufferlen); + free(buffer); + return NS_SUCCEEDED(rv); +} + +bool +ContentParent::RecvNSSU2FTokenSign(nsTArray<uint8_t>&& aApplication, + nsTArray<uint8_t>&& aChallenge, + nsTArray<uint8_t>&& aKeyHandle, + nsTArray<uint8_t>* aSignature) +{ + MOZ_ASSERT(aSignature); + + nsCOMPtr<nsINSSU2FToken> nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID)); + if (NS_WARN_IF(!nssToken)) { + return false; + } + uint8_t* buffer; + uint32_t bufferlen; + nsresult rv = nssToken->Sign(aApplication.Elements(), aApplication.Length(), + aChallenge.Elements(), aChallenge.Length(), + aKeyHandle.Elements(), aKeyHandle.Length(), + &buffer, &bufferlen); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + MOZ_ASSERT(buffer); + aSignature->ReplaceElementsAt(0, aSignature->Length(), buffer, bufferlen); + free(buffer); + return NS_SUCCEEDED(rv); +} + +bool +ContentParent::RecvGetLookAndFeelCache(nsTArray<LookAndFeelInt>* aLookAndFeelIntCache) +{ + *aLookAndFeelIntCache = LookAndFeel::GetIntCache(); + return true; +} + +bool +ContentParent::RecvIsSecureURI(const uint32_t& type, + const URIParams& uri, + const uint32_t& flags, + bool* isSecureURI) +{ + nsCOMPtr<nsISiteSecurityService> sss(do_GetService(NS_SSSERVICE_CONTRACTID)); + if (!sss) { + return false; + } + nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri); + if (!ourURI) { + return false; + } + nsresult rv = sss->IsSecureURI(type, ourURI, flags, nullptr, isSecureURI); + return NS_SUCCEEDED(rv); +} + +bool +ContentParent::RecvAccumulateMixedContentHSTS(const URIParams& aURI, const bool& aActive, const bool& aHSTSPriming) +{ + nsCOMPtr<nsIURI> ourURI = DeserializeURI(aURI); + if (!ourURI) { + return false; + } + nsMixedContentBlocker::AccumulateMixedContentHSTS(ourURI, aActive, aHSTSPriming); + return true; +} + +bool +ContentParent::RecvLoadURIExternal(const URIParams& uri, + PBrowserParent* windowContext) +{ + nsCOMPtr<nsIExternalProtocolService> extProtService(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID)); + if (!extProtService) { + return true; + } + nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri); + if (!ourURI) { + return false; + } + + RefPtr<RemoteWindowContext> context = + new RemoteWindowContext(static_cast<TabParent*>(windowContext)); + extProtService->LoadURI(ourURI, context); + return true; +} + +bool +ContentParent::RecvExtProtocolChannelConnectParent(const uint32_t& registrarId) +{ + nsresult rv; + + // First get the real channel created before redirect on the parent. + nsCOMPtr<nsIChannel> channel; + rv = NS_LinkRedirectChannels(registrarId, nullptr, getter_AddRefs(channel)); + NS_ENSURE_SUCCESS(rv, true); + + nsCOMPtr<nsIParentChannel> parent = do_QueryInterface(channel, &rv); + NS_ENSURE_SUCCESS(rv, true); + + // The channel itself is its own (faked) parent, link it. + rv = NS_LinkRedirectChannels(registrarId, parent, getter_AddRefs(channel)); + NS_ENSURE_SUCCESS(rv, true); + + // Signal the parent channel that it's a redirect-to parent. This will + // make AsyncOpen on it do nothing (what we want). + // Yes, this is a bit of a hack, but I don't think it's necessary to invent + // a new interface just to set this flag on the channel. + parent->SetParentListener(nullptr); + + return true; +} + +bool +ContentParent::HasNotificationPermission(const IPC::Principal& aPrincipal) +{ + return true; +} + +bool +ContentParent::RecvShowAlert(const AlertNotificationType& aAlert) +{ + nsCOMPtr<nsIAlertNotification> alert(dont_AddRef(aAlert)); + if (NS_WARN_IF(!alert)) { + return true; + } + + nsCOMPtr<nsIPrincipal> principal; + nsresult rv = alert->GetPrincipal(getter_AddRefs(principal)); + if (NS_WARN_IF(NS_FAILED(rv)) || + !HasNotificationPermission(IPC::Principal(principal))) { + + return true; + } + + nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID)); + if (sysAlerts) { + sysAlerts->ShowAlert(alert, this); + } + return true; +} + +bool +ContentParent::RecvCloseAlert(const nsString& aName, + const IPC::Principal& aPrincipal) +{ + if (!HasNotificationPermission(aPrincipal)) { + return true; + } + + nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID)); + if (sysAlerts) { + sysAlerts->CloseAlert(aName, aPrincipal); + } + + return true; +} + +bool +ContentParent::RecvDisableNotifications(const IPC::Principal& aPrincipal) +{ + if (HasNotificationPermission(aPrincipal)) { + Unused << Notification::RemovePermission(aPrincipal); + } + return true; +} + +bool +ContentParent::RecvOpenNotificationSettings(const IPC::Principal& aPrincipal) +{ + if (HasNotificationPermission(aPrincipal)) { + Unused << Notification::OpenSettings(aPrincipal); + } + return true; +} + +bool +ContentParent::RecvSyncMessage(const nsString& aMsg, + const ClonedMessageData& aData, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + nsTArray<StructuredCloneData>* aRetvals) +{ + return nsIContentParent::RecvSyncMessage(aMsg, aData, Move(aCpows), + aPrincipal, aRetvals); +} + +bool +ContentParent::RecvRpcMessage(const nsString& aMsg, + const ClonedMessageData& aData, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + nsTArray<StructuredCloneData>* aRetvals) +{ + return nsIContentParent::RecvRpcMessage(aMsg, aData, Move(aCpows), aPrincipal, + aRetvals); +} + +bool +ContentParent::RecvAsyncMessage(const nsString& aMsg, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData) +{ + return nsIContentParent::RecvAsyncMessage(aMsg, Move(aCpows), aPrincipal, + aData); +} + +static int32_t +AddGeolocationListener(nsIDOMGeoPositionCallback* watcher, + nsIDOMGeoPositionErrorCallback* errorCallBack, + bool highAccuracy) +{ + nsCOMPtr<nsIDOMGeoGeolocation> geo = do_GetService("@mozilla.org/geolocation;1"); + if (!geo) { + return -1; + } + + UniquePtr<PositionOptions> options = MakeUnique<PositionOptions>(); + options->mTimeout = 0; + options->mMaximumAge = 0; + options->mEnableHighAccuracy = highAccuracy; + int32_t retval = 1; + geo->WatchPosition(watcher, errorCallBack, Move(options), &retval); + return retval; +} + +bool +ContentParent::RecvAddGeolocationListener(const IPC::Principal& aPrincipal, + const bool& aHighAccuracy) +{ + // To ensure no geolocation updates are skipped, we always force the + // creation of a new listener. + RecvRemoveGeolocationListener(); + mGeolocationWatchID = AddGeolocationListener(this, this, aHighAccuracy); + return true; +} + +bool +ContentParent::RecvRemoveGeolocationListener() +{ + if (mGeolocationWatchID != -1) { + nsCOMPtr<nsIDOMGeoGeolocation> geo = do_GetService("@mozilla.org/geolocation;1"); + if (!geo) { + return true; + } + geo->ClearWatch(mGeolocationWatchID); + mGeolocationWatchID = -1; + } + return true; +} + +bool +ContentParent::RecvSetGeolocationHigherAccuracy(const bool& aEnable) +{ + // This should never be called without a listener already present, + // so this check allows us to forgo securing privileges. + if (mGeolocationWatchID != -1) { + RecvRemoveGeolocationListener(); + mGeolocationWatchID = AddGeolocationListener(this, this, aEnable); + } + return true; +} + +NS_IMETHODIMP +ContentParent::HandleEvent(nsIDOMGeoPosition* postion) +{ + Unused << SendGeolocationUpdate(GeoPosition(postion)); + return NS_OK; +} + +NS_IMETHODIMP +ContentParent::HandleEvent(nsIDOMGeoPositionError* postionError) +{ + int16_t errorCode; + nsresult rv; + rv = postionError->GetCode(&errorCode); + NS_ENSURE_SUCCESS(rv,rv); + Unused << SendGeolocationError(errorCode); + return NS_OK; +} + +nsConsoleService * +ContentParent::GetConsoleService() +{ + if (mConsoleService) { + return mConsoleService.get(); + } + + // XXXkhuey everything about this is terrible. + // Get the ConsoleService by CID rather than ContractID, so that we + // can cast the returned pointer to an nsConsoleService (rather than + // just an nsIConsoleService). This allows us to call the non-idl function + // nsConsoleService::LogMessageWithMode. + NS_DEFINE_CID(consoleServiceCID, NS_CONSOLESERVICE_CID); + nsCOMPtr<nsIConsoleService> consoleService(do_GetService(consoleServiceCID)); + mConsoleService = static_cast<nsConsoleService*>(consoleService.get()); + return mConsoleService.get(); +} + +bool +ContentParent::RecvConsoleMessage(const nsString& aMessage) +{ + RefPtr<nsConsoleService> consoleService = GetConsoleService(); + if (!consoleService) { + return true; + } + + RefPtr<nsConsoleMessage> msg(new nsConsoleMessage(aMessage.get())); + consoleService->LogMessageWithMode(msg, nsConsoleService::SuppressLog); + return true; +} + +bool +ContentParent::RecvScriptError(const nsString& aMessage, + const nsString& aSourceName, + const nsString& aSourceLine, + const uint32_t& aLineNumber, + const uint32_t& aColNumber, + const uint32_t& aFlags, + const nsCString& aCategory) +{ + RefPtr<nsConsoleService> consoleService = GetConsoleService(); + if (!consoleService) { + return true; + } + + nsCOMPtr<nsIScriptError> msg(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID)); + nsresult rv = msg->Init(aMessage, aSourceName, aSourceLine, + aLineNumber, aColNumber, aFlags, aCategory.get()); + if (NS_FAILED(rv)) + return true; + + consoleService->LogMessageWithMode(msg, nsConsoleService::SuppressLog); + return true; +} + +bool +ContentParent::RecvPrivateDocShellsExist(const bool& aExist) +{ + if (!sPrivateContent) + sPrivateContent = new nsTArray<ContentParent*>(); + if (aExist) { + sPrivateContent->AppendElement(this); + } else { + sPrivateContent->RemoveElement(this); + + // Only fire the notification if we have private and non-private + // windows: if privatebrowsing.autostart is true, all windows are + // private. + if (!sPrivateContent->Length() && + !Preferences::GetBool("browser.privatebrowsing.autostart")) { + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr); + delete sPrivateContent; + sPrivateContent = nullptr; + } + } + return true; +} + +bool +ContentParent::DoLoadMessageManagerScript(const nsAString& aURL, + bool aRunInGlobalScope) +{ + MOZ_ASSERT(!aRunInGlobalScope); + return SendLoadProcessScript(nsString(aURL)); +} + +nsresult +ContentParent::DoSendAsyncMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aHelper, + JS::Handle<JSObject *> aCpows, + nsIPrincipal* aPrincipal) +{ + ClonedMessageData data; + if (!BuildClonedMessageDataForParent(this, aHelper, data)) { + return NS_ERROR_DOM_DATA_CLONE_ERR; + } + InfallibleTArray<CpowEntry> cpows; + jsipc::CPOWManager* mgr = GetCPOWManager(); + if (aCpows && (!mgr || !mgr->Wrap(aCx, aCpows, &cpows))) { + return NS_ERROR_UNEXPECTED; + } + if (!SendAsyncMessage(nsString(aMessage), cpows, Principal(aPrincipal), data)) { + return NS_ERROR_UNEXPECTED; + } + return NS_OK; +} + +bool +ContentParent::CheckPermission(const nsAString& aPermission) +{ + return AssertAppProcessPermission(this, NS_ConvertUTF16toUTF8(aPermission).get()); +} + +bool +ContentParent::CheckManifestURL(const nsAString& aManifestURL) +{ + return AssertAppProcessManifestURL(this, NS_ConvertUTF16toUTF8(aManifestURL).get()); +} + +bool +ContentParent::CheckAppHasPermission(const nsAString& aPermission) +{ + return AssertAppHasPermission(this, NS_ConvertUTF16toUTF8(aPermission).get()); +} + +bool +ContentParent::CheckAppHasStatus(unsigned short aStatus) +{ + return AssertAppHasStatus(this, aStatus); +} + +bool +ContentParent::KillChild() +{ + KillHard("KillChild"); + return true; +} + +PBlobParent* +ContentParent::SendPBlobConstructor(PBlobParent* aActor, + const BlobConstructorParams& aParams) +{ + return PContentParent::SendPBlobConstructor(aActor, aParams); +} + +PBrowserParent* +ContentParent::SendPBrowserConstructor(PBrowserParent* aActor, + const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpId, + const bool& aIsForApp, + const bool& aIsForBrowser) +{ + return PContentParent::SendPBrowserConstructor(aActor, + aTabId, + aContext, + aChromeFlags, + aCpId, + aIsForApp, + aIsForBrowser); +} + +bool +ContentParent::RecvCreateFakeVolume(const nsString& fsName, + const nsString& mountPoint) +{ +#ifdef MOZ_WIDGET_GONK + nsresult rv; + nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv); + if (vs) { + vs->CreateFakeVolume(fsName, mountPoint); + } + return true; +#else + NS_WARNING("ContentParent::RecvCreateFakeVolume shouldn't be called when MOZ_WIDGET_GONK is not defined"); + return false; +#endif +} + +bool +ContentParent::RecvSetFakeVolumeState(const nsString& fsName, const int32_t& fsState) +{ +#ifdef MOZ_WIDGET_GONK + nsresult rv; + nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv); + if (vs) { + vs->SetFakeVolumeState(fsName, fsState); + } + return true; +#else + NS_WARNING("ContentParent::RecvSetFakeVolumeState shouldn't be called when MOZ_WIDGET_GONK is not defined"); + return false; +#endif +} + +bool +ContentParent::RecvRemoveFakeVolume(const nsString& fsName) +{ +#ifdef MOZ_WIDGET_GONK + nsresult rv; + nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv); + if (vs) { + vs->RemoveFakeVolume(fsName); + } + return true; +#else + NS_WARNING("ContentParent::RecvRemoveFakeVolume shouldn't be called when MOZ_WIDGET_GONK is not defined"); + return false; +#endif +} + +bool +ContentParent::RecvKeywordToURI(const nsCString& aKeyword, + nsString* aProviderName, + OptionalInputStreamParams* aPostData, + OptionalURIParams* aURI) +{ + *aPostData = void_t(); + *aURI = void_t(); + + nsCOMPtr<nsIURIFixup> fixup = do_GetService(NS_URIFIXUP_CONTRACTID); + if (!fixup) { + return true; + } + + nsCOMPtr<nsIInputStream> postData; + nsCOMPtr<nsIURIFixupInfo> info; + + if (NS_FAILED(fixup->KeywordToURI(aKeyword, getter_AddRefs(postData), + getter_AddRefs(info)))) { + return true; + } + info->GetKeywordProviderName(*aProviderName); + + nsTArray<mozilla::ipc::FileDescriptor> fds; + SerializeInputStream(postData, *aPostData, fds); + MOZ_ASSERT(fds.IsEmpty()); + + nsCOMPtr<nsIURI> uri; + info->GetPreferredURI(getter_AddRefs(uri)); + SerializeURI(uri, *aURI); + return true; +} + +bool +ContentParent::RecvNotifyKeywordSearchLoading(const nsString &aProvider, + const nsString &aKeyword) +{ +#ifdef MOZ_TOOLKIT_SEARCH + nsCOMPtr<nsIBrowserSearchService> searchSvc = do_GetService("@mozilla.org/browser/search-service;1"); + if (searchSvc) { + nsCOMPtr<nsISearchEngine> searchEngine; + searchSvc->GetEngineByName(aProvider, getter_AddRefs(searchEngine)); + if (searchEngine) { + nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); + if (obsSvc) { + // Note that "keyword-search" refers to a search via the url + // bar, not a bookmarks keyword search. + obsSvc->NotifyObservers(searchEngine, "keyword-search", aKeyword.get()); + } + } + } +#endif + return true; +} + +bool +ContentParent::RecvCopyFavicon(const URIParams& aOldURI, + const URIParams& aNewURI, + const IPC::Principal& aLoadingPrincipal, + const bool& aInPrivateBrowsing) +{ + nsCOMPtr<nsIURI> oldURI = DeserializeURI(aOldURI); + if (!oldURI) { + return true; + } + nsCOMPtr<nsIURI> newURI = DeserializeURI(aNewURI); + if (!newURI) { + return true; + } + + nsDocShell::CopyFavicon(oldURI, newURI, aLoadingPrincipal, aInPrivateBrowsing); + return true; +} + +bool +ContentParent::ShouldContinueFromReplyTimeout() +{ + RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get(); + return !monitor || !monitor->ShouldTimeOutCPOWs(); +} + +bool +ContentParent::RecvRecordingDeviceEvents(const nsString& aRecordingStatus, + const nsString& aPageURL, + const bool& aIsAudio, + const bool& aIsVideo) +{ + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + // recording-device-ipc-events needs to gather more information from content process + RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag(); + props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), ChildID()); + props->SetPropertyAsBool(NS_LITERAL_STRING("isApp"), IsForApp()); + props->SetPropertyAsBool(NS_LITERAL_STRING("isAudio"), aIsAudio); + props->SetPropertyAsBool(NS_LITERAL_STRING("isVideo"), aIsVideo); + + nsString requestURL = IsForApp() ? AppManifestURL() : aPageURL; + props->SetPropertyAsAString(NS_LITERAL_STRING("requestURL"), requestURL); + + obs->NotifyObservers((nsIPropertyBag2*) props, + "recording-device-ipc-events", + aRecordingStatus.get()); + } else { + NS_WARNING("Could not get the Observer service for ContentParent::RecvRecordingDeviceEvents."); + } + return true; +} + +bool +ContentParent::RecvGetGraphicsFeatureStatus(const int32_t& aFeature, + int32_t* aStatus, + nsCString* aFailureId, + bool* aSuccess) +{ + nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo(); + if (!gfxInfo) { + *aSuccess = false; + return true; + } + + *aSuccess = NS_SUCCEEDED(gfxInfo->GetFeatureStatus(aFeature, *aFailureId, aStatus)); + return true; +} + +bool +ContentParent::RecvAddIdleObserver(const uint64_t& aObserver, + const uint32_t& aIdleTimeInS) +{ + nsresult rv; + nsCOMPtr<nsIIdleService> idleService = + do_GetService("@mozilla.org/widget/idleservice;1", &rv); + NS_ENSURE_SUCCESS(rv, false); + + RefPtr<ParentIdleListener> listener = + new ParentIdleListener(this, aObserver, aIdleTimeInS); + rv = idleService->AddIdleObserver(listener, aIdleTimeInS); + NS_ENSURE_SUCCESS(rv, false); + mIdleListeners.AppendElement(listener); + return true; +} + +bool +ContentParent::RecvRemoveIdleObserver(const uint64_t& aObserver, + const uint32_t& aIdleTimeInS) +{ + RefPtr<ParentIdleListener> listener; + for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) { + listener = static_cast<ParentIdleListener*>(mIdleListeners[i].get()); + if (listener->mObserver == aObserver && + listener->mTime == aIdleTimeInS) { + nsresult rv; + nsCOMPtr<nsIIdleService> idleService = + do_GetService("@mozilla.org/widget/idleservice;1", &rv); + NS_ENSURE_SUCCESS(rv, false); + idleService->RemoveIdleObserver(listener, aIdleTimeInS); + mIdleListeners.RemoveElementAt(i); + break; + } + } + return true; +} + +bool +ContentParent::RecvBackUpXResources(const FileDescriptor& aXSocketFd) +{ +#ifndef MOZ_X11 + NS_RUNTIMEABORT("This message only makes sense on X11 platforms"); +#else + MOZ_ASSERT(0 > mChildXSocketFdDup.get(), + "Already backed up X resources??"); + if (aXSocketFd.IsValid()) { + auto rawFD = aXSocketFd.ClonePlatformHandle(); + mChildXSocketFdDup.reset(rawFD.release()); + } +#endif + return true; +} + +bool +ContentParent::RecvOpenAnonymousTemporaryFile(FileDescOrError *aFD) +{ + PRFileDesc *prfd; + nsresult rv = NS_OpenAnonymousTemporaryFile(&prfd); + if (NS_WARN_IF(NS_FAILED(rv))) { + // Returning false will kill the child process; instead + // propagate the error and let the child handle it. + *aFD = rv; + return true; + } + *aFD = FileDescriptor(FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(prfd))); + // The FileDescriptor object owns a duplicate of the file handle; we + // must close the original (and clean up the NSPR descriptor). + PR_Close(prfd); + return true; +} + +static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID); + +bool +ContentParent::RecvKeygenProcessValue(const nsString& oldValue, + const nsString& challenge, + const nsString& keytype, + const nsString& keyparams, + nsString* newValue) +{ + nsCOMPtr<nsIFormProcessor> formProcessor = + do_GetService(kFormProcessorCID); + if (!formProcessor) { + newValue->Truncate(); + return true; + } + + formProcessor->ProcessValueIPC(oldValue, challenge, keytype, keyparams, + *newValue); + return true; +} + +bool +ContentParent::RecvKeygenProvideContent(nsString* aAttribute, + nsTArray<nsString>* aContent) +{ + nsCOMPtr<nsIFormProcessor> formProcessor = + do_GetService(kFormProcessorCID); + if (!formProcessor) { + return true; + } + + formProcessor->ProvideContent(NS_LITERAL_STRING("SELECT"), *aContent, + *aAttribute); + return true; +} + +PFileDescriptorSetParent* +ContentParent::AllocPFileDescriptorSetParent(const FileDescriptor& aFD) +{ + return nsIContentParent::AllocPFileDescriptorSetParent(aFD); +} + +bool +ContentParent::DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor) +{ + return nsIContentParent::DeallocPFileDescriptorSetParent(aActor); +} + +bool +ContentParent::IgnoreIPCPrincipal() +{ + static bool sDidAddVarCache = false; + static bool sIgnoreIPCPrincipal = false; + if (!sDidAddVarCache) { + sDidAddVarCache = true; + Preferences::AddBoolVarCache(&sIgnoreIPCPrincipal, + "dom.testing.ignore_ipc_principal", false); + } + return sIgnoreIPCPrincipal; +} + +void +ContentParent::NotifyUpdatedDictionaries() +{ + nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID)); + MOZ_ASSERT(spellChecker, "No spell checker?"); + + InfallibleTArray<nsString> dictionaries; + spellChecker->GetDictionaryList(&dictionaries); + + for (auto* cp : AllProcesses(eLive)) { + Unused << cp->SendUpdateDictionaryList(dictionaries); + } +} + +/*static*/ TabId +ContentParent::AllocateTabId(const TabId& aOpenerTabId, + const IPCTabContext& aContext, + const ContentParentId& aCpId) +{ + TabId tabId; + if (XRE_IsParentProcess()) { + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + tabId = cpm->AllocateTabId(aOpenerTabId, aContext, aCpId); + } + else { + ContentChild::GetSingleton()->SendAllocateTabId(aOpenerTabId, + aContext, + aCpId, + &tabId); + } + return tabId; +} + +/*static*/ void +ContentParent::DeallocateTabId(const TabId& aTabId, + const ContentParentId& aCpId, + bool aMarkedDestroying) +{ + if (XRE_IsParentProcess()) { + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + ContentParent* cp = cpm->GetContentProcessById(aCpId); + + cp->NotifyTabDestroyed(aTabId, aMarkedDestroying); + + ContentProcessManager::GetSingleton()->DeallocateTabId(aCpId, aTabId); + } else { + ContentChild::GetSingleton()->SendDeallocateTabId(aTabId, aCpId, + aMarkedDestroying); + } +} + +bool +ContentParent::RecvAllocateTabId(const TabId& aOpenerTabId, + const IPCTabContext& aContext, + const ContentParentId& aCpId, + TabId* aTabId) +{ + *aTabId = AllocateTabId(aOpenerTabId, aContext, aCpId); + if (!(*aTabId)) { + return false; + } + return true; +} + +bool +ContentParent::RecvDeallocateTabId(const TabId& aTabId, + const ContentParentId& aCpId, + const bool& aMarkedDestroying) +{ + DeallocateTabId(aTabId, aCpId, aMarkedDestroying); + return true; +} + +bool +ContentParent::RecvNotifyTabDestroying(const TabId& aTabId, + const ContentParentId& aCpId) +{ + NotifyTabDestroying(aTabId, aCpId); + return true; +} + +nsTArray<TabContext> +ContentParent::GetManagedTabContext() +{ + return Move(ContentProcessManager::GetSingleton()-> + GetTabContextByContentProcess(this->ChildID())); +} + +mozilla::docshell::POfflineCacheUpdateParent* +ContentParent::AllocPOfflineCacheUpdateParent(const URIParams& aManifestURI, + const URIParams& aDocumentURI, + const PrincipalInfo& aLoadingPrincipalInfo, + const bool& aStickDocument) +{ + RefPtr<mozilla::docshell::OfflineCacheUpdateParent> update = + new mozilla::docshell::OfflineCacheUpdateParent(); + // Use this reference as the IPDL reference. + return update.forget().take(); +} + +bool +ContentParent::RecvPOfflineCacheUpdateConstructor(POfflineCacheUpdateParent* aActor, + const URIParams& aManifestURI, + const URIParams& aDocumentURI, + const PrincipalInfo& aLoadingPrincipal, + const bool& aStickDocument) +{ + MOZ_ASSERT(aActor); + + RefPtr<mozilla::docshell::OfflineCacheUpdateParent> update = + static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(aActor); + + nsresult rv = update->Schedule(aManifestURI, aDocumentURI, aLoadingPrincipal, aStickDocument); + if (NS_FAILED(rv) && IsAlive()) { + // Inform the child of failure. + Unused << update->SendFinish(false, false); + } + + return true; +} + +bool +ContentParent::DeallocPOfflineCacheUpdateParent(POfflineCacheUpdateParent* aActor) +{ + // Reclaim the IPDL reference. + RefPtr<mozilla::docshell::OfflineCacheUpdateParent> update = + dont_AddRef(static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(aActor)); + return true; +} + +PWebrtcGlobalParent * +ContentParent::AllocPWebrtcGlobalParent() +{ +#ifdef MOZ_WEBRTC + return WebrtcGlobalParent::Alloc(); +#else + return nullptr; +#endif +} + +bool +ContentParent::DeallocPWebrtcGlobalParent(PWebrtcGlobalParent *aActor) +{ +#ifdef MOZ_WEBRTC + WebrtcGlobalParent::Dealloc(static_cast<WebrtcGlobalParent*>(aActor)); + return true; +#else + return false; +#endif +} + +bool +ContentParent::RecvSetOfflinePermission(const Principal& aPrincipal) +{ + nsIPrincipal* principal = aPrincipal; + nsContentUtils::MaybeAllowOfflineAppByDefault(principal); + return true; +} + +void +ContentParent::MaybeInvokeDragSession(TabParent* aParent) +{ + nsCOMPtr<nsIDragService> dragService = + do_GetService("@mozilla.org/widget/dragservice;1"); + if (dragService && dragService->MaybeAddChildProcess(this)) { + // We need to send transferable data to child process. + nsCOMPtr<nsIDragSession> session; + dragService->GetCurrentSession(getter_AddRefs(session)); + if (session) { + nsTArray<IPCDataTransfer> dataTransfers; + nsCOMPtr<nsIDOMDataTransfer> domTransfer; + session->GetDataTransfer(getter_AddRefs(domTransfer)); + nsCOMPtr<DataTransfer> transfer = do_QueryInterface(domTransfer); + if (!transfer) { + // Pass eDrop to get DataTransfer with external + // drag formats cached. + transfer = new DataTransfer(nullptr, eDrop, true, -1); + session->SetDataTransfer(transfer); + } + // Note, even though this fills the DataTransfer object with + // external data, the data is usually transfered over IPC lazily when + // needed. + transfer->FillAllExternalData(); + nsCOMPtr<nsILoadContext> lc = aParent ? + aParent->GetLoadContext() : nullptr; + nsCOMPtr<nsIArray> transferables = + transfer->GetTransferables(lc); + nsContentUtils::TransferablesToIPCTransferables(transferables, + dataTransfers, + false, + nullptr, + this); + uint32_t action; + session->GetDragAction(&action); + mozilla::Unused << SendInvokeDragSession(dataTransfers, action); + } + } +} + +bool +ContentParent::RecvUpdateDropEffect(const uint32_t& aDragAction, + const uint32_t& aDropEffect) +{ + nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession(); + if (dragSession) { + dragSession->SetDragAction(aDragAction); + nsCOMPtr<nsIDOMDataTransfer> dt; + dragSession->GetDataTransfer(getter_AddRefs(dt)); + if (dt) { + dt->SetDropEffectInt(aDropEffect); + } + dragSession->UpdateDragEffect(); + } + return true; +} + +PContentPermissionRequestParent* +ContentParent::AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests, + const IPC::Principal& aPrincipal, + const TabId& aTabId) +{ + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + RefPtr<TabParent> tp = + cpm->GetTopLevelTabParentByProcessAndTabId(this->ChildID(), aTabId); + if (!tp) { + return nullptr; + } + + return nsContentPermissionUtils::CreateContentPermissionRequestParent(aRequests, + tp->GetOwnerElement(), + aPrincipal, + aTabId); +} + +bool +ContentParent::DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor) +{ + nsContentPermissionUtils::NotifyRemoveContentPermissionRequestParent(actor); + delete actor; + return true; +} + +PWebBrowserPersistDocumentParent* +ContentParent::AllocPWebBrowserPersistDocumentParent(PBrowserParent* aBrowser, + const uint64_t& aOuterWindowID) +{ + return new WebBrowserPersistDocumentParent(); +} + +bool +ContentParent::DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentParent* aActor) +{ + delete aActor; + return true; +} + +bool +ContentParent::RecvCreateWindow(PBrowserParent* aThisTab, + PBrowserParent* aNewTab, + PRenderFrameParent* aRenderFrame, + const uint32_t& aChromeFlags, + const bool& aCalledFromJS, + const bool& aPositionSpecified, + const bool& aSizeSpecified, + const nsCString& aFeatures, + const nsCString& aBaseURI, + const DocShellOriginAttributes& aOpenerOriginAttributes, + const float& aFullZoom, + nsresult* aResult, + bool* aWindowIsNew, + InfallibleTArray<FrameScriptInfo>* aFrameScripts, + nsCString* aURLToLoad, + TextureFactoryIdentifier* aTextureFactoryIdentifier, + uint64_t* aLayersId) +{ + // We always expect to open a new window here. If we don't, it's an error. + *aWindowIsNew = true; + *aResult = NS_OK; + + // The content process should never be in charge of computing whether or + // not a window should be private or remote - the parent will do that. + const uint32_t badFlags = + nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW + | nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW + | nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME + | nsIWebBrowserChrome::CHROME_REMOTE_WINDOW; + if (!!(aChromeFlags & badFlags)) { + return false; + } + + TabParent* thisTabParent = nullptr; + if (aThisTab) { + thisTabParent = TabParent::GetFrom(aThisTab); + } + + if (NS_WARN_IF(thisTabParent && thisTabParent->IsMozBrowserOrApp())) { + return false; + } + + TabParent* newTab = TabParent::GetFrom(aNewTab); + MOZ_ASSERT(newTab); + + auto destroyNewTabOnError = MakeScopeExit([&] { + if (!*aWindowIsNew || NS_FAILED(*aResult)) { + if (newTab) { + newTab->Destroy(); + } + } + }); + + // Content has requested that we open this new content window, so + // we must have an opener. + newTab->SetHasContentOpener(true); + + nsCOMPtr<nsIContent> frame; + if (thisTabParent) { + frame = do_QueryInterface(thisTabParent->GetOwnerElement()); + } + + nsCOMPtr<nsPIDOMWindowOuter> parent; + if (frame) { + parent = frame->OwnerDoc()->GetWindow(); + + // If our chrome window is in the process of closing, don't try to open a + // new tab in it. + if (parent && parent->Closed()) { + parent = nullptr; + } + } + + nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin; + if (thisTabParent) { + browserDOMWin = thisTabParent->GetBrowserDOMWindow(); + } + + // If we haven't found a chrome window to open in, just use the most recently + // opened one. + if (!parent) { + parent = nsContentUtils::GetMostRecentNonPBWindow(); + if (NS_WARN_IF(!parent)) { + *aResult = NS_ERROR_FAILURE; + return true; + } + + nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(parent); + if (rootChromeWin) { + rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin)); + } + } + + int32_t openLocation = + nsWindowWatcher::GetWindowOpenLocation(parent, aChromeFlags, aCalledFromJS, + aPositionSpecified, aSizeSpecified); + + MOZ_ASSERT(openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB || + openLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW); + + // Opening new tabs is the easy case... + if (openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB) { + if (NS_WARN_IF(!browserDOMWin)) { + *aResult = NS_ERROR_ABORT; + return true; + } + + bool isPrivate = false; + if (thisTabParent) { + nsCOMPtr<nsILoadContext> loadContext = thisTabParent->GetLoadContext(); + loadContext->GetUsePrivateBrowsing(&isPrivate); + } + + nsCOMPtr<nsIOpenURIInFrameParams> params = + new nsOpenURIInFrameParams(aOpenerOriginAttributes); + params->SetReferrer(NS_ConvertUTF8toUTF16(aBaseURI)); + params->SetIsPrivate(isPrivate); + + TabParent::AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad); + + nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner; + browserDOMWin->OpenURIInFrame(nullptr, params, + openLocation, + nsIBrowserDOMWindow::OPEN_NEW, + getter_AddRefs(frameLoaderOwner)); + if (!frameLoaderOwner) { + *aWindowIsNew = false; + } + + newTab->SwapFrameScriptsFrom(*aFrameScripts); + + RenderFrameParent* rfp = static_cast<RenderFrameParent*>(aRenderFrame); + if (!newTab->SetRenderFrame(rfp) || + !newTab->GetRenderFrameInfo(aTextureFactoryIdentifier, aLayersId)) { + *aResult = NS_ERROR_FAILURE; + } + + return true; + } + + TabParent::AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad); + + nsCOMPtr<nsPIWindowWatcher> pwwatch = + do_GetService(NS_WINDOWWATCHER_CONTRACTID, aResult); + + if (NS_WARN_IF(NS_FAILED(*aResult))) { + return true; + } + + nsCOMPtr<nsITabParent> newRemoteTab; + if (!thisTabParent) { + // Because we weren't passed an opener tab, the content process has asked us + // to open a new window that is unrelated to a pre-existing tab. + *aResult = pwwatch->OpenWindowWithoutParent(getter_AddRefs(newRemoteTab)); + } else { + *aResult = pwwatch->OpenWindowWithTabParent(thisTabParent, aFeatures, aCalledFromJS, + aFullZoom, getter_AddRefs(newRemoteTab)); + } + + if (NS_WARN_IF(NS_FAILED(*aResult))) { + return true; + } + + MOZ_ASSERT(TabParent::GetFrom(newRemoteTab) == newTab); + + newTab->SwapFrameScriptsFrom(*aFrameScripts); + + RenderFrameParent* rfp = static_cast<RenderFrameParent*>(aRenderFrame); + if (!newTab->SetRenderFrame(rfp) || + !newTab->GetRenderFrameInfo(aTextureFactoryIdentifier, aLayersId)) { + *aResult = NS_ERROR_FAILURE; + } + + return true; +} + +bool +ContentParent::RecvProfile(const nsCString& aProfile) +{ +#ifdef MOZ_ENABLE_PROFILER_SPS + if (NS_WARN_IF(!mGatherer)) { + return true; + } + mProfile = aProfile; + mGatherer->GatheredOOPProfile(); +#endif + return true; +} + +bool +ContentParent::RecvGetGraphicsDeviceInitData(ContentDeviceData* aOut) +{ + gfxPlatform::GetPlatform()->BuildContentDeviceData(aOut); + return true; +} + +bool +ContentParent::RecvGraphicsError(const nsCString& aError) +{ + gfx::LogForwarder* lf = gfx::Factory::GetLogForwarder(); + if (lf) { + std::stringstream message; + message << "CP+" << aError.get(); + lf->UpdateStringsVector(message.str()); + } + return true; +} + +bool +ContentParent::RecvBeginDriverCrashGuard(const uint32_t& aGuardType, bool* aOutCrashed) +{ + // Only one driver crash guard should be active at a time, per-process. + MOZ_ASSERT(!mDriverCrashGuard); + + UniquePtr<gfx::DriverCrashGuard> guard; + switch (gfx::CrashGuardType(aGuardType)) { + case gfx::CrashGuardType::D3D11Layers: + guard = MakeUnique<gfx::D3D11LayersCrashGuard>(this); + break; + case gfx::CrashGuardType::D3D9Video: + guard = MakeUnique<gfx::D3D9VideoCrashGuard>(this); + break; + case gfx::CrashGuardType::GLContext: + guard = MakeUnique<gfx::GLContextCrashGuard>(this); + break; + case gfx::CrashGuardType::D3D11Video: + guard = MakeUnique<gfx::D3D11VideoCrashGuard>(this); + break; + default: + MOZ_ASSERT_UNREACHABLE("unknown crash guard type"); + return false; + } + + if (guard->Crashed()) { + *aOutCrashed = true; + return true; + } + + *aOutCrashed = false; + mDriverCrashGuard = Move(guard); + return true; +} + +bool +ContentParent::RecvEndDriverCrashGuard(const uint32_t& aGuardType) +{ + mDriverCrashGuard = nullptr; + return true; +} + +bool +ContentParent::RecvGetAndroidSystemInfo(AndroidSystemInfo* aInfo) +{ +#ifdef MOZ_WIDGET_ANDROID + nsSystemInfo::GetAndroidSystemInfo(aInfo); + return true; +#else + MOZ_CRASH("wrong platform!"); + return false; +#endif +} + +bool +ContentParent::RecvNotifyBenchmarkResult(const nsString& aCodecName, + const uint32_t& aDecodeFPS) + +{ + if (aCodecName.EqualsLiteral("VP9")) { + Preferences::SetUint(VP9Benchmark::sBenchmarkFpsPref, aDecodeFPS); + Preferences::SetUint(VP9Benchmark::sBenchmarkFpsVersionCheck, + VP9Benchmark::sBenchmarkVersionID); + } + return true; +} + +void +ContentParent::StartProfiler(nsIProfilerStartParams* aParams) +{ +#ifdef MOZ_ENABLE_PROFILER_SPS + if (NS_WARN_IF(!aParams)) { + return; + } + + ProfilerInitParams ipcParams; + + ipcParams.enabled() = true; + aParams->GetEntries(&ipcParams.entries()); + aParams->GetInterval(&ipcParams.interval()); + ipcParams.features() = aParams->GetFeatures(); + ipcParams.threadFilters() = aParams->GetThreadFilterNames(); + + Unused << SendStartProfiler(ipcParams); + + nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1")); + if (NS_WARN_IF(!profiler)) { + return; + } + nsCOMPtr<nsISupports> gatherer; + profiler->GetProfileGatherer(getter_AddRefs(gatherer)); + mGatherer = static_cast<ProfileGatherer*>(gatherer.get()); +#endif +} + +bool +ContentParent::RecvNotifyPushObservers(const nsCString& aScope, + const IPC::Principal& aPrincipal, + const nsString& aMessageId) +{ + PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing()); + Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers())); + return true; +} + +bool +ContentParent::RecvNotifyPushObserversWithData(const nsCString& aScope, + const IPC::Principal& aPrincipal, + const nsString& aMessageId, + InfallibleTArray<uint8_t>&& aData) +{ + PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Some(aData)); + Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers())); + return true; +} + +bool +ContentParent::RecvNotifyPushSubscriptionChangeObservers(const nsCString& aScope, + const IPC::Principal& aPrincipal) +{ + PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal); + Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers())); + return true; +} + +bool +ContentParent::RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aScope, + const IPC::Principal& aPrincipal) +{ + PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal); + Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers())); + return true; +} + +bool +ContentParent::RecvNotifyLowMemory() +{ +#ifdef MOZ_CRASHREPORTER + nsThread::SaveMemoryReportNearOOM(nsThread::ShouldSaveMemoryReport::kForceReport); +#endif + return true; +} + +/* static */ void +ContentParent::BroadcastBlobURLRegistration(const nsACString& aURI, + BlobImpl* aBlobImpl, + nsIPrincipal* aPrincipal, + ContentParent* aIgnoreThisCP) +{ + nsCString uri(aURI); + IPC::Principal principal(aPrincipal); + + for (auto* cp : AllProcesses(eLive)) { + if (cp != aIgnoreThisCP) { + PBlobParent* blobParent = cp->GetOrCreateActorForBlobImpl(aBlobImpl); + if (blobParent) { + Unused << cp->SendBlobURLRegistration(uri, blobParent, principal); + } + } + } +} + +/* static */ void +ContentParent::BroadcastBlobURLUnregistration(const nsACString& aURI, + ContentParent* aIgnoreThisCP) +{ + nsCString uri(aURI); + + for (auto* cp : AllProcesses(eLive)) { + if (cp != aIgnoreThisCP) { + Unused << cp->SendBlobURLUnregistration(uri); + } + } +} + +bool +ContentParent::RecvStoreAndBroadcastBlobURLRegistration(const nsCString& aURI, + PBlobParent* aBlobParent, + const Principal& aPrincipal) +{ + RefPtr<BlobImpl> blobImpl = + static_cast<BlobParent*>(aBlobParent)->GetBlobImpl(); + if (NS_WARN_IF(!blobImpl)) { + return false; + } + + if (NS_SUCCEEDED(nsHostObjectProtocolHandler::AddDataEntry(aURI, aPrincipal, + blobImpl))) { + BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, this); + + // We want to store this blobURL, so we can unregister it if the child + // crashes. + mBlobURLs.AppendElement(aURI); + } + + BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, this); + return true; +} + +bool +ContentParent::RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI) +{ + nsHostObjectProtocolHandler::RemoveDataEntry(aURI, + false /* Don't broadcast */); + BroadcastBlobURLUnregistration(aURI, this); + mBlobURLs.RemoveElement(aURI); + + return true; +} + +bool +ContentParent::RecvGetA11yContentId(uint32_t* aContentId) +{ +#if defined(XP_WIN32) && defined(ACCESSIBILITY) + *aContentId = a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()); + MOZ_ASSERT(*aContentId); + return true; +#else + return false; +#endif +} + +} // namespace dom +} // namespace mozilla + +NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver) + +NS_IMETHODIMP +ParentIdleListener::Observe(nsISupports*, const char* aTopic, const char16_t* aData) +{ + mozilla::Unused << mParent->SendNotifyIdleObserver(mObserver, + nsDependentCString(aTopic), + nsDependentString(aData)); + return NS_OK; +} + +bool +ContentParent::HandleWindowsMessages(const Message& aMsg) const +{ + MOZ_ASSERT(aMsg.is_sync()); + + // a11y messages can be triggered by windows messages, which means if we + // allow handling windows messages while we wait for the response to a sync + // a11y message we can reenter the ipc message sending code. + if (a11y::PDocAccessible::PDocAccessibleStart < aMsg.type() && + a11y::PDocAccessible::PDocAccessibleEnd > aMsg.type()) { + return false; + } + + return true; +} + +bool +ContentParent::RecvGetFilesRequest(const nsID& aUUID, + const nsString& aDirectoryPath, + const bool& aRecursiveFlag) +{ + MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aUUID)); + + ErrorResult rv; + RefPtr<GetFilesHelper> helper = + GetFilesHelperParent::Create(aUUID, aDirectoryPath, aRecursiveFlag, this, + rv); + + if (NS_WARN_IF(rv.Failed())) { + return SendGetFilesResponse(aUUID, + GetFilesResponseFailure(rv.StealNSResult())); + } + + mGetFilesPendingRequests.Put(aUUID, helper); + return true; +} + +bool +ContentParent::RecvDeleteGetFilesRequest(const nsID& aUUID) +{ + GetFilesHelper* helper = mGetFilesPendingRequests.GetWeak(aUUID); + if (helper) { + mGetFilesPendingRequests.Remove(aUUID); + } + + return true; +} + +void +ContentParent::SendGetFilesResponseAndForget(const nsID& aUUID, + const GetFilesResponseResult& aResult) +{ + GetFilesHelper* helper = mGetFilesPendingRequests.GetWeak(aUUID); + if (helper) { + mGetFilesPendingRequests.Remove(aUUID); + Unused << SendGetFilesResponse(aUUID, aResult); + } +} + +void +ContentParent::ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch) +{ + if (!mHangMonitorActor) { + return; + } + ProcessHangMonitor::ForcePaint(mHangMonitorActor, aTabParent, aLayerObserverEpoch); +} + +bool +ContentParent::RecvAccumulateChildHistogram( + InfallibleTArray<Accumulation>&& aAccumulations) +{ + Telemetry::AccumulateChild(GeckoProcessType_Content, aAccumulations); + return true; +} + +bool +ContentParent::RecvAccumulateChildKeyedHistogram( + InfallibleTArray<KeyedAccumulation>&& aAccumulations) +{ + Telemetry::AccumulateChildKeyed(GeckoProcessType_Content, aAccumulations); + return true; +} diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h new file mode 100644 index 000000000..a3750de1a --- /dev/null +++ b/dom/ipc/ContentParent.h @@ -0,0 +1,1197 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_ContentParent_h +#define mozilla_dom_ContentParent_h + +#include "mozilla/dom/PContentParent.h" +#include "mozilla/dom/nsIContentParent.h" +#include "mozilla/gfx/gfxVarReceiver.h" +#include "mozilla/gfx/GPUProcessListener.h" +#include "mozilla/ipc/GeckoChildProcessHost.h" +#include "mozilla/Attributes.h" +#include "mozilla/FileUtils.h" +#include "mozilla/HalTypes.h" +#include "mozilla/LinkedList.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/UniquePtr.h" + +#include "nsDataHashtable.h" +#include "nsFrameMessageManager.h" +#include "nsHashKeys.h" +#include "nsIObserver.h" +#include "nsIThreadInternal.h" +#include "nsIDOMGeoPositionCallback.h" +#include "nsIDOMGeoPositionErrorCallback.h" +#include "nsRefPtrHashtable.h" +#include "PermissionMessageUtils.h" +#include "DriverCrashGuard.h" + +#define CHILD_PROCESS_SHUTDOWN_MESSAGE NS_LITERAL_STRING("child-process-shutdown") + +class mozIApplication; +class nsConsoleService; +class nsICycleCollectorLogSink; +class nsIDumpGCAndCCLogsCallback; +class nsITimer; +class ParentIdleListener; +class nsIWidget; + +namespace mozilla { +class PRemoteSpellcheckEngineParent; +#ifdef MOZ_ENABLE_PROFILER_SPS +class ProfileGatherer; +#endif + +#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX) +class SandboxBroker; +class SandboxBrokerPolicyFactory; +#endif + +namespace embedding { +class PrintingParent; +} + +namespace ipc { +class OptionalURIParams; +class PFileDescriptorSetParent; +class URIParams; +class TestShellParent; +} // namespace ipc + +namespace jsipc { +class PJavaScriptParent; +} // namespace jsipc + +namespace layers { +struct TextureFactoryIdentifier; +} // namespace layers + +namespace layout { +class PRenderFrameParent; +} // namespace layout + +namespace dom { + +class Element; +class TabParent; +class PStorageParent; +class ClonedMessageData; +class MemoryReport; +class TabContext; +class ContentBridgeParent; +class GetFilesHelper; + +class ContentParent final : public PContentParent + , public nsIContentParent + , public nsIObserver + , public nsIDOMGeoPositionCallback + , public nsIDOMGeoPositionErrorCallback + , public gfx::gfxVarReceiver + , public mozilla::LinkedListElement<ContentParent> + , public gfx::GPUProcessListener +{ + typedef mozilla::ipc::GeckoChildProcessHost GeckoChildProcessHost; + typedef mozilla::ipc::OptionalURIParams OptionalURIParams; + typedef mozilla::ipc::PFileDescriptorSetParent PFileDescriptorSetParent; + typedef mozilla::ipc::TestShellParent TestShellParent; + typedef mozilla::ipc::URIParams URIParams; + typedef mozilla::ipc::PrincipalInfo PrincipalInfo; + typedef mozilla::dom::ClonedMessageData ClonedMessageData; + +public: + + virtual bool IsContentParent() const override { return true; } + + /** + * Start up the content-process machinery. This might include + * scheduling pre-launch tasks. + */ + static void StartUp(); + + /** Shut down the content-process machinery. */ + static void ShutDown(); + + /** + * Ensure that all subprocesses are terminated and their OS + * resources have been reaped. This is synchronous and can be + * very expensive in general. It also bypasses the normal + * shutdown process. + */ + static void JoinAllSubprocesses(); + + static bool PreallocatedProcessReady(); + + /** + * Get or create a content process for: + * 1. browser iframe + * 2. remote xul <browser> + * 3. normal iframe + */ + static already_AddRefed<ContentParent> + GetNewOrUsedBrowserProcess(bool aForBrowserElement = false, + hal::ProcessPriority aPriority = + hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND, + ContentParent* aOpener = nullptr, + bool aLargeAllocationProcess = false); + + /** + * Create a subprocess suitable for use as a preallocated app process. + */ + static already_AddRefed<ContentParent> PreallocateAppProcess(); + + /** + * Get or create a content process for the given TabContext. aFrameElement + * should be the frame/iframe element with which this process will + * associated. + */ + static TabParent* + CreateBrowserOrApp(const TabContext& aContext, + Element* aFrameElement, + ContentParent* aOpenerContentParent, + bool aFreshProcess = false); + + static void GetAll(nsTArray<ContentParent*>& aArray); + + static void GetAllEvenIfDead(nsTArray<ContentParent*>& aArray); + + enum CPIteratorPolicy { + eLive, + eAll + }; + + class ContentParentIterator { + private: + ContentParent* mCurrent; + CPIteratorPolicy mPolicy; + + public: + ContentParentIterator(CPIteratorPolicy aPolicy, ContentParent* aCurrent) + : mCurrent(aCurrent), + mPolicy(aPolicy) + { + } + + ContentParentIterator begin() + { + // Move the cursor to the first element that matches the policy. + while (mPolicy != eAll && mCurrent && !mCurrent->mIsAlive) { + mCurrent = mCurrent->LinkedListElement<ContentParent>::getNext(); + } + + return *this; + } + ContentParentIterator end() + { + return ContentParentIterator(mPolicy, nullptr); + } + + const ContentParentIterator& operator++() + { + MOZ_ASSERT(mCurrent); + do { + mCurrent = mCurrent->LinkedListElement<ContentParent>::getNext(); + } while (mPolicy != eAll && mCurrent && !mCurrent->mIsAlive); + + return *this; + } + + bool operator!=(const ContentParentIterator& aOther) + { + MOZ_ASSERT(mPolicy == aOther.mPolicy); + return mCurrent != aOther.mCurrent; + } + + ContentParent* operator*() + { + return mCurrent; + } + }; + + static ContentParentIterator AllProcesses(CPIteratorPolicy aPolicy) + { + ContentParent* first = + sContentParents ? sContentParents->getFirst() : nullptr; + return ContentParentIterator(aPolicy, first); + } + + static bool IgnoreIPCPrincipal(); + + static void NotifyUpdatedDictionaries(); + +#if defined(XP_WIN) + /** + * Windows helper for firing off an update window request to a plugin + * instance. + * + * aWidget - the eWindowType_plugin_ipc_chrome widget associated with + * this plugin window. + */ + static void SendAsyncUpdate(nsIWidget* aWidget); +#endif + + // Let managees query if it is safe to send messages. + bool IsDestroyed() const { return !mIPCOpen; } + + virtual bool RecvCreateChildProcess(const IPCTabContext& aContext, + const hal::ProcessPriority& aPriority, + const TabId& aOpenerTabId, + ContentParentId* aCpId, + bool* aIsForApp, + bool* aIsForBrowser, + TabId* aTabId) override; + + virtual bool RecvBridgeToChildProcess(const ContentParentId& aCpId) override; + + virtual bool RecvCreateGMPService() override; + + virtual bool RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv, + uint32_t* aRunID) override; + + virtual bool RecvConnectPluginBridge(const uint32_t& aPluginId, + nsresult* aRv) override; + + virtual bool RecvGetBlocklistState(const uint32_t& aPluginId, + uint32_t* aIsBlocklisted) override; + + virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch, + nsresult* aRv, + nsTArray<PluginTag>* aPlugins, + uint32_t* aNewPluginEpoch) override; + + virtual bool RecvUngrabPointer(const uint32_t& aTime) override; + + virtual bool RecvRemovePermission(const IPC::Principal& aPrincipal, + const nsCString& aPermissionType, + nsresult* aRv) override; + + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver) + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_NSIOBSERVER + NS_DECL_NSIDOMGEOPOSITIONCALLBACK + NS_DECL_NSIDOMGEOPOSITIONERRORCALLBACK + + /** + * MessageManagerCallback methods that we override. + */ + virtual bool DoLoadMessageManagerScript(const nsAString& aURL, + bool aRunInGlobalScope) override; + + virtual nsresult DoSendAsyncMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + JS::Handle<JSObject *> aCpows, + nsIPrincipal* aPrincipal) override; + + virtual bool CheckPermission(const nsAString& aPermission) override; + + virtual bool CheckManifestURL(const nsAString& aManifestURL) override; + + virtual bool CheckAppHasPermission(const nsAString& aPermission) override; + + virtual bool CheckAppHasStatus(unsigned short aStatus) override; + + virtual bool KillChild() override; + + /** Notify that a tab is beginning its destruction sequence. */ + static void NotifyTabDestroying(const TabId& aTabId, + const ContentParentId& aCpId); + + /** Notify that a tab was destroyed during normal operation. */ + void NotifyTabDestroyed(const TabId& aTabId, + bool aNotifiedDestroying); + + TestShellParent* CreateTestShell(); + + bool DestroyTestShell(TestShellParent* aTestShell); + + TestShellParent* GetTestShellSingleton(); + + jsipc::CPOWManager* GetCPOWManager() override; + + static TabId + AllocateTabId(const TabId& aOpenerTabId, + const IPCTabContext& aContext, + const ContentParentId& aCpId); + + static void + DeallocateTabId(const TabId& aTabId, + const ContentParentId& aCpId, + bool aMarkedDestroying); + + void ReportChildAlreadyBlocked(); + + bool RequestRunToCompletion(); + + bool IsAlive() const; + + virtual bool IsForApp() const override; + + virtual bool IsForBrowser() const override + { + return mIsForBrowser; + } + + GeckoChildProcessHost* Process() const + { + return mSubprocess; + } + + ContentParent* Opener() const + { + return mOpener; + } + + bool NeedsPermissionsUpdate() const + { + return mSendPermissionUpdates; + } + + /** + * Kill our subprocess and make sure it dies. Should only be used + * in emergency situations since it bypasses the normal shutdown + * process. + * + * WARNING: aReason appears in telemetry, so any new value passed in requires + * data review. + */ + void KillHard(const char* aWhy); + + ContentParentId ChildID() const override { return mChildID; } + + const nsString& AppManifestURL() const { return mAppManifestURL; } + + bool IsPreallocated() const; + + /** + * Get a user-friendly name for this ContentParent. We make no guarantees + * about this name: It might not be unique, apps can spoof special names, + * etc. So please don't use this name to make any decisions about the + * ContentParent based on the value returned here. + */ + void FriendlyName(nsAString& aName, bool aAnonymize = false); + + virtual void OnChannelError() override; + + virtual PCrashReporterParent* + AllocPCrashReporterParent(const NativeThreadId& tid, + const uint32_t& processType) override; + + virtual bool + RecvPCrashReporterConstructor(PCrashReporterParent* actor, + const NativeThreadId& tid, + const uint32_t& processType) override; + + virtual PNeckoParent* AllocPNeckoParent() override; + + virtual bool RecvPNeckoConstructor(PNeckoParent* aActor) override + { + return PContentParent::RecvPNeckoConstructor(aActor); + } + + virtual PPrintingParent* AllocPPrintingParent() override; + + virtual bool DeallocPPrintingParent(PPrintingParent* aActor) override; + +#if defined(NS_PRINTING) + /** + * @return the PrintingParent for this ContentParent. + */ + already_AddRefed<embedding::PrintingParent> GetPrintingParent(); +#endif + + virtual PSendStreamParent* AllocPSendStreamParent() override; + virtual bool DeallocPSendStreamParent(PSendStreamParent* aActor) override; + + virtual PScreenManagerParent* + AllocPScreenManagerParent(uint32_t* aNumberOfScreens, + float* aSystemDefaultScale, + bool* aSuccess) override; + + virtual bool + DeallocPScreenManagerParent(PScreenManagerParent* aActor) override; + + virtual PHalParent* AllocPHalParent() override; + + virtual bool RecvPHalConstructor(PHalParent* aActor) override + { + return PContentParent::RecvPHalConstructor(aActor); + } + + virtual PHeapSnapshotTempFileHelperParent* + AllocPHeapSnapshotTempFileHelperParent() override; + + virtual PStorageParent* AllocPStorageParent() override; + + virtual bool RecvPStorageConstructor(PStorageParent* aActor) override + { + return PContentParent::RecvPStorageConstructor(aActor); + } + + virtual PJavaScriptParent* + AllocPJavaScriptParent() override; + + virtual bool + RecvPJavaScriptConstructor(PJavaScriptParent* aActor) override + { + return PContentParent::RecvPJavaScriptConstructor(aActor); + } + + virtual PRemoteSpellcheckEngineParent* AllocPRemoteSpellcheckEngineParent() override; + + virtual bool RecvRecordingDeviceEvents(const nsString& aRecordingStatus, + const nsString& aPageURL, + const bool& aIsAudio, + const bool& aIsVideo) override; + + bool CycleCollectWithLogs(bool aDumpAllTraces, + nsICycleCollectorLogSink* aSink, + nsIDumpGCAndCCLogsCallback* aCallback); + + virtual PBlobParent* + SendPBlobConstructor(PBlobParent* aActor, + const BlobConstructorParams& aParams) override; + + virtual bool RecvAllocateTabId(const TabId& aOpenerTabId, + const IPCTabContext& aContext, + const ContentParentId& aCpId, + TabId* aTabId) override; + + virtual bool RecvDeallocateTabId(const TabId& aTabId, + const ContentParentId& aCpId, + const bool& aMarkedDestroying) override; + + virtual bool RecvNotifyTabDestroying(const TabId& aTabId, + const ContentParentId& aCpId) override; + + nsTArray<TabContext> GetManagedTabContext(); + + virtual POfflineCacheUpdateParent* + AllocPOfflineCacheUpdateParent(const URIParams& aManifestURI, + const URIParams& aDocumentURI, + const PrincipalInfo& aLoadingPrincipalInfo, + const bool& aStickDocument) override; + + virtual bool + RecvPOfflineCacheUpdateConstructor(POfflineCacheUpdateParent* aActor, + const URIParams& aManifestURI, + const URIParams& aDocumentURI, + const PrincipalInfo& aLoadingPrincipal, + const bool& stickDocument) override; + + virtual bool + DeallocPOfflineCacheUpdateParent(POfflineCacheUpdateParent* aActor) override; + + virtual bool RecvSetOfflinePermission(const IPC::Principal& principal) override; + + virtual bool RecvFinishShutdown() override; + + void MaybeInvokeDragSession(TabParent* aParent); + + virtual PContentPermissionRequestParent* + AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests, + const IPC::Principal& aPrincipal, + const TabId& aTabId) override; + + virtual bool + DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor) override; + + virtual bool HandleWindowsMessages(const Message& aMsg) const override; + + void ForkNewProcess(bool aBlocking); + + virtual bool RecvCreateWindow(PBrowserParent* aThisTabParent, + PBrowserParent* aOpener, + layout::PRenderFrameParent* aRenderFrame, + const uint32_t& aChromeFlags, + const bool& aCalledFromJS, + const bool& aPositionSpecified, + const bool& aSizeSpecified, + const nsCString& aFeatures, + const nsCString& aBaseURI, + const DocShellOriginAttributes& aOpenerOriginAttributes, + const float& aFullZoom, + nsresult* aResult, + bool* aWindowIsNew, + InfallibleTArray<FrameScriptInfo>* aFrameScripts, + nsCString* aURLToLoad, + layers::TextureFactoryIdentifier* aTextureFactoryIdentifier, + uint64_t* aLayersId) override; + + static bool AllocateLayerTreeId(TabParent* aTabParent, uint64_t* aId); + + static void + BroadcastBlobURLRegistration(const nsACString& aURI, + BlobImpl* aBlobImpl, + nsIPrincipal* aPrincipal, + ContentParent* aIgnoreThisCP = nullptr); + + static void + BroadcastBlobURLUnregistration(const nsACString& aURI, + ContentParent* aIgnoreThisCP = nullptr); + + virtual bool + RecvStoreAndBroadcastBlobURLRegistration(const nsCString& aURI, + PBlobParent* aBlobParent, + const Principal& aPrincipal) override; + + virtual bool + RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI) override; + + virtual bool + RecvGetA11yContentId(uint32_t* aContentId) override; + + virtual int32_t Pid() const override; + + // Use the PHangMonitor channel to ask the child to repaint a tab. + void ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch); + +protected: + void OnChannelConnected(int32_t pid) override; + + virtual void ActorDestroy(ActorDestroyReason why) override; + + bool ShouldContinueFromReplyTimeout() override; + + void OnVarChanged(const GfxVarUpdate& aVar) override; + void OnCompositorUnexpectedShutdown() override; + +private: + static nsDataHashtable<nsStringHashKey, ContentParent*> *sAppContentParents; + static nsTArray<ContentParent*>* sNonAppContentParents; + static nsTArray<ContentParent*>* sLargeAllocationContentParents; + static nsTArray<ContentParent*>* sPrivateContent; + static StaticAutoPtr<LinkedList<ContentParent> > sContentParents; + + static void JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses, + Monitor* aMonitor, bool* aDone); + + // Take the preallocated process and transform it into a "real" app process, + // for the specified manifest URL. If there is no preallocated process (or + // if it's dead), create a new one and set aTookPreAllocated to false. + static already_AddRefed<ContentParent> + GetNewOrPreallocatedAppProcess(mozIApplication* aApp, + hal::ProcessPriority aInitialPriority, + ContentParent* aOpener, + /*out*/ bool* aTookPreAllocated = nullptr); + + static hal::ProcessPriority GetInitialProcessPriority(Element* aFrameElement); + + static ContentBridgeParent* CreateContentBridgeParent(const TabContext& aContext, + const hal::ProcessPriority& aPriority, + const TabId& aOpenerTabId, + /*out*/ TabId* aTabId); + + // Hide the raw constructor methods since we don't want client code + // using them. + virtual PBrowserParent* SendPBrowserConstructor( + PBrowserParent* actor, + const TabId& aTabId, + const IPCTabContext& context, + const uint32_t& chromeFlags, + const ContentParentId& aCpId, + const bool& aIsForApp, + const bool& aIsForBrowser) override; + using PContentParent::SendPTestShellConstructor; + + FORWARD_SHMEM_ALLOCATOR_TO(PContentParent) + + // No more than one of !!aApp, aIsForBrowser, and aIsForPreallocated may be + // true. + ContentParent(mozIApplication* aApp, + ContentParent* aOpener, + bool aIsForBrowser, + bool aIsForPreallocated); + + // The common initialization for the constructors. + void InitializeMembers(); + + // Launch the subprocess and associated initialization. + // Returns false if the process fails to start. + bool LaunchSubprocess(hal::ProcessPriority aInitialPriority = hal::PROCESS_PRIORITY_FOREGROUND); + + // Common initialization after sub process launch or adoption. + void InitInternal(ProcessPriority aPriority, + bool aSetupOffMainThreadCompositing, + bool aSendRegisteredChrome); + + virtual ~ContentParent(); + + void Init(); + + // Some information could be sent to content very early, it + // should be send from this function. This function should only be + // called after the process has been transformed to app or browser. + void ForwardKnownInfo(); + + // Set the child process's priority and then check whether the child is + // still alive. Returns true if the process is still alive, and false + // otherwise. If you pass a FOREGROUND* priority here, it's (hopefully) + // unlikely that the process will be killed after this point. + bool SetPriorityAndCheckIsAlive(hal::ProcessPriority aPriority); + + // Transform a pre-allocated app process into a "real" app + // process, for the specified manifest URL. + void TransformPreallocatedIntoApp(ContentParent* aOpener, + const nsAString& aAppManifestURL); + + // Transform a pre-allocated app process into a browser process. If this + // returns false, the child process has died. + void TransformPreallocatedIntoBrowser(ContentParent* aOpener); + + /** + * Mark this ContentParent as dead for the purposes of Get*(). + * This method is idempotent. + */ + void MarkAsDead(); + + /** + * How we will shut down this ContentParent and its subprocess. + */ + enum ShutDownMethod + { + // Send a shutdown message and wait for FinishShutdown call back. + SEND_SHUTDOWN_MESSAGE, + // Close the channel ourselves and let the subprocess clean up itself. + CLOSE_CHANNEL, + // Close the channel with error and let the subprocess clean up itself. + CLOSE_CHANNEL_WITH_ERROR, + }; + + /** + * Exit the subprocess and vamoose. After this call IsAlive() + * will return false and this ContentParent will not be returned + * by the Get*() funtions. However, the shutdown sequence itself + * may be asynchronous. + * + * If aMethod is CLOSE_CHANNEL_WITH_ERROR and this is the first call + * to ShutDownProcess, then we'll close our channel using CloseWithError() + * rather than vanilla Close(). CloseWithError() indicates to IPC that this + * is an abnormal shutdown (e.g. a crash). + */ + void ShutDownProcess(ShutDownMethod aMethod); + + // Perform any steps necesssary to gracefully shtudown the message + // manager and null out mMessageManager. + void ShutDownMessageManager(); + + // Start the force-kill timer on shutdown. + void StartForceKillTimer(); + + static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure); + + static bool AllocateLayerTreeId(ContentParent* aContent, + TabParent* aTopLevel, const TabId& aTabId, + uint64_t* aId); + + PGMPServiceParent* + AllocPGMPServiceParent(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess) override; + + PBackgroundParent* + AllocPBackgroundParent(Transport* aTransport, ProcessId aOtherProcess) + override; + + PProcessHangMonitorParent* + AllocPProcessHangMonitorParent(Transport* aTransport, + ProcessId aOtherProcess) override; + + virtual bool RecvGetProcessAttributes(ContentParentId* aCpId, + bool* aIsForApp, + bool* aIsForBrowser) override; + + virtual bool + RecvGetXPCOMProcessAttributes(bool* aIsOffline, + bool* aIsConnected, + int32_t* aCaptivePortalState, + bool* aIsLangRTL, + bool* aHaveBidiKeyboards, + InfallibleTArray<nsString>* dictionaries, + ClipboardCapabilities* clipboardCaps, + DomainPolicyClone* domainPolicy, + StructuredCloneData* initialData, + OptionalURIParams* aUserContentSheetURL) override; + + virtual bool + DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) override; + + virtual bool + DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent*) override; + + virtual PBrowserParent* AllocPBrowserParent(const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpId, + const bool& aIsForApp, + const bool& aIsForBrowser) override; + + virtual bool DeallocPBrowserParent(PBrowserParent* frame) override; + + virtual PBlobParent* + AllocPBlobParent(const BlobConstructorParams& aParams) override; + + virtual bool DeallocPBlobParent(PBlobParent* aActor) override; + + virtual bool + RecvPBlobConstructor(PBlobParent* aActor, + const BlobConstructorParams& params) override; + + virtual bool + DeallocPCrashReporterParent(PCrashReporterParent* crashreporter) override; + + virtual bool RecvNSSU2FTokenIsCompatibleVersion(const nsString& aVersion, + bool* aIsCompatible) override; + + virtual bool RecvNSSU2FTokenIsRegistered(nsTArray<uint8_t>&& aKeyHandle, + bool* aIsValidKeyHandle) override; + + virtual bool RecvNSSU2FTokenRegister(nsTArray<uint8_t>&& aApplication, + nsTArray<uint8_t>&& aChallenge, + nsTArray<uint8_t>* aRegistration) override; + + virtual bool RecvNSSU2FTokenSign(nsTArray<uint8_t>&& aApplication, + nsTArray<uint8_t>&& aChallenge, + nsTArray<uint8_t>&& aKeyHandle, + nsTArray<uint8_t>* aSignature) override; + + virtual bool RecvIsSecureURI(const uint32_t& aType, const URIParams& aURI, + const uint32_t& aFlags, bool* aIsSecureURI) override; + + virtual bool RecvAccumulateMixedContentHSTS(const URIParams& aURI, + const bool& aActive, + const bool& aHSTSPriming) override; + + virtual bool DeallocPHalParent(PHalParent*) override; + + virtual bool + DeallocPHeapSnapshotTempFileHelperParent(PHeapSnapshotTempFileHelperParent*) override; + + virtual PMemoryReportRequestParent* + AllocPMemoryReportRequestParent(const uint32_t& aGeneration, + const bool &aAnonymize, + const bool &aMinimizeMemoryUsage, + const MaybeFileDesc &aDMDFile) override; + + virtual bool + DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor) override; + + virtual PCycleCollectWithLogsParent* + AllocPCycleCollectWithLogsParent(const bool& aDumpAllTraces, + const FileDescriptor& aGCLog, + const FileDescriptor& aCCLog) override; + + virtual bool + DeallocPCycleCollectWithLogsParent(PCycleCollectWithLogsParent* aActor) override; + + virtual PTestShellParent* AllocPTestShellParent() override; + + virtual bool DeallocPTestShellParent(PTestShellParent* shell) override; + + virtual bool DeallocPNeckoParent(PNeckoParent* necko) override; + + virtual PPSMContentDownloaderParent* + AllocPPSMContentDownloaderParent(const uint32_t& aCertType) override; + + virtual bool + DeallocPPSMContentDownloaderParent(PPSMContentDownloaderParent* aDownloader) override; + + virtual PExternalHelperAppParent* + AllocPExternalHelperAppParent(const OptionalURIParams& aUri, + const nsCString& aMimeContentType, + const nsCString& aContentDisposition, + const uint32_t& aContentDispositionHint, + const nsString& aContentDispositionFilename, + const bool& aForceSave, + const int64_t& aContentLength, + const bool& aWasFileChannel, + const OptionalURIParams& aReferrer, + PBrowserParent* aBrowser) override; + + virtual bool + DeallocPExternalHelperAppParent(PExternalHelperAppParent* aService) override; + + virtual PHandlerServiceParent* AllocPHandlerServiceParent() override; + + virtual bool DeallocPHandlerServiceParent(PHandlerServiceParent*) override; + + virtual PMediaParent* AllocPMediaParent() override; + + virtual bool DeallocPMediaParent(PMediaParent* aActor) override; + + virtual bool DeallocPStorageParent(PStorageParent* aActor) override; + + virtual PPresentationParent* AllocPPresentationParent() override; + + virtual bool DeallocPPresentationParent(PPresentationParent* aActor) override; + + virtual bool RecvPPresentationConstructor(PPresentationParent* aActor) override; + + virtual PFlyWebPublishedServerParent* + AllocPFlyWebPublishedServerParent(const nsString& name, + const FlyWebPublishOptions& params) override; + + virtual bool DeallocPFlyWebPublishedServerParent(PFlyWebPublishedServerParent* aActor) override; + + virtual PSpeechSynthesisParent* AllocPSpeechSynthesisParent() override; + + virtual bool + DeallocPSpeechSynthesisParent(PSpeechSynthesisParent* aActor) override; + + virtual bool + RecvPSpeechSynthesisConstructor(PSpeechSynthesisParent* aActor) override; + + virtual PWebBrowserPersistDocumentParent* + AllocPWebBrowserPersistDocumentParent(PBrowserParent* aBrowser, + const uint64_t& aOuterWindowID) override; + + virtual bool + DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentParent* aActor) override; + + virtual bool RecvReadPrefsArray(InfallibleTArray<PrefSetting>* aPrefs) override; + virtual bool RecvGetGfxVars(InfallibleTArray<GfxVarUpdate>* aVars) override; + + virtual bool RecvReadFontList(InfallibleTArray<FontListEntry>* retValue) override; + + virtual bool RecvReadDataStorageArray(const nsString& aFilename, + InfallibleTArray<DataStorageItem>* aValues) override; + + virtual bool RecvReadPermissions(InfallibleTArray<IPC::Permission>* aPermissions) override; + + virtual bool RecvSetClipboard(const IPCDataTransfer& aDataTransfer, + const bool& aIsPrivateData, + const IPC::Principal& aRequestingPrincipal, + const int32_t& aWhichClipboard) override; + + virtual bool RecvGetClipboard(nsTArray<nsCString>&& aTypes, + const int32_t& aWhichClipboard, + IPCDataTransfer* aDataTransfer) override; + + virtual bool RecvEmptyClipboard(const int32_t& aWhichClipboard) override; + + virtual bool RecvClipboardHasType(nsTArray<nsCString>&& aTypes, + const int32_t& aWhichClipboard, + bool* aHasType) override; + + virtual bool RecvGetSystemColors(const uint32_t& colorsCount, + InfallibleTArray<uint32_t>* colors) override; + + virtual bool RecvGetIconForExtension(const nsCString& aFileExt, + const uint32_t& aIconSize, + InfallibleTArray<uint8_t>* bits) override; + + virtual bool RecvGetShowPasswordSetting(bool* showPassword) override; + + virtual bool RecvStartVisitedQuery(const URIParams& uri) override; + + virtual bool RecvVisitURI(const URIParams& uri, + const OptionalURIParams& referrer, + const uint32_t& flags) override; + + virtual bool RecvSetURITitle(const URIParams& uri, + const nsString& title) override; + + bool HasNotificationPermission(const IPC::Principal& aPrincipal); + + virtual bool RecvShowAlert(const AlertNotificationType& aAlert) override; + + virtual bool RecvCloseAlert(const nsString& aName, + const IPC::Principal& aPrincipal) override; + + virtual bool RecvDisableNotifications(const IPC::Principal& aPrincipal) override; + + virtual bool RecvOpenNotificationSettings(const IPC::Principal& aPrincipal) override; + + virtual bool RecvLoadURIExternal(const URIParams& uri, + PBrowserParent* windowContext) override; + virtual bool RecvExtProtocolChannelConnectParent(const uint32_t& registrarId) override; + + virtual bool RecvSyncMessage(const nsString& aMsg, + const ClonedMessageData& aData, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + nsTArray<StructuredCloneData>* aRetvals) override; + + virtual bool RecvRpcMessage(const nsString& aMsg, + const ClonedMessageData& aData, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + nsTArray<StructuredCloneData>* aRetvals) override; + + virtual bool RecvAsyncMessage(const nsString& aMsg, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData) override; + + virtual bool RecvAddGeolocationListener(const IPC::Principal& aPrincipal, + const bool& aHighAccuracy) override; + virtual bool RecvRemoveGeolocationListener() override; + + virtual bool RecvSetGeolocationHigherAccuracy(const bool& aEnable) override; + + virtual bool RecvConsoleMessage(const nsString& aMessage) override; + + virtual bool 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) override; + + virtual bool RecvPrivateDocShellsExist(const bool& aExist) override; + + virtual bool RecvFirstIdle() override; + + virtual bool RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel, + const bool& aHidden) override; + + virtual bool RecvAudioChannelServiceStatus(const bool& aTelephonyChannel, + const bool& aContentOrNormalChannel, + const bool& aAnyChannel) override; + + virtual bool RecvGetLookAndFeelCache(nsTArray<LookAndFeelInt>* aLookAndFeelIntCache) override; + + virtual bool RecvSpeakerManagerGetSpeakerStatus(bool* aValue) override; + + virtual bool RecvSpeakerManagerForceSpeaker(const bool& aEnable) override; + + virtual bool RecvCreateFakeVolume(const nsString& aFsName, + const nsString& aMountPoint) override; + + virtual bool RecvSetFakeVolumeState(const nsString& aFsName, + const int32_t& aFsState) override; + + virtual bool RecvRemoveFakeVolume(const nsString& fsName) override; + + virtual bool RecvKeywordToURI(const nsCString& aKeyword, + nsString* aProviderName, + OptionalInputStreamParams* aPostData, + OptionalURIParams* aURI) override; + + virtual bool RecvNotifyKeywordSearchLoading(const nsString &aProvider, + const nsString &aKeyword) override; + + virtual bool RecvCopyFavicon(const URIParams& aOldURI, + const URIParams& aNewURI, + const IPC::Principal& aLoadingPrincipal, + const bool& aInPrivateBrowsing) override; + + virtual void ProcessingError(Result aCode, const char* aMsgName) override; + + virtual bool RecvAllocateLayerTreeId(const ContentParentId& aCpId, + const TabId& aTabId, + uint64_t* aId) override; + + virtual bool RecvDeallocateLayerTreeId(const uint64_t& aId) override; + + virtual bool RecvGetGraphicsFeatureStatus(const int32_t& aFeature, + int32_t* aStatus, + nsCString* aFailureId, + bool* aSuccess) override; + + virtual bool RecvGraphicsError(const nsCString& aError) override; + + virtual bool + RecvBeginDriverCrashGuard(const uint32_t& aGuardType, + bool* aOutCrashed) override; + + virtual bool RecvEndDriverCrashGuard(const uint32_t& aGuardType) override; + + virtual bool RecvAddIdleObserver(const uint64_t& observerId, + const uint32_t& aIdleTimeInS) override; + + virtual bool RecvRemoveIdleObserver(const uint64_t& observerId, + const uint32_t& aIdleTimeInS) override; + + virtual bool + RecvBackUpXResources(const FileDescriptor& aXSocketFd) override; + + virtual bool + RecvOpenAnonymousTemporaryFile(FileDescOrError* aFD) override; + + virtual bool + RecvKeygenProcessValue(const nsString& oldValue, const nsString& challenge, + const nsString& keytype, const nsString& keyparams, + nsString* newValue) override; + + virtual bool + RecvKeygenProvideContent(nsString* aAttribute, + nsTArray<nsString>* aContent) override; + + virtual PFileDescriptorSetParent* + AllocPFileDescriptorSetParent(const mozilla::ipc::FileDescriptor&) override; + + virtual bool + DeallocPFileDescriptorSetParent(PFileDescriptorSetParent*) override; + + virtual PWebrtcGlobalParent* AllocPWebrtcGlobalParent() override; + virtual bool DeallocPWebrtcGlobalParent(PWebrtcGlobalParent *aActor) override; + + + virtual bool RecvUpdateDropEffect(const uint32_t& aDragAction, + const uint32_t& aDropEffect) override; + + virtual bool RecvProfile(const nsCString& aProfile) override; + + virtual bool RecvGetGraphicsDeviceInitData(ContentDeviceData* aOut) override; + + void StartProfiler(nsIProfilerStartParams* aParams); + + virtual bool RecvGetAndroidSystemInfo(AndroidSystemInfo* aInfo) override; + + virtual bool RecvNotifyBenchmarkResult(const nsString& aCodecName, + const uint32_t& aDecodeFPS) override; + + virtual bool RecvNotifyPushObservers(const nsCString& aScope, + const IPC::Principal& aPrincipal, + const nsString& aMessageId) override; + + virtual bool RecvNotifyPushObserversWithData(const nsCString& aScope, + const IPC::Principal& aPrincipal, + const nsString& aMessageId, + InfallibleTArray<uint8_t>&& aData) override; + + virtual bool RecvNotifyPushSubscriptionChangeObservers(const nsCString& aScope, + const IPC::Principal& aPrincipal) override; + + virtual bool RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aScope, + const IPC::Principal& aPrincipal) override; + + virtual bool RecvNotifyLowMemory() override; + + virtual bool RecvGetFilesRequest(const nsID& aID, + const nsString& aDirectoryPath, + const bool& aRecursiveFlag) override; + + virtual bool RecvDeleteGetFilesRequest(const nsID& aID) override; + + virtual bool RecvAccumulateChildHistogram( + InfallibleTArray<Accumulation>&& aAccumulations) override; + virtual bool RecvAccumulateChildKeyedHistogram( + InfallibleTArray<KeyedAccumulation>&& aAccumulations) override; +public: + void SendGetFilesResponseAndForget(const nsID& aID, + const GetFilesResponseResult& aResult); + +private: + + // If you add strong pointers to cycle collected objects here, be sure to + // release these objects in ShutDownProcess. See the comment there for more + // details. + + GeckoChildProcessHost* mSubprocess; + ContentParent* mOpener; + + ContentParentId mChildID; + int32_t mGeolocationWatchID; + + nsString mAppManifestURL; + + nsCString mKillHardAnnotation; + + /** + * We cache mAppName instead of looking it up using mAppManifestURL when we + * need it because it turns out that getting an app from the apps service is + * expensive. + */ + nsString mAppName; + + // After we initiate shutdown, we also start a timer to ensure + // that even content processes that are 100% blocked (say from + // SIGSTOP), are still killed eventually. This task enforces that + // timer. + nsCOMPtr<nsITimer> mForceKillTimer; + // How many tabs we're waiting to finish their destruction + // sequence. Precisely, how many TabParents have called + // NotifyTabDestroying() but not called NotifyTabDestroyed(). + int32_t mNumDestroyingTabs; + // True only while this is ready to be used to host remote tabs. + // This must not be used for new purposes after mIsAlive goes to + // false, but some previously scheduled IPC traffic may still pass + // through. + bool mIsAlive; + + // True only the if process is already a browser or app or has + // been transformed into one. + bool mMetamorphosed; + + bool mSendPermissionUpdates; + bool mIsForBrowser; + + // These variables track whether we've called Close() and KillHard() on our + // channel. + bool mCalledClose; + bool mCalledKillHard; + bool mCreatedPairedMinidumps; + bool mShutdownPending; + bool mIPCOpen; + + friend class CrashReporterParent; + + RefPtr<nsConsoleService> mConsoleService; + nsConsoleService* GetConsoleService(); + + nsTArray<nsCOMPtr<nsIObserver>> mIdleListeners; + +#ifdef MOZ_X11 + // Dup of child's X socket, used to scope its resources to this + // object instead of the child process's lifetime. + ScopedClose mChildXSocketFdDup; +#endif + + PProcessHangMonitorParent* mHangMonitorActor; + +#ifdef MOZ_ENABLE_PROFILER_SPS + RefPtr<mozilla::ProfileGatherer> mGatherer; +#endif + nsCString mProfile; + + UniquePtr<gfx::DriverCrashGuard> mDriverCrashGuard; + +#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX) + mozilla::UniquePtr<SandboxBroker> mSandboxBroker; + static mozilla::UniquePtr<SandboxBrokerPolicyFactory> + sSandboxBrokerPolicyFactory; +#endif + +#ifdef NS_PRINTING + RefPtr<embedding::PrintingParent> mPrintingParent; +#endif + + // This hashtable is used to run GetFilesHelper objects in the parent process. + // GetFilesHelper can be aborted by receiving RecvDeleteGetFilesRequest. + nsRefPtrHashtable<nsIDHashKey, GetFilesHelper> mGetFilesPendingRequests; + + nsTArray<nsCString> mBlobURLs; + bool mLargeAllocationProcess; +}; + +} // namespace dom +} // namespace mozilla + +class ParentIdleListener : public nsIObserver +{ + friend class mozilla::dom::ContentParent; + +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + ParentIdleListener(mozilla::dom::ContentParent* aParent, + uint64_t aObserver, uint32_t aTime) + : mParent(aParent), mObserver(aObserver), mTime(aTime) + {} + +private: + virtual ~ParentIdleListener() {} + + RefPtr<mozilla::dom::ContentParent> mParent; + uint64_t mObserver; + uint32_t mTime; +}; + +#endif // mozilla_dom_ContentParent_h diff --git a/dom/ipc/ContentProcess.cpp b/dom/ipc/ContentProcess.cpp new file mode 100644 index 000000000..66125f332 --- /dev/null +++ b/dom/ipc/ContentProcess.cpp @@ -0,0 +1,162 @@ +/* -*- 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/ipc/IOThreadChild.h" + +#include "ContentProcess.h" + +#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX) +#include "mozilla/WindowsVersion.h" +#endif + +#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) +#include <stdlib.h> +#endif + +#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX) +#include "mozilla/Preferences.h" +#include "nsAppDirectoryServiceDefs.h" +#include "nsDirectoryService.h" +#include "nsDirectoryServiceDefs.h" +#endif + +using mozilla::ipc::IOThreadChild; + +namespace mozilla { +namespace dom { + +#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX) +static bool +IsSandboxTempDirRequired() +{ + // On Windows, a sandbox-writable temp directory is only used + // for Vista or later with sandbox pref level >= 1. + return (IsVistaOrLater() && + (Preferences::GetInt("security.sandbox.content.level") >= 1)); +} + +static void +SetTmpEnvironmentVariable(nsIFile* aValue) +{ + // Save the TMP environment variable so that is is picked up by GetTempPath(). + // Note that we specifically write to the TMP variable, as that is the first + // variable that is checked by GetTempPath() to determine its output. + nsAutoString fullTmpPath; + nsresult rv = aValue->GetPath(fullTmpPath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + Unused << NS_WARN_IF(!SetEnvironmentVariableW(L"TMP", fullTmpPath.get())); + // We also set TEMP in case there is naughty third-party code that is + // referencing the environment variable directly. + Unused << NS_WARN_IF(!SetEnvironmentVariableW(L"TEMP", fullTmpPath.get())); +} +#endif + +#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) +static bool +IsSandboxTempDirRequired() +{ + // On OSX, use the sandbox-writable temp when the pref level >= 1. + return (Preferences::GetInt("security.sandbox.content.level") >= 1); +} + +static void +SetTmpEnvironmentVariable(nsIFile* aValue) +{ + nsAutoCString fullTmpPath; + nsresult rv = aValue->GetNativePath(fullTmpPath); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + Unused << NS_WARN_IF(setenv("TMPDIR", fullTmpPath.get(), 1) != 0); +} +#endif + +#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX) +static void +SetUpSandboxEnvironment() +{ + MOZ_ASSERT(nsDirectoryService::gService, + "SetUpSandboxEnvironment relies on nsDirectoryService being initialized"); + + if (!IsSandboxTempDirRequired()) { + return; + } + + nsCOMPtr<nsIFile> sandboxedContentTemp; + nsresult rv = + nsDirectoryService::gService->Get(NS_APP_CONTENT_PROCESS_TEMP_DIR, + NS_GET_IID(nsIFile), + getter_AddRefs(sandboxedContentTemp)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + // Change the gecko defined temp directory to our sandbox-writable one. + // Undefine returns a failure if the property is not already set. + Unused << nsDirectoryService::gService->Undefine(NS_OS_TEMP_DIR); + rv = nsDirectoryService::gService->Set(NS_OS_TEMP_DIR, sandboxedContentTemp); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + SetTmpEnvironmentVariable(sandboxedContentTemp); +} +#endif + +void +ContentProcess::SetAppDir(const nsACString& aPath) +{ + mXREEmbed.SetAppDir(aPath); +} + +#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) +void +ContentProcess::SetProfile(const nsACString& aProfile) +{ + bool flag; + nsresult rv = + XRE_GetFileFromPath(aProfile.BeginReading(), getter_AddRefs(mProfileDir)); + if (NS_FAILED(rv) || + NS_FAILED(mProfileDir->Exists(&flag)) || !flag) { + NS_WARNING("Invalid profile directory passed to content process."); + mProfileDir = nullptr; + } +} +#endif + +bool +ContentProcess::Init() +{ + mContent.Init(IOThreadChild::message_loop(), + ParentPid(), + IOThreadChild::channel()); + mXREEmbed.Start(); + mContent.InitXPCOM(); + mContent.InitGraphicsDeviceData(); + +#if (defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX) + mContent.SetProfileDir(mProfileDir); +#endif + +#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX) + SetUpSandboxEnvironment(); +#endif + + return true; +} + +// Note: CleanUp() never gets called in non-debug builds because we exit early +// in ContentChild::ActorDestroy(). +void +ContentProcess::CleanUp() +{ + mXREEmbed.Stop(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/ContentProcess.h b/dom/ipc/ContentProcess.h new file mode 100644 index 000000000..bf9968f8c --- /dev/null +++ b/dom/ipc/ContentProcess.h @@ -0,0 +1,65 @@ +/* -*- 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/. */ + +#ifndef dom_tabs_ContentThread_h +#define dom_tabs_ContentThread_h 1 + +#include "mozilla/ipc/ProcessChild.h" +#include "mozilla/ipc/ScopedXREEmbed.h" +#include "ContentChild.h" + +#if defined(XP_WIN) +#include "mozilla/mscom/MainThreadRuntime.h" +#endif + +namespace mozilla { +namespace dom { + +/** + * ContentProcess is a singleton on the content process which represents + * the main thread where tab instances live. + */ +class ContentProcess : public mozilla::ipc::ProcessChild +{ + typedef mozilla::ipc::ProcessChild ProcessChild; + +public: + explicit ContentProcess(ProcessId aParentPid) + : ProcessChild(aParentPid) + { } + + ~ContentProcess() + { } + + virtual bool Init() override; + virtual void CleanUp() override; + + void SetAppDir(const nsACString& aPath); + +#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) + void SetProfile(const nsACString& aProfile); +#endif + +private: + ContentChild mContent; + mozilla::ipc::ScopedXREEmbed mXREEmbed; + +#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) + nsCOMPtr<nsIFile> mProfileDir; +#endif + +#if defined(XP_WIN) + // This object initializes and configures COM. + mozilla::mscom::MainThreadRuntime mCOMRuntime; +#endif + + DISALLOW_EVIL_CONSTRUCTORS(ContentProcess); +}; + +} // namespace dom +} // namespace mozilla + +#endif // ifndef dom_tabs_ContentThread_h diff --git a/dom/ipc/ContentProcessManager.cpp b/dom/ipc/ContentProcessManager.cpp new file mode 100644 index 000000000..02fdcba34 --- /dev/null +++ b/dom/ipc/ContentProcessManager.cpp @@ -0,0 +1,372 @@ +/* -*- 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 "ContentProcessManager.h" +#include "ContentParent.h" +#include "mozilla/dom/TabParent.h" + +#include "mozilla/StaticPtr.h" +#include "mozilla/ClearOnShutdown.h" + +#include "nsPrintfCString.h" +#include "nsIScriptSecurityManager.h" + +// XXX need another bug to move this to a common header. +#ifdef DISABLE_ASSERTS_FOR_FUZZING +#define ASSERT_UNLESS_FUZZING(...) do { } while (0) +#else +#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__) +#endif + +namespace mozilla { +namespace dom { + +static uint64_t gTabId = 0; + +/* static */ +StaticAutoPtr<ContentProcessManager> +ContentProcessManager::sSingleton; + +/* static */ ContentProcessManager* +ContentProcessManager::GetSingleton() +{ + MOZ_ASSERT(XRE_IsParentProcess()); + + if (!sSingleton) { + sSingleton = new ContentProcessManager(); + ClearOnShutdown(&sSingleton); + } + return sSingleton; +} + +void +ContentProcessManager::AddContentProcess(ContentParent* aChildCp, + const ContentParentId& aParentCpId) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aChildCp); + + ContentProcessInfo info; + info.mCp = aChildCp; + info.mParentCpId = aParentCpId; + mContentParentMap[aChildCp->ChildID()] = info; +} + +void +ContentProcessManager::RemoveContentProcess(const ContentParentId& aChildCpId) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mContentParentMap.find(aChildCpId) != mContentParentMap.end()); + + mContentParentMap.erase(aChildCpId); + for (auto iter = mContentParentMap.begin(); + iter != mContentParentMap.end(); + ++iter) { + if (!iter->second.mChildrenCpId.empty()) { + iter->second.mChildrenCpId.erase(aChildCpId); + } + } +} + +bool +ContentProcessManager::AddGrandchildProcess(const ContentParentId& aParentCpId, + const ContentParentId& aChildCpId) +{ + MOZ_ASSERT(NS_IsMainThread()); + + auto iter = mContentParentMap.find(aParentCpId); + if (NS_WARN_IF(iter == mContentParentMap.end())) { + ASSERT_UNLESS_FUZZING("Parent process should be already in map!"); + return false; + } + iter->second.mChildrenCpId.insert(aChildCpId); + return true; +} + +bool +ContentProcessManager::GetParentProcessId(const ContentParentId& aChildCpId, + /*out*/ ContentParentId* aParentCpId) +{ + MOZ_ASSERT(NS_IsMainThread()); + + auto iter = mContentParentMap.find(aChildCpId); + if (NS_WARN_IF(iter == mContentParentMap.end())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + *aParentCpId = iter->second.mParentCpId; + return true; +} + +ContentParent* +ContentProcessManager::GetContentProcessById(const ContentParentId& aChildCpId) +{ + MOZ_ASSERT(NS_IsMainThread()); + + auto iter = mContentParentMap.find(aChildCpId); + if (NS_WARN_IF(iter == mContentParentMap.end())) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + return iter->second.mCp; +} + +nsTArray<ContentParentId> +ContentProcessManager::GetAllChildProcessById(const ContentParentId& aParentCpId) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsTArray<ContentParentId> cpIdArray; + auto iter = mContentParentMap.find(aParentCpId); + if (NS_WARN_IF(iter == mContentParentMap.end())) { + ASSERT_UNLESS_FUZZING(); + return Move(cpIdArray); + } + + for (auto cpIter = iter->second.mChildrenCpId.begin(); + cpIter != iter->second.mChildrenCpId.end(); + ++cpIter) { + cpIdArray.AppendElement(*cpIter); + } + + return Move(cpIdArray); +} + +TabId +ContentProcessManager::AllocateTabId(const TabId& aOpenerTabId, + const IPCTabContext& aContext, + const ContentParentId& aChildCpId) +{ + MOZ_ASSERT(NS_IsMainThread()); + + auto iter = mContentParentMap.find(aChildCpId); + if (NS_WARN_IF(iter == mContentParentMap.end())) { + ASSERT_UNLESS_FUZZING(); + return TabId(0); + } + + struct RemoteFrameInfo info; + + // If it's a PopupIPCTabContext, it's the case that a TabChild want to + // open a new tab. aOpenerTabId has to be it's parent frame's opener id. + if (aContext.type() == IPCTabContext::TPopupIPCTabContext) { + auto remoteFrameIter = iter->second.mRemoteFrames.find(aOpenerTabId); + if (remoteFrameIter == iter->second.mRemoteFrames.end()) { + ASSERT_UNLESS_FUZZING("Failed to find parent frame's opener id."); + return TabId(0); + } + + info.mOpenerTabId = remoteFrameIter->second.mOpenerTabId; + + const PopupIPCTabContext &ipcContext = aContext.get_PopupIPCTabContext(); + MOZ_ASSERT(ipcContext.opener().type() == PBrowserOrId::TTabId); + + remoteFrameIter = iter->second.mRemoteFrames.find(ipcContext.opener().get_TabId()); + if (remoteFrameIter == iter->second.mRemoteFrames.end()) { + ASSERT_UNLESS_FUZZING("Failed to find tab id."); + return TabId(0); + } + + info.mContext = remoteFrameIter->second.mContext; + } + else { + MaybeInvalidTabContext tc(aContext); + if (!tc.IsValid()) { + NS_ERROR(nsPrintfCString("Received an invalid TabContext from " + "the child process. (%s)", + tc.GetInvalidReason()).get()); + return TabId(0); + } + info.mOpenerTabId = aOpenerTabId; + info.mContext = tc.GetTabContext(); + } + + mUniqueId = ++gTabId; + iter->second.mRemoteFrames[mUniqueId] = info; + + return mUniqueId; +} + +void +ContentProcessManager::DeallocateTabId(const ContentParentId& aChildCpId, + const TabId& aChildTabId) +{ + MOZ_ASSERT(NS_IsMainThread()); + + auto iter = mContentParentMap.find(aChildCpId); + if (NS_WARN_IF(iter == mContentParentMap.end())) { + ASSERT_UNLESS_FUZZING(); + return; + } + + auto remoteFrameIter = iter->second.mRemoteFrames.find(aChildTabId); + if (remoteFrameIter != iter->second.mRemoteFrames.end()) { + iter->second.mRemoteFrames.erase(aChildTabId); + } +} + +bool +ContentProcessManager::GetTabContextByProcessAndTabId(const ContentParentId& aChildCpId, + const TabId& aChildTabId, + /*out*/ TabContext* aTabContext) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aTabContext); + + auto iter = mContentParentMap.find(aChildCpId); + if (NS_WARN_IF(iter == mContentParentMap.end())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + auto remoteFrameIter = iter->second.mRemoteFrames.find(aChildTabId); + if (NS_WARN_IF(remoteFrameIter == iter->second.mRemoteFrames.end())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + *aTabContext = remoteFrameIter->second.mContext; + + return true; +} + +nsTArray<TabContext> +ContentProcessManager::GetTabContextByContentProcess(const ContentParentId& aChildCpId) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsTArray<TabContext> tabContextArray; + auto iter = mContentParentMap.find(aChildCpId); + if (NS_WARN_IF(iter == mContentParentMap.end())) { + ASSERT_UNLESS_FUZZING(); + return Move(tabContextArray); + } + + for (auto remoteFrameIter = iter->second.mRemoteFrames.begin(); + remoteFrameIter != iter->second.mRemoteFrames.end(); + ++remoteFrameIter) { + tabContextArray.AppendElement(remoteFrameIter->second.mContext); + } + + return Move(tabContextArray); +} + +bool +ContentProcessManager::GetRemoteFrameOpenerTabId(const ContentParentId& aChildCpId, + const TabId& aChildTabId, + /*out*/TabId* aOpenerTabId) +{ + MOZ_ASSERT(NS_IsMainThread()); + auto iter = mContentParentMap.find(aChildCpId); + if (NS_WARN_IF(iter == mContentParentMap.end())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + auto remoteFrameIter = iter->second.mRemoteFrames.find(aChildTabId); + if (NS_WARN_IF(remoteFrameIter == iter->second.mRemoteFrames.end())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + *aOpenerTabId = remoteFrameIter->second.mOpenerTabId; + + return true; +} + +already_AddRefed<TabParent> +ContentProcessManager::GetTabParentByProcessAndTabId(const ContentParentId& aChildCpId, + const TabId& aChildTabId) +{ + MOZ_ASSERT(NS_IsMainThread()); + + auto iter = mContentParentMap.find(aChildCpId); + if (NS_WARN_IF(iter == mContentParentMap.end())) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + const ManagedContainer<PBrowserParent>& browsers = iter->second.mCp->ManagedPBrowserParent(); + for (auto iter = browsers.ConstIter(); !iter.Done(); iter.Next()) { + RefPtr<TabParent> tab = TabParent::GetFrom(iter.Get()->GetKey()); + if (tab->GetTabId() == aChildTabId) { + return tab.forget(); + } + } + + return nullptr; +} + +already_AddRefed<TabParent> +ContentProcessManager::GetTopLevelTabParentByProcessAndTabId(const ContentParentId& aChildCpId, + const TabId& aChildTabId) +{ + MOZ_ASSERT(NS_IsMainThread()); + + // Used to keep the current ContentParentId and the current TabId + // in the iteration(do-while loop below) + ContentParentId currentCpId; + TabId currentTabId; + + // To get the ContentParentId and the TabParentId on upper level + ContentParentId parentCpId = aChildCpId; + TabId openerTabId = aChildTabId; + + // Stop this loop when the upper ContentParentId of + // the current ContentParentId is chrome(ContentParentId = 0). + do { + // Update the current ContentParentId and TabId in iteration + currentCpId = parentCpId; + currentTabId = openerTabId; + + // Get the ContentParentId and TabId on upper level + if (!GetParentProcessId(currentCpId, &parentCpId) || + !GetRemoteFrameOpenerTabId(currentCpId, currentTabId, &openerTabId)) { + return nullptr; + } + } while (parentCpId); + + // Get the top level TabParent by the current ContentParentId and TabId + return GetTabParentByProcessAndTabId(currentCpId, currentTabId); +} + +nsTArray<TabId> +ContentProcessManager::GetTabParentsByProcessId(const ContentParentId& aChildCpId) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsTArray<TabId> tabIdList; + auto iter = mContentParentMap.find(aChildCpId); + if (NS_WARN_IF(iter == mContentParentMap.end())) { + ASSERT_UNLESS_FUZZING(); + return Move(tabIdList); + } + + for (auto remoteFrameIter = iter->second.mRemoteFrames.begin(); + remoteFrameIter != iter->second.mRemoteFrames.end(); + ++remoteFrameIter) { + tabIdList.AppendElement(remoteFrameIter->first); + } + + return Move(tabIdList); +} + +uint32_t +ContentProcessManager::GetAppIdByProcessAndTabId(const ContentParentId& aChildCpId, + const TabId& aChildTabId) +{ + uint32_t appId = nsIScriptSecurityManager::NO_APP_ID; + if (aChildCpId && aChildTabId) { + TabContext tabContext; + if (GetTabContextByProcessAndTabId(aChildCpId, aChildTabId, &tabContext)) { + appId = tabContext.OwnOrContainingAppId(); + } + } + return appId; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/ContentProcessManager.h b/dom/ipc/ContentProcessManager.h new file mode 100644 index 000000000..cc6d8bd8b --- /dev/null +++ b/dom/ipc/ContentProcessManager.h @@ -0,0 +1,166 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_ContentProcessManager_h +#define mozilla_dom_ContentProcessManager_h + +#include <map> +#include <set> +#include "mozilla/StaticPtr.h" +#include "mozilla/dom/TabContext.h" +#include "mozilla/dom/ipc/IdType.h" +#include "nsTArray.h" + +namespace mozilla { +namespace dom { +class ContentParent; + +struct RemoteFrameInfo +{ + TabId mOpenerTabId; + TabContext mContext; +}; + +struct ContentProcessInfo +{ + ContentParent* mCp; + ContentParentId mParentCpId; + std::set<ContentParentId> mChildrenCpId; + std::map<TabId, RemoteFrameInfo> mRemoteFrames; +}; + +class ContentProcessManager final +{ +public: + static ContentProcessManager* GetSingleton(); + ~ContentProcessManager() {MOZ_COUNT_DTOR(ContentProcessManager);}; + + /** + * Add a new content process into the map. + * If aParentCpId is not 0, it's a nested content process. + */ + void AddContentProcess(ContentParent* aChildCp, + const ContentParentId& aParentCpId = ContentParentId(0)); + /** + * Remove the content process by id. + */ + void RemoveContentProcess(const ContentParentId& aChildCpId); + /** + * Add a grandchild content process into the map. + * aParentCpId must be already added in the map by AddContentProcess(). + */ + bool AddGrandchildProcess(const ContentParentId& aParentCpId, + const ContentParentId& aChildCpId); + /** + * Get the parent process's id by child process's id. + * Used to check if a child really belongs to the parent. + */ + bool GetParentProcessId(const ContentParentId& aChildCpId, + /*out*/ ContentParentId* aParentCpId); + /** + * Return the ContentParent pointer by id. + */ + ContentParent* GetContentProcessById(const ContentParentId& aChildCpId); + + /** + * Return a list of all child process's id. + */ + nsTArray<ContentParentId> + GetAllChildProcessById(const ContentParentId& aParentCpId); + + /** + * Allocate a tab id for the given content process's id. + * Used when a content process wants to create a new tab. aOpenerTabId and + * aContext are saved in RemoteFrameInfo, which is a part of + * ContentProcessInfo. We can use the tab id and process id to locate the + * TabContext for future use. + */ + TabId AllocateTabId(const TabId& aOpenerTabId, + const IPCTabContext& aContext, + const ContentParentId& aChildCpId); + + /** + * Remove the RemoteFrameInfo by the given process and tab id. + */ + void DeallocateTabId(const ContentParentId& aChildCpId, + const TabId& aChildTabId); + + /** + * Get the TabContext by the given content process and tab id. + */ + bool + GetTabContextByProcessAndTabId(const ContentParentId& aChildCpId, + const TabId& aChildTabId, + /*out*/ TabContext* aTabContext); + + /** + * Get all TabContext which are inside the given content process. + * Used for AppProcessChecker to cehck app status. + */ + nsTArray<TabContext> + GetTabContextByContentProcess(const ContentParentId& aChildCpId); + + /** + * Query a tab's opener id by the given process and tab id. + * XXX Currently not used. Plan to be used for bug 1020179. + */ + bool GetRemoteFrameOpenerTabId(const ContentParentId& aChildCpId, + const TabId& aChildTabId, + /*out*/ TabId* aOpenerTabId); + + /** + * Get all TabParents' Ids managed by the givent content process. + * Return empty array when TabParent couldn't be found via aChildCpId + */ + nsTArray<TabId> + GetTabParentsByProcessId(const ContentParentId& aChildCpId); + + /** + * Get the TabParent by the given content process and tab id. + * Return nullptr when TabParent couldn't be found via aChildCpId + * and aChildTabId. + * (or probably because the TabParent is not in the chrome process) + */ + already_AddRefed<TabParent> + GetTabParentByProcessAndTabId(const ContentParentId& aChildCpId, + const TabId& aChildTabId); + + /** + * Get the TabParent on top level by the given content process and tab id. + * + * This function return the TabParent belong to the chrome process, + * called top-level TabParent here, by given aChildCpId and aChildTabId. + * The given aChildCpId and aChildTabId are related to a content process + * and a tab respectively. In nested-oop, the top-level TabParent isn't + * always the opener tab of the given tab in content process. This function + * will call GetTabParentByProcessAndTabId iteratively until the Tab returned + * is belong to the chrome process. + */ + already_AddRefed<TabParent> + GetTopLevelTabParentByProcessAndTabId(const ContentParentId& aChildCpId, + const TabId& aChildTabId); + + /** + * Return appId by given TabId and ContentParentId. + * It will return nsIScriptSecurityManager::NO_APP_ID + * if the given tab is not an app. + */ + uint32_t + GetAppIdByProcessAndTabId(const ContentParentId& aChildCpId, + const TabId& aChildTabId); + +private: + static StaticAutoPtr<ContentProcessManager> sSingleton; + TabId mUniqueId; + std::map<ContentParentId, ContentProcessInfo> mContentParentMap; + + ContentProcessManager() {MOZ_COUNT_CTOR(ContentProcessManager);}; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_ContentProcessManager_h diff --git a/dom/ipc/CrashReporterChild.cpp b/dom/ipc/CrashReporterChild.cpp new file mode 100644 index 000000000..8174452e7 --- /dev/null +++ b/dom/ipc/CrashReporterChild.cpp @@ -0,0 +1,42 @@ +/* -*- 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/plugins/PluginModuleChild.h" +#include "ContentChild.h" +#include "CrashReporterChild.h" +#include "nsXULAppAPI.h" + +using mozilla::plugins::PluginModuleChild; + +namespace mozilla { +namespace dom { + +/*static*/ +PCrashReporterChild* +CrashReporterChild::GetCrashReporter() +{ + const ManagedContainer<PCrashReporterChild>* reporters = nullptr; + switch (XRE_GetProcessType()) { + case GeckoProcessType_Content: { + ContentChild* child = ContentChild::GetSingleton(); + reporters = &child->ManagedPCrashReporterChild(); + break; + } + case GeckoProcessType_Plugin: { + PluginModuleChild* child = PluginModuleChild::GetChrome(); + reporters = &child->ManagedPCrashReporterChild(); + break; + } + default: + break; + } + if (!reporters) { + return nullptr; + } + return LoneManagedOrNullAsserts(*reporters); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/CrashReporterChild.h b/dom/ipc/CrashReporterChild.h new file mode 100644 index 000000000..96355ca11 --- /dev/null +++ b/dom/ipc/CrashReporterChild.h @@ -0,0 +1,32 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_CrashReporterChild_h +#define mozilla_dom_CrashReporterChild_h + +#include "mozilla/dom/PCrashReporterChild.h" + +namespace mozilla { +namespace dom { + +class CrashReporterChild : + public PCrashReporterChild +{ +public: + CrashReporterChild() { + MOZ_COUNT_CTOR(CrashReporterChild); + } + ~CrashReporterChild() { + MOZ_COUNT_DTOR(CrashReporterChild); + } + + static PCrashReporterChild* GetCrashReporter(); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_CrashReporterChild_h diff --git a/dom/ipc/CrashReporterParent.cpp b/dom/ipc/CrashReporterParent.cpp new file mode 100644 index 000000000..fc627387f --- /dev/null +++ b/dom/ipc/CrashReporterParent.cpp @@ -0,0 +1,146 @@ +/* -*- 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 "CrashReporterParent.h" +#include "mozilla/Sprintf.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/ipc/CrashReporterHost.h" +#include "nsAutoPtr.h" +#include "nsXULAppAPI.h" +#include <time.h> + +#include "mozilla/Telemetry.h" + +#ifdef MOZ_CRASHREPORTER +#include "nsExceptionHandler.h" +#include "nsICrashService.h" +#include "mozilla/SyncRunnable.h" +#include "nsThreadUtils.h" +#endif + +namespace mozilla { +namespace dom { + +using namespace mozilla::ipc; + +void +CrashReporterParent::AnnotateCrashReport(const nsCString& key, + const nsCString& data) +{ +#ifdef MOZ_CRASHREPORTER + mNotes.Put(key, data); +#endif +} + +void +CrashReporterParent::ActorDestroy(ActorDestroyReason aWhy) +{ + // Implement me! Bug 1005155 +} + +bool +CrashReporterParent::RecvAppendAppNotes(const nsCString& data) +{ + mAppNotes.Append(data); + return true; +} + +CrashReporterParent::CrashReporterParent() + : +#ifdef MOZ_CRASHREPORTER + mNotes(4), +#endif + mStartTime(::time(nullptr)) + , mInitialized(false) +{ + MOZ_COUNT_CTOR(CrashReporterParent); +} + +CrashReporterParent::~CrashReporterParent() +{ + MOZ_COUNT_DTOR(CrashReporterParent); +} + +void +CrashReporterParent::SetChildData(const NativeThreadId& tid, + const uint32_t& processType) +{ + mInitialized = true; + mMainThread = tid; + mProcessType = GeckoProcessType(processType); +} + +#ifdef MOZ_CRASHREPORTER +bool +CrashReporterParent::GenerateCrashReportForMinidump(nsIFile* minidump, + const AnnotationTable* processNotes) +{ + if (!CrashReporter::GetIDFromMinidump(minidump, mChildDumpID)) { + return false; + } + + bool result = GenerateChildData(processNotes); + FinalizeChildData(); + return result; +} + +bool +CrashReporterParent::GenerateChildData(const AnnotationTable* processNotes) +{ + MOZ_ASSERT(mInitialized); + + if (mChildDumpID.IsEmpty()) { + NS_WARNING("problem with GenerateChildData: no child dump id yet!"); + return false; + } + + nsAutoCString type; + switch (mProcessType) { + case GeckoProcessType_Content: + type = NS_LITERAL_CSTRING("content"); + break; + case GeckoProcessType_Plugin: + case GeckoProcessType_GMPlugin: + type = NS_LITERAL_CSTRING("plugin"); + break; + default: + NS_ERROR("unknown process type"); + break; + } + mNotes.Put(NS_LITERAL_CSTRING("ProcessType"), type); + + char startTime[32]; + SprintfLiteral(startTime, "%lld", static_cast<long long>(mStartTime)); + mNotes.Put(NS_LITERAL_CSTRING("StartupTime"), nsDependentCString(startTime)); + + if (!mAppNotes.IsEmpty()) { + mNotes.Put(NS_LITERAL_CSTRING("Notes"), mAppNotes); + } + + // Append these notes to the end of the extra file based on the current + // dump id we obtained from CreatePairedMinidumps. + bool ret = CrashReporter::AppendExtraData(mChildDumpID, mNotes); + if (ret && processNotes) { + ret = CrashReporter::AppendExtraData(mChildDumpID, *processNotes); + } + + if (!ret) { + NS_WARNING("problem appending child data to .extra"); + } + return ret; +} + +void +CrashReporterParent::FinalizeChildData() +{ + MOZ_ASSERT(mInitialized); + + CrashReporterHost::NotifyCrashService(mProcessType, mChildDumpID, &mNotes); + mNotes.Clear(); +} +#endif + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/CrashReporterParent.h b/dom/ipc/CrashReporterParent.h new file mode 100644 index 000000000..25824f279 --- /dev/null +++ b/dom/ipc/CrashReporterParent.h @@ -0,0 +1,306 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_CrashReporterParent_h +#define mozilla_dom_CrashReporterParent_h + +#include "mozilla/dom/PCrashReporterParent.h" +#include "mozilla/dom/TabMessageUtils.h" +#include "nsIFile.h" +#ifdef MOZ_CRASHREPORTER +#include "nsExceptionHandler.h" +#include "nsDataHashtable.h" +#endif + +namespace mozilla { +namespace dom { + +class CrashReporterParent : public PCrashReporterParent +{ +#ifdef MOZ_CRASHREPORTER + typedef CrashReporter::AnnotationTable AnnotationTable; +#endif +public: + CrashReporterParent(); + virtual ~CrashReporterParent(); + +#ifdef MOZ_CRASHREPORTER + + /* + * Attempt to create a bare-bones crash report, along with extra process- + * specific annotations present in the given AnnotationTable. Calls + * GenerateChildData and FinalizeChildData. + * + * @returns true if successful, false otherwise. + */ + template<class Toplevel> + bool + GenerateCrashReport(Toplevel* t, const AnnotationTable* processNotes); + + /* + * Attempt to generate a parent/child pair of minidumps from the given + * toplevel actor. This calls CrashReporter::CreateMinidumpsAndPair to + * generate the minidumps. Crash reporter annotations set prior to this + * call will be saved via PairedDumpCallbackExtra into an .extra file + * under the proper crash id. AnnotateCrashReport annotations are not + * set in this call and the report is not finalized. + * + * @returns true if successful, false otherwise. + */ + template<class Toplevel> + bool + GeneratePairedMinidump(Toplevel* t); + + /* + * Attempts to take a minidump of the current process and pair that with + * a named minidump handed in by the caller. + * + * @param aTopLevel - top level actor this reporter is associated with. + * @param aMinidump - the minidump to associate with. + * @param aPairName - the name of the additional minidump. + * @returns true if successful, false otherwise. + */ + template<class Toplevel> + bool + GenerateMinidumpAndPair(Toplevel* aTopLevel, nsIFile* aMinidump, + const nsACString& aPairName); + + /** + * Apply child process annotations to an existing paired mindump generated + * with GeneratePairedMinidump. + * + * Be careful about calling generate apis immediately after this call, + * see FinalizeChildData. + * + * @param processNotes (optional) - Additional notes to append. Annotations + * stored in mNotes will also be applied. processNotes can be null. + * @returns true if successful, false otherwise. + */ + bool + GenerateChildData(const AnnotationTable* processNotes); + + /** + * Handles main thread finalization tasks after a report has been + * generated. Does the following: + * - register the finished report with the crash service manager + * - records telemetry related data about crashes + * + * Be careful about calling generate apis immediately after this call, + * if this api is called on a non-main thread it will fire off a runnable + * to complete its work async. + */ + void + FinalizeChildData(); + + /* + * Attempt to generate a full paired dump complete with any child + * annoations, and finalizes the report. Note this call is only valid + * on the main thread. Calling on a background thread will fail. + * + * @returns true if successful, false otherwise. + */ + template<class Toplevel> + bool + GenerateCompleteMinidump(Toplevel* t); + + /** + * Submits a raw minidump handed in, calls GenerateChildData and + * FinalizeChildData. Used by content plugins and gmp. + * + * @returns true if successful, false otherwise. + */ + bool + GenerateCrashReportForMinidump(nsIFile* minidump, + const AnnotationTable* processNotes); + + /* + * Instantiate a new crash reporter actor from a given parent that manages + * the protocol. + * + * @returns true if successful, false otherwise. + */ + template<class Toplevel> + static bool CreateCrashReporter(Toplevel* actor); +#endif // MOZ_CRASHREPORTER + + /* + * Initialize this reporter with data from the child process. + */ + void + SetChildData(const NativeThreadId& id, const uint32_t& processType); + + /* + * Returns the ID of the child minidump. + * GeneratePairedMinidump or GenerateCrashReport must be called first. + */ + const nsString& ChildDumpID() const { + return mChildDumpID; + } + + /* + * Add an annotation to our internally tracked list of annotations. + * Callers must apply these notes using GenerateChildData otherwise + * the notes will get dropped. + */ + void + AnnotateCrashReport(const nsCString& aKey, const nsCString& aData); + + protected: + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + virtual bool RecvAnnotateCrashReport(const nsCString& aKey, + const nsCString& aData) override + { + AnnotateCrashReport(aKey, aData); + return true; + } + + virtual bool RecvAppendAppNotes(const nsCString& aData) override; + +#ifdef MOZ_CRASHREPORTER + void + NotifyCrashService(); +#endif + +#ifdef MOZ_CRASHREPORTER + AnnotationTable mNotes; +#endif + nsCString mAppNotes; + nsString mChildDumpID; + // stores the child main thread id + NativeThreadId mMainThread; + time_t mStartTime; + // stores the child process type + GeckoProcessType mProcessType; + bool mInitialized; +}; + +#ifdef MOZ_CRASHREPORTER +template<class Toplevel> +inline bool +CrashReporterParent::GeneratePairedMinidump(Toplevel* t) +{ + mozilla::ipc::ScopedProcessHandle child; +#ifdef XP_MACOSX + child = t->Process()->GetChildTask(); +#else + if (!base::OpenPrivilegedProcessHandle(t->OtherPid(), &child.rwget())) { + NS_WARNING("Failed to open child process handle."); + return false; + } +#endif + nsCOMPtr<nsIFile> childDump; + if (CrashReporter::CreateMinidumpsAndPair(child, + mMainThread, + NS_LITERAL_CSTRING("browser"), + nullptr, // pair with a dump of this process and thread + getter_AddRefs(childDump)) && + CrashReporter::GetIDFromMinidump(childDump, mChildDumpID)) { + return true; + } + return false; +} + +template<class Toplevel> +inline bool +CrashReporterParent::GenerateMinidumpAndPair(Toplevel* aTopLevel, + nsIFile* aMinidumpToPair, + const nsACString& aPairName) +{ + mozilla::ipc::ScopedProcessHandle childHandle; +#ifdef XP_MACOSX + childHandle = aTopLevel->Process()->GetChildTask(); +#else + if (!base::OpenPrivilegedProcessHandle(aTopLevel->OtherPid(), + &childHandle.rwget())) { + NS_WARNING("Failed to open child process handle."); + return false; + } +#endif + nsCOMPtr<nsIFile> targetDump; + if (CrashReporter::CreateMinidumpsAndPair(childHandle, + mMainThread, // child thread id + aPairName, + aMinidumpToPair, + getter_AddRefs(targetDump)) && + CrashReporter::GetIDFromMinidump(targetDump, mChildDumpID)) { + return true; + } + return false; +} + +template<class Toplevel> +inline bool +CrashReporterParent::GenerateCrashReport(Toplevel* t, + const AnnotationTable* processNotes) +{ + nsCOMPtr<nsIFile> crashDump; + if (t->TakeMinidump(getter_AddRefs(crashDump), nullptr) && + CrashReporter::GetIDFromMinidump(crashDump, mChildDumpID)) { + bool result = GenerateChildData(processNotes); + FinalizeChildData(); + return result; + } + return false; +} + +template<class Toplevel> +inline bool +CrashReporterParent::GenerateCompleteMinidump(Toplevel* t) +{ + mozilla::ipc::ScopedProcessHandle child; + if (!NS_IsMainThread()) { + NS_WARNING("GenerateCompleteMinidump can't be called on non-main thread."); + return false; + } + +#ifdef XP_MACOSX + child = t->Process()->GetChildTask(); +#else + if (!base::OpenPrivilegedProcessHandle(t->OtherPid(), &child.rwget())) { + NS_WARNING("Failed to open child process handle."); + return false; + } +#endif + nsCOMPtr<nsIFile> childDump; + if (CrashReporter::CreateMinidumpsAndPair(child, + mMainThread, + NS_LITERAL_CSTRING("browser"), + nullptr, // pair with a dump of this process and thread + getter_AddRefs(childDump)) && + CrashReporter::GetIDFromMinidump(childDump, mChildDumpID)) { + bool result = GenerateChildData(nullptr); + FinalizeChildData(); + return result; + } + return false; +} + +template<class Toplevel> +/* static */ bool +CrashReporterParent::CreateCrashReporter(Toplevel* actor) +{ +#ifdef MOZ_CRASHREPORTER + NativeThreadId id; + uint32_t processType; + PCrashReporterParent* p = + actor->CallPCrashReporterConstructor(&id, &processType); + if (p) { + static_cast<CrashReporterParent*>(p)->SetChildData(id, processType); + } else { + NS_ERROR("Error creating crash reporter actor"); + } + return !!p; +#endif + return false; +} + +#endif + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_CrashReporterParent_h diff --git a/dom/ipc/DOMTypes.ipdlh b/dom/ipc/DOMTypes.ipdlh new file mode 100644 index 000000000..e02760ada --- /dev/null +++ b/dom/ipc/DOMTypes.ipdlh @@ -0,0 +1,181 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */ +/* 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 protocol PBlob; +include protocol PSendStream; +include IPCStream; +include ProtocolTypes; + +using struct mozilla::void_t + from "ipc/IPCMessageUtils.h"; + +using struct mozilla::SerializedStructuredCloneBuffer + from "ipc/IPCMessageUtils.h"; + +namespace mozilla { +namespace dom { + +union OptionalID +{ + nsID; + void_t; +}; + +struct MessagePortIdentifier +{ + nsID uuid; + nsID destinationUuid; + uint32_t sequenceId; + bool neutered; +}; + +struct ClonedMessageData +{ + SerializedStructuredCloneBuffer data; + PBlob[] blobs; + MessagePortIdentifier[] identfiers; +}; + +struct BlobDataStream +{ + IPCStream stream; + uint64_t length; +}; + +union BlobData +{ + // For remote blobs. + nsID; + + // For memory-backed blobs. + BlobDataStream; + + // For multiplex blobs. + BlobData[]; +}; + +union OptionalBlobData +{ + BlobData; + void_t; +}; + +struct NormalBlobConstructorParams +{ + nsString contentType; + uint64_t length; + + // This must be of type BlobData in a child->parent message, and will always + // be of type void_t in a parent->child message. + OptionalBlobData optionalBlobData; +}; + +struct FileBlobConstructorParams +{ + nsString name; + nsString contentType; + nsString path; + uint64_t length; + int64_t modDate; + bool isDirectory; + + // This must be of type BlobData in a child->parent message, and will always + // be of type void_t in a parent->child message. + OptionalBlobData optionalBlobData; +}; + +struct SlicedBlobConstructorParams +{ + PBlob source; + nsID id; + uint64_t begin; + uint64_t end; + nsString contentType; +}; + +struct MysteryBlobConstructorParams +{ + // Nothing is known about this type of blob. +}; + +struct KnownBlobConstructorParams +{ + nsID id; +}; + +// This may only be used for same-process inter-thread communication! +struct SameProcessBlobConstructorParams +{ + // This member should be reinterpret_cast'd to mozilla::dom::BlobImpl. It + // carries a reference. + intptr_t addRefedBlobImpl; +}; + +union AnyBlobConstructorParams +{ + // These types may be sent to/from parent and child. + NormalBlobConstructorParams; + FileBlobConstructorParams; + SameProcessBlobConstructorParams; + + // This type may only be sent from parent to child. + MysteryBlobConstructorParams; + + // These types may only be sent from child to parent. + SlicedBlobConstructorParams; + KnownBlobConstructorParams; +}; + +struct ChildBlobConstructorParams +{ + nsID id; + + // May not be SlicedBlobConstructorParams or KnownBlobConstructorParams. + AnyBlobConstructorParams blobParams; +}; + +struct ParentBlobConstructorParams +{ + // May not be MysteryBlobConstructorParams. + AnyBlobConstructorParams blobParams; +}; + +union BlobConstructorParams +{ + ChildBlobConstructorParams; + ParentBlobConstructorParams; +}; + +union IPCDataTransferData +{ + nsString; // text + Shmem; // images using Shmem + PBlob; // files +}; + +struct IPCDataTransferImage +{ + uint32_t width; + uint32_t height; + uint32_t stride; + uint8_t format; +}; + +struct IPCDataTransferItem +{ + nsCString flavor; + // The image details are only used when transferring images. + IPCDataTransferImage imageDetails; + IPCDataTransferData data; +}; + +struct IPCDataTransfer +{ + IPCDataTransferItem[] items; +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/DatePickerParent.cpp b/dom/ipc/DatePickerParent.cpp new file mode 100644 index 000000000..509944ddd --- /dev/null +++ b/dom/ipc/DatePickerParent.cpp @@ -0,0 +1,87 @@ +/* -*- 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 "DatePickerParent.h" +#include "nsComponentManagerUtils.h" +#include "nsIDocument.h" +#include "nsIDOMWindow.h" +#include "mozilla/Unused.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/TabParent.h" + +using mozilla::Unused; +using namespace mozilla::dom; + +NS_IMPL_ISUPPORTS(DatePickerParent::DatePickerShownCallback, + nsIDatePickerShownCallback); + +NS_IMETHODIMP +DatePickerParent::DatePickerShownCallback::Cancel() +{ + if (mDatePickerParent) { + Unused << mDatePickerParent->SendCancel(); + } + return NS_OK; +} + +NS_IMETHODIMP +DatePickerParent::DatePickerShownCallback::Done(const nsAString& aDate) +{ + if (mDatePickerParent) { + Unused << mDatePickerParent->Send__delete__(mDatePickerParent, + nsString(aDate)); + } + return NS_OK; +} + +void +DatePickerParent::DatePickerShownCallback::Destroy() +{ + mDatePickerParent = nullptr; +} + +bool +DatePickerParent::CreateDatePicker() +{ + mPicker = do_CreateInstance("@mozilla.org/datepicker;1"); + if (!mPicker) { + return false; + } + + Element* ownerElement = TabParent::GetFrom(Manager())->GetOwnerElement(); + if (!ownerElement) { + return false; + } + + nsCOMPtr<mozIDOMWindowProxy> window = do_QueryInterface(ownerElement->OwnerDoc()->GetWindow()); + if (!window) { + return false; + } + + return NS_SUCCEEDED(mPicker->Init(window, mTitle, mInitialDate)); +} + +bool +DatePickerParent::RecvOpen() +{ + if (!CreateDatePicker()) { + Unused << Send__delete__(this, mInitialDate); + return true; + } + + mCallback = new DatePickerShownCallback(this); + + mPicker->Open(mCallback); + return true; +}; + +void +DatePickerParent::ActorDestroy(ActorDestroyReason aWhy) +{ + if (mCallback) { + mCallback->Destroy(); + } +} diff --git a/dom/ipc/DatePickerParent.h b/dom/ipc/DatePickerParent.h new file mode 100644 index 000000000..73b66f96c --- /dev/null +++ b/dom/ipc/DatePickerParent.h @@ -0,0 +1,61 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_DatePickerParent_h +#define mozilla_dom_DatePickerParent_h + +#include "mozilla/dom/PDatePickerParent.h" +#include "nsIDatePicker.h" + +namespace mozilla { +namespace dom { + +class DatePickerParent : public PDatePickerParent +{ + public: + DatePickerParent(const nsString& aTitle, + const nsString& aInitialDate) + : mTitle(aTitle) + , mInitialDate(aInitialDate) + {} + + virtual bool RecvOpen() override; + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + class DatePickerShownCallback final + : public nsIDatePickerShownCallback + { + public: + explicit DatePickerShownCallback(DatePickerParent* aDatePickerParnet) + : mDatePickerParent(aDatePickerParnet) + {} + + NS_DECL_ISUPPORTS + NS_DECL_NSIDATEPICKERSHOWNCALLBACK + + void Destroy(); + + private: + ~DatePickerShownCallback() {} + DatePickerParent* mDatePickerParent; + }; + + private: + virtual ~DatePickerParent() {} + + bool CreateDatePicker(); + + RefPtr<DatePickerShownCallback> mCallback; + nsCOMPtr<nsIDatePicker> mPicker; + + nsString mTitle; + nsString mInitialDate; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_DatePickerParent_h diff --git a/dom/ipc/FilePickerParent.cpp b/dom/ipc/FilePickerParent.cpp new file mode 100644 index 000000000..0d4a17978 --- /dev/null +++ b/dom/ipc/FilePickerParent.cpp @@ -0,0 +1,306 @@ +/* -*- 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 "FilePickerParent.h" +#include "nsComponentManagerUtils.h" +#include "nsNetCID.h" +#include "nsIDocument.h" +#include "nsIDOMWindow.h" +#include "nsIFile.h" +#include "nsISimpleEnumerator.h" +#include "mozilla/Unused.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/FileSystemSecurity.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/dom/ipc/BlobParent.h" + +using mozilla::Unused; +using namespace mozilla::dom; + +NS_IMPL_ISUPPORTS(FilePickerParent::FilePickerShownCallback, + nsIFilePickerShownCallback); + +NS_IMETHODIMP +FilePickerParent::FilePickerShownCallback::Done(int16_t aResult) +{ + if (mFilePickerParent) { + mFilePickerParent->Done(aResult); + } + return NS_OK; +} + +void +FilePickerParent::FilePickerShownCallback::Destroy() +{ + mFilePickerParent = nullptr; +} + +FilePickerParent::~FilePickerParent() +{ +} + +// Before sending a blob to the child, we need to get its size and modification +// date. Otherwise it will be sent as a "mystery blob" by +// GetOrCreateActorForBlob, which will cause problems for the child +// process. This runnable stat()s the file off the main thread. +// +// We run code in three places: +// 1. The main thread calls Dispatch() to start the runnable. +// 2. The stream transport thread stat()s the file in Run() and then dispatches +// the same runnable on the main thread. +// 3. The main thread sends the results over IPC. +FilePickerParent::IORunnable::IORunnable(FilePickerParent *aFPParent, + nsTArray<nsCOMPtr<nsIFile>>& aFiles, + bool aIsDirectory) + : mFilePickerParent(aFPParent) + , mIsDirectory(aIsDirectory) +{ + mFiles.SwapElements(aFiles); + MOZ_ASSERT_IF(aIsDirectory, mFiles.Length() == 1); +} + +bool +FilePickerParent::IORunnable::Dispatch() +{ + MOZ_ASSERT(NS_IsMainThread()); + + mEventTarget = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); + if (!mEventTarget) { + return false; + } + + nsresult rv = mEventTarget->Dispatch(this, NS_DISPATCH_NORMAL); + return NS_SUCCEEDED(rv); +} + +NS_IMETHODIMP +FilePickerParent::IORunnable::Run() +{ + // If we're on the main thread, then that means we're done. Just send the + // results. + if (NS_IsMainThread()) { + if (mFilePickerParent) { + mFilePickerParent->SendFilesOrDirectories(mResults); + } + return NS_OK; + } + + // We're not on the main thread, so do the IO. + + for (uint32_t i = 0; i < mFiles.Length(); ++i) { + if (mIsDirectory) { + nsAutoString path; + nsresult rv = mFiles[i]->GetPath(path); + if (NS_WARN_IF(NS_FAILED(rv))) { + continue; + } + + BlobImplOrString* data = mResults.AppendElement(); + data->mType = BlobImplOrString::eDirectoryPath; + data->mDirectoryPath = path; + continue; + } + + RefPtr<BlobImpl> blobImpl = new BlobImplFile(mFiles[i]); + + ErrorResult error; + blobImpl->GetSize(error); + if (NS_WARN_IF(error.Failed())) { + error.SuppressException(); + continue; + } + + blobImpl->GetLastModified(error); + if (NS_WARN_IF(error.Failed())) { + error.SuppressException(); + continue; + } + + BlobImplOrString* data = mResults.AppendElement(); + data->mType = BlobImplOrString::eBlobImpl; + data->mBlobImpl = blobImpl; + } + + // Dispatch ourselves back on the main thread. + if (NS_FAILED(NS_DispatchToMainThread(this))) { + // It's hard to see how we can recover gracefully in this case. The child + // process is waiting for an IPC, but that can only happen on the main + // thread. + MOZ_CRASH(); + } + + return NS_OK; +} + +void +FilePickerParent::IORunnable::Destroy() +{ + mFilePickerParent = nullptr; +} + +void +FilePickerParent::SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData) +{ + nsIContentParent* parent = TabParent::GetFrom(Manager())->Manager(); + + if (mMode == nsIFilePicker::modeGetFolder) { + MOZ_ASSERT(aData.Length() <= 1); + if (aData.IsEmpty()) { + Unused << Send__delete__(this, void_t(), mResult); + return; + } + + MOZ_ASSERT(aData[0].mType == BlobImplOrString::eDirectoryPath); + + // Let's inform the security singleton about the given access of this tab on + // this directory path. + RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate(); + fss->GrantAccessToContentProcess(parent->ChildID(), + aData[0].mDirectoryPath); + + InputDirectory input; + input.directoryPath() = aData[0].mDirectoryPath; + Unused << Send__delete__(this, input, mResult); + return; + } + + InfallibleTArray<PBlobParent*> blobs; + + for (unsigned i = 0; i < aData.Length(); i++) { + MOZ_ASSERT(aData[i].mType == BlobImplOrString::eBlobImpl); + BlobParent* blobParent = parent->GetOrCreateActorForBlobImpl(aData[i].mBlobImpl); + if (blobParent) { + blobs.AppendElement(blobParent); + } + } + + InputBlobs inblobs; + inblobs.blobsParent().SwapElements(blobs); + Unused << Send__delete__(this, inblobs, mResult); +} + +void +FilePickerParent::Done(int16_t aResult) +{ + mResult = aResult; + + if (mResult != nsIFilePicker::returnOK) { + Unused << Send__delete__(this, void_t(), mResult); + return; + } + + nsTArray<nsCOMPtr<nsIFile>> files; + if (mMode == nsIFilePicker::modeOpenMultiple) { + nsCOMPtr<nsISimpleEnumerator> iter; + NS_ENSURE_SUCCESS_VOID(mFilePicker->GetFiles(getter_AddRefs(iter))); + + nsCOMPtr<nsISupports> supports; + bool loop = true; + while (NS_SUCCEEDED(iter->HasMoreElements(&loop)) && loop) { + iter->GetNext(getter_AddRefs(supports)); + if (supports) { + nsCOMPtr<nsIFile> file = do_QueryInterface(supports); + MOZ_ASSERT(file); + files.AppendElement(file); + } + } + } else { + nsCOMPtr<nsIFile> file; + mFilePicker->GetFile(getter_AddRefs(file)); + if (file) { + files.AppendElement(file); + } + } + + if (files.IsEmpty()) { + Unused << Send__delete__(this, void_t(), mResult); + return; + } + + MOZ_ASSERT(!mRunnable); + mRunnable = new IORunnable(this, files, mMode == nsIFilePicker::modeGetFolder); + + // Dispatch to background thread to do I/O: + if (!mRunnable->Dispatch()) { + Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel); + } +} + +bool +FilePickerParent::CreateFilePicker() +{ + mFilePicker = do_CreateInstance("@mozilla.org/filepicker;1"); + if (!mFilePicker) { + return false; + } + + Element* element = TabParent::GetFrom(Manager())->GetOwnerElement(); + if (!element) { + return false; + } + + nsCOMPtr<mozIDOMWindowProxy> window = element->OwnerDoc()->GetWindow(); + if (!window) { + return false; + } + + return NS_SUCCEEDED(mFilePicker->Init(window, mTitle, mMode)); +} + +bool +FilePickerParent::RecvOpen(const int16_t& aSelectedType, + const bool& aAddToRecentDocs, + const nsString& aDefaultFile, + const nsString& aDefaultExtension, + InfallibleTArray<nsString>&& aFilters, + InfallibleTArray<nsString>&& aFilterNames, + const nsString& aDisplayDirectory, + const nsString& aOkButtonLabel) +{ + if (!CreateFilePicker()) { + Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel); + return true; + } + + mFilePicker->SetAddToRecentDocs(aAddToRecentDocs); + + for (uint32_t i = 0; i < aFilters.Length(); ++i) { + mFilePicker->AppendFilter(aFilterNames[i], aFilters[i]); + } + + mFilePicker->SetDefaultString(aDefaultFile); + mFilePicker->SetDefaultExtension(aDefaultExtension); + mFilePicker->SetFilterIndex(aSelectedType); + mFilePicker->SetOkButtonLabel(aOkButtonLabel); + + if (!aDisplayDirectory.IsEmpty()) { + nsCOMPtr<nsIFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID); + if (localFile) { + localFile->InitWithPath(aDisplayDirectory); + mFilePicker->SetDisplayDirectory(localFile); + } + } + + mCallback = new FilePickerShownCallback(this); + + mFilePicker->Open(mCallback); + return true; +} + +void +FilePickerParent::ActorDestroy(ActorDestroyReason aWhy) +{ + if (mCallback) { + mCallback->Destroy(); + mCallback = nullptr; + } + if (mRunnable) { + mRunnable->Destroy(); + mRunnable = nullptr; + } +} diff --git a/dom/ipc/FilePickerParent.h b/dom/ipc/FilePickerParent.h new file mode 100644 index 000000000..599d118ff --- /dev/null +++ b/dom/ipc/FilePickerParent.h @@ -0,0 +1,110 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_FilePickerParent_h +#define mozilla_dom_FilePickerParent_h + +#include "nsIEventTarget.h" +#include "nsIFilePicker.h" +#include "nsCOMArray.h" +#include "nsThreadUtils.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/PFilePickerParent.h" + +class nsIFile; + +namespace mozilla { +namespace dom { + +class FilePickerParent : public PFilePickerParent +{ + public: + FilePickerParent(const nsString& aTitle, + const int16_t& aMode) + : mTitle(aTitle) + , mMode(aMode) + {} + + virtual ~FilePickerParent(); + + void Done(int16_t aResult); + + struct BlobImplOrString + { + RefPtr<BlobImpl> mBlobImpl; + nsString mDirectoryPath; + + enum { + eBlobImpl, + eDirectoryPath + } mType; + }; + + void SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData); + + virtual bool RecvOpen(const int16_t& aSelectedType, + const bool& aAddToRecentDocs, + const nsString& aDefaultFile, + const nsString& aDefaultExtension, + InfallibleTArray<nsString>&& aFilters, + InfallibleTArray<nsString>&& aFilterNames, + const nsString& aDisplayDirectory, + const nsString& aOkButtonLabel) override; + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + class FilePickerShownCallback : public nsIFilePickerShownCallback + { + public: + explicit FilePickerShownCallback(FilePickerParent* aFilePickerParent) + : mFilePickerParent(aFilePickerParent) + { } + + NS_DECL_ISUPPORTS + NS_DECL_NSIFILEPICKERSHOWNCALLBACK + + void Destroy(); + + private: + virtual ~FilePickerShownCallback() {} + FilePickerParent* mFilePickerParent; + }; + + private: + bool CreateFilePicker(); + + // This runnable is used to do some I/O operation on a separate thread. + class IORunnable : public Runnable + { + FilePickerParent* mFilePickerParent; + nsTArray<nsCOMPtr<nsIFile>> mFiles; + nsTArray<BlobImplOrString> mResults; + nsCOMPtr<nsIEventTarget> mEventTarget; + bool mIsDirectory; + + public: + IORunnable(FilePickerParent *aFPParent, + nsTArray<nsCOMPtr<nsIFile>>& aFiles, + bool aIsDirectory); + + bool Dispatch(); + NS_IMETHOD Run(); + void Destroy(); + }; + + RefPtr<IORunnable> mRunnable; + RefPtr<FilePickerShownCallback> mCallback; + nsCOMPtr<nsIFilePicker> mFilePicker; + + nsString mTitle; + int16_t mMode; + int16_t mResult; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_FilePickerParent_h diff --git a/dom/ipc/IdType.h b/dom/ipc/IdType.h new file mode 100644 index 000000000..b683178eb --- /dev/null +++ b/dom/ipc/IdType.h @@ -0,0 +1,73 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_IdType_h +#define mozilla_dom_IdType_h + +#include "ipc/IPCMessageUtils.h" + +namespace IPC { +template<typename T> struct ParamTraits; +} // namespace IPC + +namespace mozilla { +namespace dom { +class ContentParent; +class TabParent; + + +template<typename T> +class IdType +{ + friend struct IPC::ParamTraits<IdType<T>>; + +public: + IdType() : mId(0) {} + explicit IdType(uint64_t aId) : mId(aId) {} + + operator uint64_t() const { return mId; } + + IdType& operator=(uint64_t aId) + { + mId = aId; + return *this; + } + + bool operator<(const IdType& rhs) + { + return mId < rhs.mId; + } +private: + uint64_t mId; +}; + +typedef IdType<TabParent> TabId; +typedef IdType<ContentParent> ContentParentId; + +} // namespace dom +} // namespace mozilla + +namespace IPC { + +template<typename T> +struct ParamTraits<mozilla::dom::IdType<T>> +{ + typedef mozilla::dom::IdType<T> paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mId); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + return ReadParam(aMsg, aIter, &aResult->mId); + } +}; + +} // namespace IPC + +#endif // mozilla_dom_IdType_h diff --git a/dom/ipc/PBlob.ipdl b/dom/ipc/PBlob.ipdl new file mode 100644 index 000000000..3e758fe92 --- /dev/null +++ b/dom/ipc/PBlob.ipdl @@ -0,0 +1,57 @@ +/* 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 protocol PBackground; +include protocol PBlobStream; +include protocol PContent; +include protocol PContentBridge; +include protocol PFileDescriptorSet; +include protocol PSendStream; + +include BlobTypes; +include DOMTypes; +include InputStreamParams; + +namespace mozilla { +namespace dom { + +union ResolveMysteryParams +{ + NormalBlobConstructorParams; + FileBlobConstructorParams; +}; + +sync protocol PBlob +{ + manager PBackground or PContent or PContentBridge; + manages PBlobStream; + +both: + async __delete__(); + +parent: + async PBlobStream(uint64_t begin, uint64_t length); + + async ResolveMystery(ResolveMysteryParams params); + + sync BlobStreamSync(uint64_t begin, uint64_t length) + returns (InputStreamParams params, OptionalFileDescriptorSet fds); + + sync WaitForSliceCreation(); + + // Use only for testing! + sync GetFileId() + returns (int64_t fileId); + + sync GetFilePath() + returns (nsString filePath); + +child: + // This method must be called by the parent when the PBlobParent is fully + // created in order to release the known blob. + async CreatedFromKnownBlob(); +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/PBlobStream.ipdl b/dom/ipc/PBlobStream.ipdl new file mode 100644 index 000000000..f99399c8a --- /dev/null +++ b/dom/ipc/PBlobStream.ipdl @@ -0,0 +1,22 @@ +/* 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 protocol PBlob; +include protocol PFileDescriptorSet; +include BlobTypes; +include InputStreamParams; + +namespace mozilla { +namespace dom { + +protocol PBlobStream +{ + manager PBlob; + +child: + async __delete__(InputStreamParams params, OptionalFileDescriptorSet fds); +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl new file mode 100644 index 000000000..249657c26 --- /dev/null +++ b/dom/ipc/PBrowser.ipdl @@ -0,0 +1,928 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */ + +/* 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 protocol PBlob; +include protocol PColorPicker; +include protocol PContent; +include protocol PContentBridge; +include protocol PDatePicker; +include protocol PDocAccessible; +include protocol PDocumentRenderer; +include protocol PFilePicker; +include protocol PIndexedDBPermissionRequest; +include protocol PRenderFrame; +include protocol PPluginWidget; +include protocol PRemotePrintJob; +include DOMTypes; +include JavaScriptTypes; +include URIParams; +include PPrintingTypes; +include PTabContext; + + +using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h"; +using class mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h"; +using struct gfxSize from "gfxPoint.h"; +using CSSRect from "Units.h"; +using CSSSize from "Units.h"; +using mozilla::LayoutDeviceIntRect from "Units.h"; +using mozilla::LayoutDeviceIntPoint from "Units.h"; +using mozilla::LayoutDevicePoint from "Units.h"; +using mozilla::ScreenIntPoint from "Units.h"; +using ScreenIntSize from "Units.h"; +using struct mozilla::layers::FrameMetrics from "FrameMetrics.h"; +using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h"; +using struct mozilla::layers::ZoomConstraints from "FrameMetrics.h"; +using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h"; +using mozilla::layers::GeckoContentController::TapType from "mozilla/layers/GeckoContentController.h"; +using FrameMetrics::ViewID from "FrameMetrics.h"; +using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; +using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h"; +using nscolor from "nsColor.h"; +using class mozilla::WidgetCompositionEvent from "ipc/nsGUIEventIPC.h"; +using struct mozilla::widget::IMENotification from "nsIWidget.h"; +using struct nsIMEUpdatePreference from "nsIWidget.h"; +using mozilla::gfx::IntSize from "mozilla/gfx/Point.h"; +using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h"; +using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h"; +using class mozilla::ContentCache from "ipc/nsGUIEventIPC.h"; +using class mozilla::WidgetKeyboardEvent from "ipc/nsGUIEventIPC.h"; +using class mozilla::WidgetMouseEvent from "ipc/nsGUIEventIPC.h"; +using class mozilla::WidgetWheelEvent from "ipc/nsGUIEventIPC.h"; +using class mozilla::WidgetDragEvent from "ipc/nsGUIEventIPC.h"; +using struct nsRect from "nsRect.h"; +using class mozilla::WidgetSelectionEvent from "ipc/nsGUIEventIPC.h"; +using class mozilla::WidgetTouchEvent from "ipc/nsGUIEventIPC.h"; +using class mozilla::WidgetPluginEvent from "ipc/nsGUIEventIPC.h"; +using struct mozilla::dom::RemoteDOMEvent from "mozilla/dom/TabMessageUtils.h"; +using mozilla::dom::ScreenOrientationInternal from "mozilla/dom/ScreenOrientation.h"; +using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h"; +using mozilla::CSSToScreenScale from "Units.h"; +using mozilla::CommandInt from "mozilla/EventForwards.h"; +using mozilla::WritingMode from "mozilla/WritingModes.h"; +using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h"; +using nsIWidget::TouchPointerState from "nsIWidget.h"; +using struct LookAndFeelInt from "mozilla/widget/WidgetMessageUtils.h"; +using class mozilla::dom::MessagePort from "mozilla/dom/MessagePort.h"; +using class mozilla::dom::ipc::StructuredCloneData from "mozilla/dom/ipc/StructuredCloneData.h"; +using mozilla::EventMessage from "mozilla/EventForwards.h"; +using nsEventStatus from "mozilla/EventForwards.h"; +using mozilla::Modifiers from "mozilla/EventForwards.h"; +using nsSizeMode from "nsIWidgetListener.h"; +using mozilla::widget::CandidateWindowPosition from "ipc/nsGUIEventIPC.h"; +using class mozilla::NativeEventData from "ipc/nsGUIEventIPC.h"; +using mozilla::FontRange from "ipc/nsGUIEventIPC.h"; +using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/IPCTypes.h"; + +namespace mozilla { +namespace dom { + +struct NativeKeyBinding +{ + CommandInt[] singleLineCommands; + CommandInt[] multiLineCommands; + CommandInt[] richTextCommands; +}; + +union MaybeNativeKeyBinding +{ + NativeKeyBinding; + void_t; +}; + +struct ShowInfo +{ + nsString name; + bool fullscreenAllowed; + bool isPrivate; + bool fakeShowInfo; + bool isTransparent; + float dpi; + int32_t widgetRounding; + double defaultScale; +}; + +union OptionalShmem +{ + void_t; + Shmem; +}; + +nested(upto inside_cpow) sync protocol PBrowser +{ + manager PContent or PContentBridge; + + manages PColorPicker; + manages PDatePicker; + manages PDocAccessible; + manages PDocumentRenderer; + manages PFilePicker; + manages PIndexedDBPermissionRequest; + manages PRenderFrame; + manages PPluginWidget; + +both: + async AsyncMessage(nsString aMessage, CpowEntry[] aCpows, + Principal aPrincipal, ClonedMessageData aData); + + /** + * Create a layout frame (encapsulating a remote layer tree) for + * the page that is currently loaded in the <browser>. + */ + async PRenderFrame(); + +parent: + /** + * Tell the parent process a new accessible document has been created. + * aParentDoc is the accessible document it was created in if any, and + * aParentAcc is the id of the accessible in that document the new document + * is a child of. aMsaaID is the MSAA id for this content process, and + * is only valid on Windows. Set to 0 on other platforms. aDocCOMProxy + * is also Windows-specific and should be set to 0 on other platforms. + */ + async PDocAccessible(nullable PDocAccessible aParentDoc, uint64_t aParentAcc, + uint32_t aMsaaID, IAccessibleHolder aDocCOMProxy); + + /* + * Creates a new remoted nsIWidget connection for windowed plugins + * in e10s mode. This is always initiated from the child in response + * to windowed plugin creation. + */ + sync PPluginWidget(); + + /** + * Return native data of root widget + */ + nested(inside_cpow) sync GetWidgetNativeData() returns (WindowsHandle value); + + /** + * Sends an NS_NATIVE_CHILD_OF_SHAREABLE_WINDOW to be adopted by the + * widget's shareable window on the chrome side. Only used on Windows. + */ + async SetNativeChildOfShareableWindow(uintptr_t childWindow); + + /** + * When content moves focus from a native plugin window that's a child + * of the native browser window we need to move native focus to the + * browser. Otherwise the plugin window will never relinquish focus. + */ + sync DispatchFocusToTopLevelWindow(); + +parent: + /** + * When child sends this message, parent should move focus to + * the next or previous focusable element or document. + */ + async MoveFocus(bool forward, bool forDocumentNavigation); + + /** + * SizeShellTo request propagation to parent. + * + * aFlag Can indicate if one of the dimensions should be ignored. + * If only one dimension has changed it has to be indicated + * by the nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_* flags. + * aShellItemWidth, + * aShellItemHeight On parent side we won't be able to decide the dimensions + * of the shell item parameter in the original SizeShellTo + * call so we send over its dimensions that will be used + * for the actual resize. + **/ + async SizeShellTo(uint32_t aFlag, int32_t aWidth, int32_t aHeight, + int32_t aShellItemWidth, int32_t aShellItemHeight); + + /** + * Called by the child to inform the parent that links are dropped into + * content area. + * + * aLinks A flat array of url, name, and type for each link + */ + async DropLinks(nsString[] aLinks); + + async Event(RemoteDOMEvent aEvent); + + sync SyncMessage(nsString aMessage, ClonedMessageData aData, + CpowEntry[] aCpows, Principal aPrincipal) + returns (StructuredCloneData[] retval); + + nested(inside_sync) sync RpcMessage(nsString aMessage, ClonedMessageData aData, + CpowEntry[] aCpows, Principal aPrincipal) + returns (StructuredCloneData[] retval); + + /** + * Notifies chrome that there is a focus change involving an editable + * object (input, textarea, document, contentEditable. etc.) + * + * contentCache Cache of content + * notification Whole data of the notification + * preference Native widget preference for IME updates + */ + nested(inside_cpow) sync NotifyIMEFocus(ContentCache contentCache, + IMENotification notification) + returns (nsIMEUpdatePreference preference); + + /** + * Notifies chrome that there has been a change in text content + * One call can encompass both a delete and an insert operation + * Only called when NotifyIMEFocus returns PR_TRUE for mWantUpdates + * + * contentCache Cache of content + * notification Whole data of the notification + */ + nested(inside_cpow) async NotifyIMETextChange(ContentCache contentCache, + IMENotification notification); + + /** + * Notifies chrome that there is a IME compostion rect updated + * + * contentCache Cache of content + */ + nested(inside_cpow) async NotifyIMECompositionUpdate(ContentCache contentCache, + IMENotification notification); + + /** + * Notifies chrome that there has been a change in selection + * Only called when NotifyIMEFocus returns PR_TRUE for mWantUpdates + * + * contentCache Cache of content + * notification Whole data of the notification + */ + nested(inside_cpow) async NotifyIMESelection(ContentCache contentCache, + IMENotification notification); + + /** + * Notifies chrome of updating its content cache. + * This is useful if content is modified but we don't need to notify IME. + * + * contentCache Cache of content + */ + nested(inside_cpow) async UpdateContentCache(ContentCache contentCache); + + /** + * Notifies IME of mouse button event on a character in focused editor. + * + * Returns true if the mouse button event is consumd by IME. + */ + nested(inside_cpow) sync NotifyIMEMouseButtonEvent(IMENotification notification) + returns (bool consumedByIME); + + /** + * Notifies chrome to position change + * + * contentCache Cache of content + */ + nested(inside_cpow) async NotifyIMEPositionChange(ContentCache contentCache, + IMENotification notification); + + /** + * Requests chrome to commit or cancel composition of IME. + * + * cancel Set true if composition should be cancelled. + * + * isCommitted Returns true if the request causes composition + * being committed synchronously. + * committedString Returns committed string. The may be non-empty + * string even if cancel is true because IME may + * try to restore selected string which was + * replaced with the composition. + */ + nested(inside_cpow) sync RequestIMEToCommitComposition(bool cancel) + returns (bool isCommitted, nsString committedString); + + /** + * OnEventNeedingAckHandled() is called after a child process dispatches a + * composition event or a selection event which is sent from the parent + * process. + * + * message The message value of the handled event. + */ + nested(inside_cpow) async OnEventNeedingAckHandled(EventMessage message); + + /** + * Tells chrome to start plugin IME. If this results in a string getting + * committed, the result is in aCommitted (otherwise aCommitted is empty). + * + * aKeyboardEvent The event with which plugin IME is to be started + * panelX and panelY Location in screen coordinates of the IME input panel + * (should be just under the plugin) + * aCommitted The string committed during IME -- otherwise empty + */ + nested(inside_cpow) sync StartPluginIME(WidgetKeyboardEvent aKeyboardEvent, + int32_t panelX, int32_t panelY) + returns (nsString aCommitted); + + /** + * Tells chrome (and specifically the appropriate widget) whether or not + * a plugin (inside the widget) has the keyboard focus. Should be sent + * when the keyboard focus changes too or from a plugin. + * + * aFocused Whether or not a plugin is focused + */ + nested(inside_cpow) async SetPluginFocused(bool aFocused); + + /** + * Set IME candidate window by windowless plugin if plugin has focus. + */ + async SetCandidateWindowForPlugin(CandidateWindowPosition aPosition); + + /** + * Notifies the parent process of native key event data received in a + * plugin process directly. + * + * aKeyEventData The native key event data. The actual type copied into + * NativeEventData depending on the caller. Please check + * PluginInstanceChild. + */ + nested(inside_cpow) async OnWindowedPluginKeyEvent(NativeEventData aKeyEventData); + + /** + * When plugin event isn't consumed, call this + */ + async DefaultProcOfPluginEvent(WidgetPluginEvent aEvent); + + /** + * Request that the parent process move focus to the browser's frame. If + * canRaise is true, the window can be raised if it is inactive. + */ + async RequestFocus(bool canRaise); + + /** + * Indicate, based on the current state, that some commands are enabled and + * some are disabled. + */ + async EnableDisableCommands(nsString action, + nsCString[] enabledCommands, + nsCString[] disabledCommands); + + nested(inside_cpow) sync GetInputContext() returns (int32_t IMEEnabled, + int32_t IMEOpen); + + nested(inside_cpow) async SetInputContext(int32_t IMEEnabled, + int32_t IMEOpen, + nsString type, + nsString inputmode, + nsString actionHint, + int32_t cause, + int32_t focusChange); + + sync IsParentWindowMainWidgetVisible() returns (bool visible); + + /** + * Gets the DPI of the screen corresponding to this browser. + */ + sync GetDPI() returns (float value); + + /** + * Gets the default scaling factor of the screen corresponding to this browser. + */ + sync GetDefaultScale() returns (double value); + + /** + * Gets the rounding of coordinates in the widget. + */ + sync GetWidgetRounding() returns (int32_t value); + + /** + * Gets maximum of touch points at current device. + */ + sync GetMaxTouchPoints() returns (uint32_t value); + + /** + * Set the native cursor. + * @param value + * The widget cursor to set. + * @param force + * Invalidate any locally cached cursor settings and force an + * update. + */ + async SetCursor(uint32_t value, bool force); + + /** + * Set the native cursor using a custom image. + * @param cursorData + * Serialized image data. + * @param width + * Width of the image. + * @param height + * Height of the image. + * @param stride + * Stride used in the image data. + * @param format + * Image format, see gfx::SurfaceFormat for possible values. + * @param hotspotX + * Horizontal hotspot of the image, as specified by the css cursor property. + * @param hotspotY + * Vertical hotspot of the image, as specified by the css cursor property. + * @param force + * Invalidate any locally cached cursor settings and force an + * update. + */ + async SetCustomCursor(nsCString cursorData, uint32_t width, uint32_t height, + uint32_t stride, uint8_t format, + uint32_t hotspotX, uint32_t hotspotY, bool force); + + /** + * Used to set the current text of the status tooltip. + * Nowadays this is mainly used for link locations on hover. + */ + async SetStatus(uint32_t type, nsString status); + + /** + * Show/hide a tooltip when the mouse hovers over an element in the content + * document. + */ + async ShowTooltip(uint32_t x, uint32_t y, nsString tooltip, nsString direction); + async HideTooltip(); + + /** + * Create an asynchronous color picker on the parent side, + * but don't open it yet. + */ + async PColorPicker(nsString title, nsString initialColor); + + /** + * Create an asynchronous date picker on the parent side, + * but don't open it yet. + */ + async PDatePicker(nsString title, nsString initialDate); + + async PFilePicker(nsString aTitle, int16_t aMode); + + /** + * Initiates an asynchronous request for one of the special indexedDB + * permissions for the provided principal. + * + * @param principal + * The principal of the request. + * + * NOTE: The principal is untrusted in the parent process. Only + * principals that can live in the content process should + * provided. + */ + async PIndexedDBPermissionRequest(Principal principal); + + /** + * window.open from inside <iframe mozbrowser> is special. When the child + * process calls window.open, it creates a new PBrowser (in its own + * process), then calls BrowserFrameOpenWindow on it. + * + * The parent process gets a chance to accept or reject the window.open + * call, and windowOpened is set to true if we ended up going through with + * the window.open. + * + * @param opener the PBrowser whose content called window.open. + */ + sync BrowserFrameOpenWindow(PBrowser opener, PRenderFrame renderFrame, + nsString aURL, nsString aName, nsString aFeatures) + returns (bool windowOpened, + TextureFactoryIdentifier textureFactoryIdentifier, + uint64_t layersId); + + /** + * Tells the containing widget whether the given input block results in a + * swipe. Should be called in response to a WidgetWheelEvent that has + * mFlags.mCanTriggerSwipe set on it. + */ + async RespondStartSwipeEvent(uint64_t aInputBlockId, bool aStartSwipe); + + /** + * Brings up the auth prompt dialog. + * Called when this is the PBrowserParent for a nested remote iframe. + * aCallbackId corresponds to an nsIAuthPromptCallback that lives in the + * root process. It will be passed back to the root process with either the + * OnAuthAvailable or OnAuthCancelled message. + */ + async AsyncAuthPrompt(nsCString uri, nsString realm, uint64_t aCallbackId); + + /** + * Look up dictionary by selected word for OSX + * + * @param aText The word to look up + * @param aFontRange Text decoration of aText + * @param aIsVertical true if vertical layout + */ + async LookUpDictionary(nsString aText, FontRange[] aFontRangeArray, + bool aIsVertical, LayoutDeviceIntPoint aPoint); + + async __delete__(); + + async ReplyKeyEvent(WidgetKeyboardEvent event); + + async DispatchAfterKeyboardEvent(WidgetKeyboardEvent event); + + sync RequestNativeKeyBindings(WidgetKeyboardEvent event) + returns (MaybeNativeKeyBinding bindings); + + async SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout, + int32_t aNativeKeyCode, + uint32_t aModifierFlags, + nsString aCharacters, + nsString aUnmodifiedCharacters, + uint64_t aObserverId); + async SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint, + uint32_t aNativeMessage, + uint32_t aModifierFlags, + uint64_t aObserverId); + async SynthesizeNativeMouseMove(LayoutDeviceIntPoint aPoint, + uint64_t aObserverId); + async SynthesizeNativeMouseScrollEvent(LayoutDeviceIntPoint aPoint, + uint32_t aNativeMessage, + double aDeltaX, + double aDeltaY, + double aDeltaZ, + uint32_t aModifierFlags, + uint32_t aAdditionalFlags, + uint64_t aObserverId); + async SynthesizeNativeTouchPoint(uint32_t aPointerId, + TouchPointerState aPointerState, + LayoutDeviceIntPoint aPoint, + double aPointerPressure, + uint32_t aPointerOrientation, + uint64_t aObserverId); + async SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint, + bool aLongTap, + uint64_t aObserverId); + async ClearNativeTouchSequence(uint64_t aObserverId); + + /** + * Returns the number of tabs in the window via the out parameter. + * If the number of tabs can't be determined, returns 0. + * + * @param aValue where to store the tab count + */ + sync GetTabCount() returns (uint32_t value); + + async AccessKeyNotHandled(WidgetKeyboardEvent event); + +child: + async NativeSynthesisResponse(uint64_t aObserverId, nsCString aResponse); + + +parent: + + /** + * Child informs the parent that the graphics objects are ready for + * compositing. This is sent when all pending changes have been + * sent to the compositor and are ready to be shown on the next composite. + * @see PCompositor + * @see RequestNotifyAfterRemotePaint + */ + async RemotePaintIsReady(); + + /** + * Child informs the parent that the layer tree is already available. + */ + async ForcePaintNoOp(uint64_t aLayerObserverEpoch); + + /** + * Sent by the child to the parent to inform it that an update to the + * dimensions has been requested, likely through win.moveTo or resizeTo + */ + async SetDimensions(uint32_t aFlags, int32_t aX, int32_t aY, int32_t aCx, int32_t aCy); + + nested(inside_sync) sync DispatchWheelEvent(WidgetWheelEvent event); + nested(inside_sync) sync DispatchMouseEvent(WidgetMouseEvent event); + nested(inside_sync) sync DispatchKeyboardEvent(WidgetKeyboardEvent event); + + async InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action, + OptionalShmem visualData, + uint32_t stride, uint8_t format, + LayoutDeviceIntRect dragRect); + + async AudioChannelActivityNotification(uint32_t aAudioChannel, + bool aActive); + + // After a compositor reset, it is necessary to reconnect each layers ID to + // the compositor of the widget that will render those layers. Note that + // this is sync so we can ensure that messages to the window compositor + // arrive before the TabChild attempts to use its cross-process compositor + // bridge. + sync EnsureLayersConnected(); + + /** + * Notify parent that one or more entries have been added / removed from + * the child session history. + * + * @param aCount the updated number of entries in child session history + */ + async NotifySessionHistoryChange(uint32_t aCount); + + /** + * When the session history is across multiple root docshells, this function + * is used to notify parent that it needs to navigate to an entry out of + * local index of the child. + * + * @param aGlobalIndex The global index of history entry to navigate to. + */ + async RequestCrossBrowserNavigation(uint32_t aGlobalIndex); + +child: + /** + * Notify the remote browser that it has been Show()n on this + * side, with the given |visibleRect|. This message is expected + * to trigger creation of the remote browser's "widget". + * + * |Show()| and |Move()| take IntSizes rather than Rects because + * content processes always render to a virtual <0, 0> top-left + * point. + */ + async Show(ScreenIntSize size, + ShowInfo info, + TextureFactoryIdentifier textureFactoryIdentifier, + uint64_t layersId, + nullable PRenderFrame renderFrame, + bool parentIsActive, + nsSizeMode sizeMode); + + async LoadURL(nsCString uri, ShowInfo info); + + async UpdateDimensions(CSSRect rect, CSSSize size, + ScreenOrientationInternal orientation, + LayoutDeviceIntPoint clientOffset, + LayoutDeviceIntPoint chromeDisp) compressall; + + async SizeModeChanged(nsSizeMode sizeMode); + + /** + * Sending an activate message moves focus to the child. + */ + async Activate(); + + async Deactivate(); + + async ParentActivated(bool aActivated); + + async SetKeyboardIndicators(UIStateChangeType showAccelerators, + UIStateChangeType showFocusRings); + + /** + * StopIMEStateManagement() is called when the process loses focus and + * should stop managing IME state. + */ + async StopIMEStateManagement(); + + /** + * MenuKeyboardListenerInstalled() is called when menu keyboard listener + * is installed in the parent process. + */ + async MenuKeyboardListenerInstalled(bool aInstalled); + + /** + * @see nsIDOMWindowUtils sendMouseEvent. + */ + async MouseEvent(nsString aType, + float aX, + float aY, + int32_t aButton, + int32_t aClickCount, + int32_t aModifiers, + bool aIgnoreRootScrollFrame); + + /** + * When two consecutive mouse move events would be added to the message queue, + * they are 'compressed' by dumping the oldest one. + */ + async RealMouseMoveEvent(WidgetMouseEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId) compress; + /** + * Mouse move events with |reason == eSynthesized| are sent via a separate + * message because they do not generate DOM 'mousemove' events, and the + * 'compress' attribute on RealMouseMoveEvent() could result in a + * |reason == eReal| event being dropped in favour of an |eSynthesized| + * event, and thus a DOM 'mousemove' event to be lost. + */ + async SynthMouseMoveEvent(WidgetMouseEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId); + async RealMouseButtonEvent(WidgetMouseEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId); + async RealKeyEvent(WidgetKeyboardEvent event, MaybeNativeKeyBinding keyBinding); + async MouseWheelEvent(WidgetWheelEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId); + async RealTouchEvent(WidgetTouchEvent aEvent, + ScrollableLayerGuid aGuid, + uint64_t aInputBlockId, + nsEventStatus aApzResponse); + async HandleTap(TapType aType, LayoutDevicePoint point, Modifiers aModifiers, + ScrollableLayerGuid aGuid, uint64_t aInputBlockId); + async RealTouchMoveEvent(WidgetTouchEvent aEvent, + ScrollableLayerGuid aGuid, + uint64_t aInputBlockId, + nsEventStatus aApzResponse); + async RealDragEvent(WidgetDragEvent aEvent, uint32_t aDragAction, uint32_t aDropEffect); + async PluginEvent(WidgetPluginEvent aEvent); + + /** + * @see nsIDOMWindowUtils sendKeyEvent. + */ + async KeyEvent(nsString aType, + int32_t aKeyCode, + int32_t aCharCode, + int32_t aModifiers, + bool aPreventDefault); + + async CompositionEvent(WidgetCompositionEvent event); + + async SelectionEvent(WidgetSelectionEvent event); + + /** + * Call PasteTransferable via a controller on the content process + * to handle the command content event, "pasteTransferable". + */ + async PasteTransferable(IPCDataTransfer aDataTransfer, + bool aIsPrivateData, + Principal aRequestingPrincipal); + + /** + * Activate event forwarding from client to parent. + */ + async ActivateFrameEvent(nsString aType, bool capture); + + async LoadRemoteScript(nsString aURL, bool aRunInGlobalScope); + + /** + * Create a asynchronous request to render whatever document is + * loaded in the child when this message arrives. When the + * request finishes, PDocumentRenderer:__delete__ is sent back to + * this side to notify completion. + * + * |documentRect| is the area of the remote document to draw, + * transformed by |transform|. The rendered area will have the + * default background color |bgcolor|. |renderFlags| are the + * nsIPresShell::RenderDocument() flags to use on the remote side, + * and if true, |flushLayout| will do just that before rendering + * the document. The rendered image will be of size |renderSize|. + */ + async PDocumentRenderer(nsRect documentRect, Matrix transform, + nsString bgcolor, + uint32_t renderFlags, bool flushLayout, + IntSize renderSize); + + /** + * Sent by the chrome process when it no longer wants this remote + * <browser>. The child side cleans up in response, then + * finalizing its death by sending back __delete__() to the + * parent. + */ + async Destroy(); + + /** + * Update the child side docShell active (resource use) state. + * + * @param aIsActive + * Whether to activate or deactivate the docshell. + * @param aPreserveLayers + * Whether layer trees should be preserved for inactive docshells. + * @param aLayerObserverEpoch + * The layer observer epoch for this activation. This message should be + * ignored if this epoch has already been observed (via ForcePaint). + */ + async SetDocShellIsActive(bool aIsActive, bool aPreserveLayers, uint64_t aLayerObserverEpoch); + + /** + * Notify the child that it shouldn't paint the offscreen displayport. + * This is useful to speed up interactive operations over async + * scrolling performance like resize, tabswitch, pageload. + * + * Each enable call must be matched with a disable call. The child + * will remain in the suppress mode as long as there's + * a single unmatched call. + */ + async SuppressDisplayport(bool aEnabled); + + /** + * Navigate by key (Tab/Shift+Tab/F6/Shift+f6). + */ + async NavigateByKey(bool aForward, bool aForDocumentNavigation); + + /** + * The parent (chrome thread) requests that the child inform it when + * the graphics objects are ready to display. + * @see PCompositor + * @see RemotePaintIsReady + */ + async RequestNotifyAfterRemotePaint(); + + /** + * Tell the child that the UI resolution changed for the containing + * window. + * To avoid some sync messages from child to parent, we also send the dpi + * and default scale with the notification. + * If we don't know the dpi and default scale, we just pass in a negative + * value (-1) but in the majority of the cases this saves us from two + * sync requests from the child to the parent. + */ + async UIResolutionChanged(float dpi, int32_t rounding, double scale); + + /** + * Tell the child that the system theme has changed, and that a repaint + * is necessary. + */ + async ThemeChanged(LookAndFeelInt[] lookAndFeelIntCache); + + /** + * Tell the browser that its frame loader has been swapped + * with another. + */ + async SwappedWithOtherRemoteLoader(IPCTabContext context); + + /** + * A potential accesskey was just pressed. Look for accesskey targets + * using the list of provided charCodes. + * + * @param event keyboard event + * @param isTrusted true if triggered by a trusted key event + * @param modifierMask indicates which accesskey modifiers are pressed + */ + async HandleAccessKey(WidgetKeyboardEvent event, + uint32_t[] charCodes, int32_t modifierMask); + + /** + * Propagate a refresh to the child process + */ + async AudioChannelChangeNotification(uint32_t aAudioChannel, + float aVolume, + bool aMuted); + + /** + * Tells the root child docShell whether or not to use + * global history. This is sent right after the PBrowser + * is bound to a frameloader element. + */ + async SetUseGlobalHistory(bool aUse); + + /** + * HandledWindowedPluginKeyEvent() is always called after posting a native + * key event with OnWindowedPluginKeyEvent(). + * + * @param aKeyEventData The key event which was posted to the parent + * process. + * @param aIsConsumed true if aKeyEventData is consumed in the + * parent process. Otherwise, false. + */ + async HandledWindowedPluginKeyEvent(NativeEventData aKeyEventData, + bool aIsConsumed); + + /** + * Tell the child to print the current page with the given settings. + * + * @param aOuterWindowID the ID of the outer window to print + * @param aPrintData the serialized settings to print with + */ + async Print(uint64_t aOuterWindowID, PrintData aPrintData); + + /** + * Update the child with the tab's current top-level native window handle. + * This is used by a11y objects who must expose their native window. + * + * @param aNewHandle The native window handle of the tab's top-level window. + */ + async UpdateNativeWindowHandle(uintptr_t aNewHandle); + + /** + * Called when the session history of this particular PBrowser has been + * attached to a grouped session history. + * + * @param aOffset The number of entries in the grouped session + * history before this session history object. + */ + async NotifyAttachGroupedSessionHistory(uint32_t aOffset); + + /** + * Notify that the session history associated to this PBrowser has become + * the active history in the grouped session history. + * + * @param aGlobalLength The up-to-date number of entries in the grouped + * session history. + * @param aTargetLocalIndex The target local index to navigate to. + */ + async NotifyPartialSessionHistoryActive(uint32_t aGlobalLength, + uint32_t aTargetLocalIndex); + + /** + * Notify that the session history asssociates to this PBrowser has become + * an inactive history in the grouped session history. + */ + async NotifyPartialSessionHistoryDeactive(); + + /** + * Tell the child that it is a fresh process created for a Large-Allocation + * load. + */ + async SetFreshProcess(); + +/* + * FIXME: write protocol! + +state LIVE: + send LoadURL goto LIVE; +//etc. + send Destroy goto DYING; + +state DYING: + discard send blah; +// etc. + recv __delete__; + */ +}; + +} +} diff --git a/dom/ipc/PBrowserOrId.ipdlh b/dom/ipc/PBrowserOrId.ipdlh new file mode 100644 index 000000000..8673f188a --- /dev/null +++ b/dom/ipc/PBrowserOrId.ipdlh @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 ft=c: */ +/* 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 protocol PBrowser; + +using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h"; + +namespace mozilla { +namespace dom { + +union PBrowserOrId +{ + nullable PBrowser; + TabId; +}; + +} // namespace dom +} // namespace mozilla
\ No newline at end of file diff --git a/dom/ipc/PColorPicker.ipdl b/dom/ipc/PColorPicker.ipdl new file mode 100644 index 000000000..0e54ad598 --- /dev/null +++ b/dom/ipc/PColorPicker.ipdl @@ -0,0 +1,27 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */ + +/* 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 protocol PBrowser; + +namespace mozilla { +namespace dom { + +protocol PColorPicker +{ + manager PBrowser; + +parent: + async Open(); + +child: + async Update(nsString color); + + async __delete__(nsString color); +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl new file mode 100644 index 000000000..d436c19fe --- /dev/null +++ b/dom/ipc/PContent.ipdl @@ -0,0 +1,1063 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */ +/* 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 protocol PBackground; +include protocol PBlob; +include protocol PBrowser; +include protocol PCompositorBridge; +include protocol PContentBridge; +include protocol PContentPermissionRequest; +include protocol PCycleCollectWithLogs; +include protocol PCrashReporter; +include protocol PPSMContentDownloader; +include protocol PExternalHelperApp; +include protocol PHandlerService; +include protocol PFileDescriptorSet; +include protocol PHal; +include protocol PHeapSnapshotTempFileHelper; +include protocol PProcessHangMonitor; +include protocol PImageBridge; +include protocol PMedia; +include protocol PMemoryReportRequest; +include protocol PNecko; +// FIXME This is pretty ridiculous, but we have to keep the order of the +// following 4 includes, or the parser is confused about PGMPContent +// bridging PContent and PGMP. As soon as it registers the bridge between +// PContent and PPluginModule it seems to think that PContent's parent and +// child live in the same process! +include protocol PGMPContent; +include protocol PGMPService; +include protocol PPluginModule; +include protocol PGMP; +include protocol PPrinting; +include protocol PSendStream; +include protocol POfflineCacheUpdate; +include protocol PRenderFrame; +include protocol PScreenManager; +include protocol PSpeechSynthesis; +include protocol PStorage; +include protocol PTestShell; +include protocol PJavaScript; +include protocol PRemoteSpellcheckEngine; +include protocol PWebBrowserPersistDocument; +include protocol PWebrtcGlobal; +include protocol PPresentation; +include protocol PVRManager; +include protocol PVideoDecoderManager; +include protocol PFlyWebPublishedServer; +include DOMTypes; +include JavaScriptTypes; +include InputStreamParams; +include PTabContext; +include URIParams; +include PluginTypes; +include ProtocolTypes; +include PBackgroundSharedTypes; +include PContentPermission; +include ServiceWorkerConfiguration; +include GraphicsMessages; +include ProfilerTypes; + +// Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp +// are put into different UnifiedProtocolsXX.cpp files. +// XXX Remove this once bug 1069073 is fixed +include "mozilla/dom/PContentBridgeParent.h"; + +using GeoPosition from "nsGeoPositionIPCSerialiser.h"; +using AlertNotificationType from "mozilla/AlertNotificationIPCSerializer.h"; + +using struct ChromePackage from "mozilla/chrome/RegistryMessageUtils.h"; +using struct SubstitutionMapping from "mozilla/chrome/RegistryMessageUtils.h"; +using struct OverrideMapping from "mozilla/chrome/RegistryMessageUtils.h"; +using base::ChildPrivileges from "base/process_util.h"; +using base::ProcessId from "base/process.h"; +using struct IPC::Permission from "mozilla/net/NeckoMessageUtils.h"; +using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h"; +using struct mozilla::null_t from "ipc/IPCMessageUtils.h"; +using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; +using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h"; +using mozilla::hal::ProcessPriority from "mozilla/HalTypes.h"; +using mozilla::gfx::IntSize from "mozilla/gfx/2D.h"; +using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h"; +using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h"; +using mozilla::LayoutDeviceIntPoint from "Units.h"; +using struct LookAndFeelInt from "mozilla/widget/WidgetMessageUtils.h"; +using class mozilla::dom::MessagePort from "mozilla/dom/MessagePort.h"; +using class mozilla::dom::ipc::StructuredCloneData from "mozilla/dom/ipc/StructuredCloneData.h"; +using mozilla::DataStorageType from "ipc/DataStorageIPCUtils.h"; +using mozilla::DocShellOriginAttributes from "mozilla/ipc/BackgroundUtils.h"; +using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h"; +using struct mozilla::dom::FlyWebPublishOptions from "mozilla/dom/FlyWebPublishOptionsIPCSerializer.h"; +using mozilla::Telemetry::Accumulation from "mozilla/TelemetryComms.h"; +using mozilla::Telemetry::KeyedAccumulation from "mozilla/TelemetryComms.h"; + +union ChromeRegistryItem +{ + ChromePackage; + OverrideMapping; + SubstitutionMapping; +}; + +namespace mozilla { +namespace dom { + +struct FontListEntry { + nsString familyName; + nsString faceName; + nsCString filepath; + uint16_t weight; + int16_t stretch; + uint8_t italic; + uint8_t index; + bool isHidden; +}; + +union PrefValue { + nsCString; + int32_t; + bool; +}; + +union MaybePrefValue { + PrefValue; + null_t; +}; + +struct PrefSetting { + nsCString name; + MaybePrefValue defaultValue; + MaybePrefValue userValue; +}; + +struct DataStorageItem { + nsCString key; + nsCString value; + DataStorageType type; +}; + +// Note: Any changes to this structure should also be changed in +// FileSystemUpdate below. +struct VolumeInfo { + nsString name; + nsString mountPoint; + int32_t volState; + int32_t mountGeneration; + bool isMediaPresent; + bool isSharing; + bool isFormatting; + bool isFake; + bool isUnmounting; + bool isRemovable; + bool isHotSwappable; +}; + +struct ClipboardCapabilities { + bool supportsSelectionClipboard; + bool supportsFindClipboard; +}; + +union MaybeFileDesc { + FileDescriptor; + void_t; +}; + +union FileDescOrError { + FileDescriptor; + nsresult; +}; + +struct DomainPolicyClone +{ + bool active; + URIParams[] blacklist; + URIParams[] whitelist; + URIParams[] superBlacklist; + URIParams[] superWhitelist; +}; + + + +struct FrameScriptInfo +{ + nsString url; + bool runInGlobalScope; +}; + +struct AndroidSystemInfo +{ + nsString device; + nsString manufacturer; + nsString release_version; + nsString hardware; + uint32_t sdk_version; + bool isTablet; +}; + +struct GetFilesResponseSuccess +{ + PBlob[] blobs; +}; + +struct GetFilesResponseFailure +{ + nsresult errorCode; +}; + +union GetFilesResponseResult +{ + GetFilesResponseSuccess; + GetFilesResponseFailure; +}; + +struct BlobURLRegistrationData +{ + nsCString url; + PBlob blob; + Principal principal; +}; + +struct GMPAPITags +{ + nsCString api; + nsCString[] tags; +}; + +struct GMPCapabilityData +{ + nsCString name; + nsCString version; + GMPAPITags[] capabilities; +}; + +nested(upto inside_cpow) sync protocol PContent +{ + parent spawns PPluginModule; + + parent opens PProcessHangMonitor; + parent opens PGMPService; + child opens PBackground; + + manages PBlob; + manages PBrowser; + manages PContentPermissionRequest; + manages PCrashReporter; + manages PCycleCollectWithLogs; + manages PPSMContentDownloader; + manages PExternalHelperApp; + manages PFileDescriptorSet; + manages PHal; + manages PHandlerService; + manages PHeapSnapshotTempFileHelper; + manages PMedia; + manages PMemoryReportRequest; + manages PNecko; + manages POfflineCacheUpdate; + manages PPrinting; + manages PSendStream; + manages PScreenManager; + manages PSpeechSynthesis; + manages PStorage; + manages PTestShell; + manages PJavaScript; + manages PRemoteSpellcheckEngine; + manages PWebBrowserPersistDocument; + manages PWebrtcGlobal; + manages PPresentation; + manages PFlyWebPublishedServer; + +both: + // Depending on exactly how the new browser is being created, it might be + // created from either the child or parent process! + // + // The child creates the PBrowser as part of + // TabChild::BrowserFrameProvideWindow (which happens when the child's + // content calls window.open()), and the parent creates the PBrowser as part + // of ContentParent::CreateBrowserOrApp. + // + // When the parent constructs a PBrowser, the child trusts the app token and + // other attributes it receives from the parent. In that case, the + // context should be FrameIPCTabContext. + // + // When the child constructs a PBrowser, the parent doesn't trust the app + // token it receives from the child. In this case, context must have type + // PopupIPCTabContext. The browser created using a PopupIPCTabContext has + // the opener PBrowser's app-id and containing-app-id. The parent checks + // that if the opener is a browser element, the context is also for a + // browser element. + // + // This allows the parent to prevent a malicious child from escalating its + // privileges by requesting a PBrowser corresponding to a highly-privileged + // app; the child can only request privileges for an app which the child has + // access to (in the form of a TabChild). + // + // Keep the last 3 attributes in sync with GetProcessAttributes! + async PBrowser(TabId tabId, IPCTabContext context, uint32_t chromeFlags, + ContentParentId cpId, bool isForApp, bool isForBrowser); + + async PBlob(BlobConstructorParams params); + + async PFileDescriptorSet(FileDescriptor fd); + + // For parent->child, aBrowser must be non-null; aOuterWindowID can + // be 0 to indicate the browser's current root document, or nonzero + // to persist a subdocument. For child->parent, arguments are + // ignored and should be null/zero. + async PWebBrowserPersistDocument(nullable PBrowser aBrowser, + uint64_t aOuterWindowID); + +child: + // Give the content process its endpoints to the compositor. + async InitRendering( + Endpoint<PCompositorBridgeChild> compositor, + Endpoint<PImageBridgeChild> imageBridge, + Endpoint<PVRManagerChild> vr, + Endpoint<PVideoDecoderManagerChild> video); + + // Re-create the rendering stack using the given endpoints. This is sent + // after the compositor process has crashed. The new endpoints may be to a + // newly launched GPU process, or the compositor thread of the UI process. + async ReinitRendering( + Endpoint<PCompositorBridgeChild> compositor, + Endpoint<PImageBridgeChild> bridge, + Endpoint<PVRManagerChild> vr, + Endpoint<PVideoDecoderManagerChild> video); + + /** + * Enable system-level sandboxing features, if available. Can + * usually only be performed zero or one times. The child may + * abnormally exit if this fails; the details are OS-specific. + */ + async SetProcessSandbox(MaybeFileDesc aBroker); + + async PMemoryReportRequest(uint32_t generation, bool anonymize, + bool minimizeMemoryUsage, MaybeFileDesc DMDFile); + + /** + * Sent to notify that aTabId has been allocated aLayersId + */ + async NotifyLayerAllocated(TabId aTabId, uint64_t aLayersId); + + async SpeakerManagerNotify(); + + /** + * Communication between the PuppetBidiKeyboard and the actual + * BidiKeyboard hosted by the parent + */ + async BidiKeyboardNotify(bool isLangRTL, bool haveBidiKeyboards); + + /** + * Dump this process's GC and CC logs to the provided files. + * + * For documentation on the other args, see dumpGCAndCCLogsToFile in + * nsIMemoryInfoDumper.idl + */ + async PCycleCollectWithLogs(bool dumpAllTraces, + FileDescriptor gcLog, + FileDescriptor ccLog); + + async PTestShell(); + + async RegisterChrome(ChromePackage[] packages, SubstitutionMapping[] substitutions, + OverrideMapping[] overrides, nsCString locale, bool reset); + async RegisterChromeItem(ChromeRegistryItem item); + + async ClearImageCache(bool privateLoader, bool chrome); + + async SetOffline(bool offline); + async SetConnectivity(bool connectivity); + async SetCaptivePortalState(int32_t aState); + + async NotifyVisited(URIParams uri); + + async PreferenceUpdate(PrefSetting pref); + async VarUpdate(GfxVarUpdate var); + + async DataStoragePut(nsString aFilename, DataStorageItem aItem); + async DataStorageRemove(nsString aFilename, nsCString aKey, DataStorageType aType); + async DataStorageClear(nsString aFilename); + + async NotifyAlertsObserver(nsCString topic, nsString data); + + async GeolocationUpdate(GeoPosition somewhere); + + async GeolocationError(uint16_t errorCode); + + async UpdateDictionaryList(nsString[] dictionaries); + + // nsIPermissionManager messages + async AddPermission(Permission permission); + + async Volumes(VolumeInfo[] volumes); + + async FlushMemory(nsString reason); + + async GarbageCollect(); + async CycleCollect(); + + /** + * Start accessibility engine in content process. + * @param aMsaaID is an a11y-specific unique id for the content process + * that is generated by the chrome process. Only used on + * Windows; pass 0 on other platforms. + */ + async ActivateA11y(uint32_t aMsaaID); + + /** + * Shutdown accessibility engine in content process (if not in use). + */ + async ShutdownA11y(); + + async AppInfo(nsCString version, nsCString buildID, nsCString name, nsCString UAName, + nsCString ID, nsCString vendor); + async AppInit(); + + /** + * Send ServiceWorkerRegistrationData to child process. + */ + async InitServiceWorkers(ServiceWorkerConfiguration aConfig); + + /** + * Send BlobURLRegistrationData to child process. + */ + async InitBlobURLs(BlobURLRegistrationData[] registrations); + + // Notify child that last-pb-context-exited notification was observed + async LastPrivateDocShellDestroyed(); + + // Note: Any changes to this structure should also be changed in + // VolumeInfo above. + async FileSystemUpdate(nsString fsName, nsString mountPoint, int32_t fsState, + int32_t mountGeneration, bool isMediaPresent, + bool isSharing, bool isFormatting, bool isFake, + bool isUnmounting, bool isRemovable, bool isHotSwappable); + + // Notify volume is removed. + async VolumeRemoved(nsString fsName); + + async NotifyProcessPriorityChanged(ProcessPriority priority); + async MinimizeMemoryUsage(); + + /** + * Used to manage nsIStyleSheetService across processes. + */ + async LoadAndRegisterSheet(URIParams uri, uint32_t type); + async UnregisterSheet(URIParams uri, uint32_t type); + + async NotifyPhoneStateChange(nsString newState); + + /** + * Notify idle observers in the child + */ + async NotifyIdleObserver(uint64_t observerId, nsCString topic, nsString str); + + /** + * Called during plugin initialization to map a plugin id to a child process + * id. + */ + async AssociatePluginId(uint32_t aPluginId, ProcessId aProcessId); + + /** + * This call is used by async plugin initialization to notify the + * PluginModuleContentParent that the PluginModuleChromeParent's async + * init has completed. + */ + async LoadPluginResult(uint32_t aPluginId, bool aResult); + + /** + * Control the Gecko Profiler in the child process. + */ + async StartProfiler(ProfilerInitParams params); + async StopProfiler(); + async PauseProfiler(bool aPause); + + async GatherProfile(); + + async InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action); + + async EndDragSession(bool aDoneDrag, bool aUserCancelled, + LayoutDeviceIntPoint aDragEndPoint); + + async DomainSetChanged(uint32_t aSetType, uint32_t aChangeType, OptionalURIParams aDomain); + + /** + * Notify the child to shutdown. The child will in turn call FinishShutdown + * and let the parent close the channel. + */ + async Shutdown(); + + async LoadProcessScript(nsString url); + + /** + * Requests a full native update of a native plugin child window. This is + * a Windows specific call. + */ + async UpdateWindow(uintptr_t aChildId); + + /** + * Notify the child that presentation receiver has been launched with the + * correspondent iframe. + */ + async NotifyPresentationReceiverLaunched(PBrowser aIframe, nsString aSessionId); + + /** + * Notify the child that the info about a presentation receiver needs to be + * cleaned up. + */ + async NotifyPresentationReceiverCleanUp(nsString aSessionId); + + /** + * Notify the child that cache is emptied. + */ + async NotifyEmptyHTTPCache(); + + /** + * Send a `push` event without data to a service worker in the child. + */ + async Push(nsCString scope, Principal principal, nsString messageId); + + /** + * Send a `push` event with data to a service worker in the child. + */ + async PushWithData(nsCString scope, Principal principal, + nsString messageId, uint8_t[] data); + + /** + * Send a `pushsubscriptionchange` event to a service worker in the child. + */ + async PushSubscriptionChange(nsCString scope, Principal principal); + + /** + * Send a Push error message to all service worker clients in the child. + */ + async PushError(nsCString scope, Principal principal, nsString message, + uint32_t flags); + + /** + * Windows specific: associate this content process with the browsers + * audio session. + */ + async SetAudioSessionData(nsID aID, + nsString aDisplayName, + nsString aIconPath); + + async GetFilesResponse(nsID aID, GetFilesResponseResult aResult); + + async BlobURLRegistration(nsCString aURI, PBlob aBlob, + Principal aPrincipal); + + async BlobURLUnregistration(nsCString aURI); + + + async GMPsChanged(GMPCapabilityData[] capabilities); + +parent: + /** + * Tell the content process some attributes of itself. This is + * among the first information queried by content processes after + * startup. (The message is sync to allow the content process to + * control when it receives the information.) + * + * |id| is a unique ID among all subprocesses. When |isForApp && + * isForBrowser|, we're loading <browser> for an app. When + * |isForBrowser|, we're loading <browser>. When |!isForApp && + * !isForBrowser|, we're probably loading <xul:browser remote>. + * + * Keep the return values in sync with PBrowser()! + */ + sync GetProcessAttributes() + returns (ContentParentId cpId, bool isForApp, bool isForBrowser); + sync GetXPCOMProcessAttributes() + returns (bool isOffline, bool isConnected, int32_t captivePortalState, + bool isLangRTL, + bool haveBidiKeyboards, nsString[] dictionaries, + ClipboardCapabilities clipboardCaps, + DomainPolicyClone domainPolicy, + StructuredCloneData initialData, + OptionalURIParams userContentSheetURL); + + sync CreateChildProcess(IPCTabContext context, + ProcessPriority priority, + TabId openerTabId) + returns (ContentParentId cpId, bool isForApp, bool isForBrowser, TabId tabId); + sync BridgeToChildProcess(ContentParentId cpId); + + async CreateGMPService(); + + /** + * This call connects the content process to a plugin process. While this + * call runs, a new PluginModuleParent will be created in the ContentChild + * via bridging. The corresponding PluginModuleChild will live in the plugin + * process. + */ + sync LoadPlugin(uint32_t aPluginId) returns (nsresult aResult, uint32_t aRunID); + + /** + * This call is used by asynchronous plugin instantiation to notify the + * content parent that it is now safe to initiate the plugin bridge for + * the specified plugin id. When this call returns, the requested bridge + * connection has been made. + */ + sync ConnectPluginBridge(uint32_t aPluginId) returns (nsresult rv); + + /** + * Return the current blocklist state for a particular plugin. + */ + sync GetBlocklistState(uint32_t aPluginId) returns (uint32_t aState); + + /** + * This call returns the set of plugins loaded in the chrome + * process. However, in many cases this set will not have changed since the + * last FindPlugins message. Consequently, the chrome process increments an + * epoch number every time the set of plugins changes. The content process + * sends up the last epoch it observed. If the epochs are the same, the + * chrome process returns no plugins. Otherwise it returns a complete list. + * + * |pluginEpoch| is the epoch last observed by the content + * process. |newPluginEpoch| is the current epoch in the chrome process. If + * |pluginEpoch == newPluginEpoch|, then |plugins| will be left empty. + */ + sync FindPlugins(uint32_t pluginEpoch) returns (nsresult aResult, PluginTag[] plugins, uint32_t newPluginEpoch); + + async PJavaScript(); + + async PRemoteSpellcheckEngine(); + + sync PCrashReporter(NativeThreadId tid, uint32_t processType); + + /** + * Is this token compatible with the provided version? + * + * |version| The offered version to test + * Returns |True| if the offered version is compatible + */ + sync NSSU2FTokenIsCompatibleVersion(nsString version) + returns (bool result); + + /** + * Return whether the provided KeyHandle belongs to this Token + * + * |keyHandle| Key Handle to evaluate. + * Returns |True| if the Key Handle is ours. + */ + sync NSSU2FTokenIsRegistered(uint8_t[] keyHandle) + returns (bool isValidKeyHandle); + + /** + * Generates a public/private keypair for the provided application + * and challenge, returning the pubkey, challenge response, and + * key handle in the registration data. + * + * |application| The FIDO Application data to associate with the key. + * |challenge| The Challenge to satisfy in the response. + * |registration| An array containing the pubkey, challenge response, + * and key handle. + */ + sync NSSU2FTokenRegister(uint8_t[] application, uint8_t[] challenge) + returns (uint8_t[] registration); + + /** + * Creates a signature over the "param" arguments using the private key + * provided in the key handle argument. + * + * |application| The FIDO Application data to associate with the key. + * |challenge| The Challenge to satisfy in the response. + * |keyHandle| The Key Handle opaque object to use. + * |signature| The resulting signature. + */ + sync NSSU2FTokenSign(uint8_t[] application, uint8_t[] challenge, + uint8_t[] keyHandle) + returns (uint8_t[] signature); + + sync IsSecureURI(uint32_t type, URIParams uri, uint32_t flags) + returns (bool isSecureURI); + + async AccumulateMixedContentHSTS(URIParams uri, bool active, bool hasHSTSPriming); + + sync GetLookAndFeelCache() + returns (LookAndFeelInt[] lookAndFeelIntCache); + + nested(inside_cpow) async PHal(); + + async PHeapSnapshotTempFileHelper(); + + async PNecko(); + + async PPrinting(); + + async PSendStream(); + + nested(inside_sync) sync PScreenManager() + returns (uint32_t numberOfScreens, + float systemDefaultScale, + bool success); + + async PSpeechSynthesis(); + + nested(inside_cpow) async PStorage(); + + async PMedia(); + + async PWebrtcGlobal(); + + async PPresentation(); + + async PFlyWebPublishedServer(nsString name, FlyWebPublishOptions params); + + // Services remoting + + async StartVisitedQuery(URIParams uri); + async VisitURI(URIParams uri, OptionalURIParams referrer, uint32_t flags); + async SetURITitle(URIParams uri, nsString title); + + async LoadURIExternal(URIParams uri, PBrowser windowContext); + async ExtProtocolChannelConnectParent(uint32_t registrarId); + + // PrefService message + sync ReadPrefsArray() returns (PrefSetting[] prefs) verify; + sync GetGfxVars() returns (GfxVarUpdate[] vars); + + sync ReadFontList() returns (FontListEntry[] retValue); + + sync ReadDataStorageArray(nsString aFilename) + returns (DataStorageItem[] retValue); + + sync SyncMessage(nsString aMessage, ClonedMessageData aData, + CpowEntry[] aCpows, Principal aPrincipal) + returns (StructuredCloneData[] retval); + + nested(inside_sync) sync RpcMessage(nsString aMessage, ClonedMessageData aData, + CpowEntry[] aCpows, Principal aPrincipal) + returns (StructuredCloneData[] retval); + + async ShowAlert(AlertNotificationType alert); + + async CloseAlert(nsString name, Principal principal); + + async DisableNotifications(Principal principal); + + async OpenNotificationSettings(Principal principal); + + async PPSMContentDownloader(uint32_t aCertType); + + async PExternalHelperApp(OptionalURIParams uri, + nsCString aMimeContentType, + nsCString aContentDisposition, + uint32_t aContentDispositionHint, + nsString aContentDispositionFilename, + bool aForceSave, + int64_t aContentLength, + bool aWasFileChannel, + OptionalURIParams aReferrer, + nullable PBrowser aBrowser); + + async PHandlerService(); + + async AddGeolocationListener(Principal principal, bool highAccuracy); + async RemoveGeolocationListener(); + async SetGeolocationHigherAccuracy(bool enable); + + async ConsoleMessage(nsString message); + async ScriptError(nsString message, nsString sourceName, nsString sourceLine, + uint32_t lineNumber, uint32_t colNumber, uint32_t flags, + nsCString category); + + // nsIPermissionManager messages + sync ReadPermissions() returns (Permission[] permissions); + + // Places the items within dataTransfer on the clipboard. + async SetClipboard(IPCDataTransfer aDataTransfer, + bool aIsPrivateData, + Principal aRequestingPrincipal, + int32_t aWhichClipboard); + + // Given a list of supported types, returns the clipboard data for the + // first type that matches. + sync GetClipboard(nsCString[] aTypes, int32_t aWhichClipboard) + returns (IPCDataTransfer dataTransfer); + + // Clears the clipboard. + async EmptyClipboard(int32_t aWhichClipboard); + + // Returns true if data of one of the specified types is on the clipboard. + sync ClipboardHasType(nsCString[] aTypes, int32_t aWhichClipboard) + returns (bool hasType); + + sync GetSystemColors(uint32_t colorsCount) + returns (uint32_t[] colors); + + sync GetIconForExtension(nsCString aFileExt, uint32_t aIconSize) + returns (uint8_t[] bits); + + sync GetShowPasswordSetting() + returns (bool showPassword); + + // Notify the parent of the presence or absence of private docshells + async PrivateDocShellsExist(bool aExist); + + // Tell the parent that the child has gone idle for the first time + async FirstIdle(); + + async AudioChannelServiceStatus(bool aActiveTelephonyChannel, + bool aContentOrNormalChannel, + bool aAnyActiveChannel); + + async AudioChannelChangeDefVolChannel(int32_t aChannel, bool aHidden); + + // called by the child (test code only) to propagate volume changes to the parent + async CreateFakeVolume(nsString fsName, nsString mountPoint); + async SetFakeVolumeState(nsString fsName, int32_t fsState); + async RemoveFakeVolume(nsString fsName); + + sync KeywordToURI(nsCString keyword) + returns (nsString providerName, OptionalInputStreamParams postData, OptionalURIParams uri); + + sync NotifyKeywordSearchLoading(nsString providerName, nsString keyword); + + async CopyFavicon(URIParams oldURI, URIParams newURI, Principal aLoadingPrincipal, bool isPrivate); + + // Tell the compositor to allocate a layer tree id for nested remote mozbrowsers. + sync AllocateLayerTreeId(ContentParentId cpId, TabId tabId) + returns (uint64_t id); + async DeallocateLayerTreeId(uint64_t id); + + sync SpeakerManagerForceSpeaker(bool aEnable); + + sync SpeakerManagerGetSpeakerStatus() + returns (bool value); + + /** + * Notifies the parent about a recording device is starting or shutdown. + * @param recordingStatus starting or shutdown + * @param pageURL URL that request that changing the recording status + * @param isAudio recording start with microphone + * @param isVideo recording start with camera + */ + async RecordingDeviceEvents(nsString recordingStatus, + nsString pageURL, + bool isAudio, + bool isVideo); + + sync GetGraphicsFeatureStatus(int32_t aFeature) returns (int32_t aStatus, nsCString aFailureCode, + bool aSuccess); + + // Graphics errors + async GraphicsError(nsCString aError); + + // Driver crash guards. aGuardType must be a member of CrashGuardType. + sync BeginDriverCrashGuard(uint32_t aGuardType) returns (bool crashDetected); + sync EndDriverCrashGuard(uint32_t aGuardType); + + async AddIdleObserver(uint64_t observerId, uint32_t idleTimeInS); + async RemoveIdleObserver(uint64_t observerId, uint32_t idleTimeInS); + + /** + * This message is only used on X11 platforms. + * + * Send a dup of the plugin process's X socket to the parent + * process. In theory, this scheme keeps the plugin's X resources + * around until after both the plugin process shuts down *and* the + * parent process closes the dup fd. This is used to prevent the + * parent process from crashing on X errors if, e.g., the plugin + * crashes *just before* a repaint and the parent process tries to + * use the newly-invalid surface. + */ + async BackUpXResources(FileDescriptor aXSocketFd); + + sync OpenAnonymousTemporaryFile() returns (FileDescOrError aFD); + + /** + * Keygen requires us to call it after a <keygen> element is parsed and + * before one is submitted. This is urgent because an extension might use + * a CPOW to synchronously submit a keygen element. + */ + nested(inside_cpow) sync KeygenProcessValue(nsString oldValue, + nsString challenge, + nsString keytype, + nsString keyparams) + returns (nsString newValue); + + /** + * Called to provide the options for <keygen> elements. + */ + sync KeygenProvideContent() + returns (nsString aAttribute, nsString[] aContent); + + /** + * Tell the chrome process there is an creation of PBrowser. + * return a system-wise unique Id. + */ + sync AllocateTabId(TabId openerTabId, IPCTabContext context, ContentParentId cpId) + returns (TabId tabId); + async DeallocateTabId(TabId tabId, + ContentParentId cpId, + bool aMarkedDestroying); + + /** + * Tell the chrome process there is a destruction of PBrowser(Tab) + */ + async NotifyTabDestroying(TabId tabId, + ContentParentId cpId); + /** + * Starts an offline application cache update. + * @param manifestURI + * URI of the manifest to fetch, the application cache group ID + * @param documentURI + * URI of the document that referred the manifest + * @param loadingPrincipal + * Principal of the document that referred the manifest + * @param stickDocument + * True if the update was initiated by a document load that referred + * a manifest. + * False if the update was initiated by applicationCache.update() call. + * + * Tells the update to carry the documentURI to a potential separate + * update of implicit (master) items. + * + * Why this argument? If the document was not found in an offline cache + * before load and refers a manifest and this manifest itself has not + * been changed since the last fetch, we will not do the application + * cache group update. But we must cache the document (identified by the + * documentURI). This argument will ensure that a previously uncached + * document will get cached and that we don't re-cache a document that + * has already been cached (stickDocument=false). + * @param tabId + * To identify which tab owns the app. + */ + async POfflineCacheUpdate(URIParams manifestURI, URIParams documentURI, + PrincipalInfo loadingPrincipal, bool stickDocument); + + /** + * Sets "offline-app" permission for the principal. Called when we hit + * a web app with the manifest attribute in <html> and + * offline-apps.allow_by_default is set to true. + */ + async SetOfflinePermission(Principal principal); + + /** + * Notifies the parent to continue shutting down after the child performs + * its shutdown tasks. + */ + async FinishShutdown(); + + async UpdateDropEffect(uint32_t aDragAction, uint32_t aDropEffect); + + /** + * Initiates an asynchronous request for permission for the + * provided principal. + * + * @param aRequests + * The array of permissions to request. + * @param aPrincipal + * The principal of the request. + * @param tabId + * To identify which tab issues this request. + * + * NOTE: The principal is untrusted in the parent process. Only + * principals that can live in the content process should + * provided. + */ + async PContentPermissionRequest(PermissionRequest[] aRequests, Principal aPrincipal, + TabId tabId); + + async Profile(nsCString aProfile); + + /** + * Request graphics initialization information from the parent. + */ + sync GetGraphicsDeviceInitData() + returns (ContentDeviceData aData); + + sync CreateWindow(nullable PBrowser aThisTab, + PBrowser aNewTab, + PRenderFrame aRenderFrame, + uint32_t aChromeFlags, + bool aCalledFromJS, + bool aPositionSpecified, + bool aSizeSpecified, + nsCString aFeatures, + nsCString aBaseURI, + DocShellOriginAttributes aOpenerOriginAttributes, + float aFullZoom) + returns (nsresult rv, + bool windowOpened, + FrameScriptInfo[] frameScripts, + nsCString urlToLoad, + TextureFactoryIdentifier textureFactoryIdentifier, + uint64_t layersId); + + sync GetAndroidSystemInfo() + returns (AndroidSystemInfo info); + + /** + * Tells the parent to ungrab the pointer on the default display. + * + * This is for GTK platforms where we have to ensure the pointer ungrab happens in the + * chrome process as that's the process that receives the pointer event. + */ + sync UngrabPointer(uint32_t time); + + sync RemovePermission(Principal principal, nsCString permissionType) returns (nsresult rv); + + /** + * Tell the parent that a decoder's' benchmark has been completed. + * The result can then be stored in permanent storage. + */ + async NotifyBenchmarkResult(nsString aCodecName, uint32_t aDecodeFPS); + + /** + * Notify `push-message` observers without data in the parent. + */ + async NotifyPushObservers(nsCString scope, Principal principal, + nsString messageId); + + /** + * Notify `push-message` observers with data in the parent. + */ + async NotifyPushObserversWithData(nsCString scope, Principal principal, + nsString messageId, uint8_t[] data); + + /** + * Notify `push-subscription-change` observers in the parent. + */ + async NotifyPushSubscriptionChangeObservers(nsCString scope, + Principal principal); + + /** + * Tell the parent process that the child process is low on memory. This + * allows the parent process to save a memory report that can potentially be + * sent with a crash report from the content process. + */ + async NotifyLowMemory(); + + async GetFilesRequest(nsID aID, nsString aDirectory, bool aRecursiveFlag); + async DeleteGetFilesRequest(nsID aID); + + async StoreAndBroadcastBlobURLRegistration(nsCString url, PBlob blob, + Principal principal); + + async UnstoreAndBroadcastBlobURLUnregistration(nsCString url); + + /** + * Messages for communicating child Telemetry to the parent process + */ + async AccumulateChildHistogram(Accumulation[] accumulations); + async AccumulateChildKeyedHistogram(KeyedAccumulation[] accumulations); + + sync GetA11yContentId() returns (uint32_t aContentId); + +both: + async AsyncMessage(nsString aMessage, CpowEntry[] aCpows, + Principal aPrincipal, ClonedMessageData aData); + + /** + * Notify `push-subscription-modified` observers in the parent and child. + */ + async NotifyPushSubscriptionModifiedObservers(nsCString scope, + Principal principal); +}; + +} +} diff --git a/dom/ipc/PContentBridge.ipdl b/dom/ipc/PContentBridge.ipdl new file mode 100644 index 000000000..72f839178 --- /dev/null +++ b/dom/ipc/PContentBridge.ipdl @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=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 protocol PBlob; +include protocol PBrowser; +include protocol PContent; +include protocol PJavaScript; +include protocol PFileDescriptorSet; +include protocol PSendStream; + +include DOMTypes; +include JavaScriptTypes; +include PTabContext; + +using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h"; +using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h"; +using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h"; +using class mozilla::dom::MessagePort from "mozilla/dom/MessagePort.h"; +using class mozilla::dom::ipc::StructuredCloneData from "mozilla/dom/ipc/StructuredCloneData.h"; + +namespace mozilla { +namespace dom { + +/* + * PContentBridge allows us to represent a parent/child relationship between two + * child processes. When a child process wants to open its own child, it asks + * the root process to create a new process and then bridge them. The first + * child will allocate the PContentBridgeParent, and the newly opened child will + * allocate the PContentBridgeChild. This protocol allows these processes to + * share PBrowsers and send messages to each other. + */ +nested(upto inside_cpow) sync protocol PContentBridge +{ + bridges PContent, PContent; + + manages PBlob; + manages PBrowser; + manages PFileDescriptorSet; + manages PJavaScript; + manages PSendStream; + +parent: + sync SyncMessage(nsString aMessage, ClonedMessageData aData, + CpowEntry[] aCpows, Principal aPrincipal) + returns (StructuredCloneData[] retval); + + async PJavaScript(); + + async PSendStream(); + +both: + // Both the parent and the child can construct the PBrowser. + // See the comment in PContent::PBrowser(). + async PBrowser(TabId tabId, IPCTabContext context, uint32_t chromeFlags, + ContentParentId cpId, bool isForApp, bool isForBrowser); + + async PBlob(BlobConstructorParams params); + + async PFileDescriptorSet(FileDescriptor fd); + + async AsyncMessage(nsString aMessage, CpowEntry[] aCpows, + Principal aPrincipal, ClonedMessageData aData); +}; + +} +} diff --git a/dom/ipc/PContentPermission.ipdlh b/dom/ipc/PContentPermission.ipdlh new file mode 100644 index 000000000..1909c0ef8 --- /dev/null +++ b/dom/ipc/PContentPermission.ipdlh @@ -0,0 +1,20 @@ +/* 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/. */ + +namespace mozilla { +namespace dom { + +struct PermissionRequest { + nsCString type; + nsCString access; + nsString[] options; +}; + +struct PermissionChoice { + nsCString type; + nsString choice; +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/PContentPermissionRequest.ipdl b/dom/ipc/PContentPermissionRequest.ipdl new file mode 100644 index 000000000..8eb827862 --- /dev/null +++ b/dom/ipc/PContentPermissionRequest.ipdl @@ -0,0 +1,28 @@ +/* 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 protocol PContent; +include PContentPermission; + +namespace mozilla { +namespace dom { + +protocol PContentPermissionRequest +{ + manager PContent; + +parent: + async prompt(); + async NotifyVisibility(bool visibility); + async Destroy(); + +child: + async GetVisibility(); + async NotifyResult(bool allow, PermissionChoice[] choices); + async __delete__(); +}; + + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/PCrashReporter.ipdl b/dom/ipc/PCrashReporter.ipdl new file mode 100644 index 000000000..8f965f2ee --- /dev/null +++ b/dom/ipc/PCrashReporter.ipdl @@ -0,0 +1,31 @@ +/* -*- 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 protocol PContent; +include protocol PPluginModule; +include protocol PGMP; + +namespace mozilla { +namespace dom { + +struct Mapping { + nsCString library_name; + nsCString file_id; + uintptr_t start_address; + size_t mapping_length; + size_t file_offset; +}; + +async protocol PCrashReporter { + manager PContent or PPluginModule or PGMP; +parent: + async AnnotateCrashReport(nsCString key, nsCString data); + async AppendAppNotes(nsCString data); + async __delete__(); +}; + +} +} diff --git a/dom/ipc/PCycleCollectWithLogs.ipdl b/dom/ipc/PCycleCollectWithLogs.ipdl new file mode 100644 index 000000000..f420c6dda --- /dev/null +++ b/dom/ipc/PCycleCollectWithLogs.ipdl @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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 protocol PContent; + +namespace mozilla { +namespace dom { + +protocol PCycleCollectWithLogs { + manager PContent; + +parent: + async CloseGCLog(); + async CloseCCLog(); + + async __delete__(); +}; + +} +} diff --git a/dom/ipc/PDatePicker.ipdl b/dom/ipc/PDatePicker.ipdl new file mode 100644 index 000000000..90a2654bb --- /dev/null +++ b/dom/ipc/PDatePicker.ipdl @@ -0,0 +1,27 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */ + +/* 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 protocol PBrowser; + +namespace mozilla { +namespace dom { + +protocol PDatePicker +{ + manager PBrowser; + +parent: + async Open(); + +child: + async Cancel(); + + async __delete__(nsString color); +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/PDocumentRenderer.ipdl b/dom/ipc/PDocumentRenderer.ipdl new file mode 100644 index 000000000..bdaed45d7 --- /dev/null +++ b/dom/ipc/PDocumentRenderer.ipdl @@ -0,0 +1,25 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* 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 protocol PBrowser; + +include "mozilla/GfxMessageUtils.h"; + +using nsIntSize from "nsSize.h"; + +namespace mozilla { +namespace ipc { + +protocol PDocumentRenderer +{ + manager PBrowser; + +parent: + // Returns the width and height, in pixels, of the returned ARGB32 data. + async __delete__(nsIntSize renderedSize, nsCString data); +}; + +} // namespace ipc +} // namespace mozilla diff --git a/dom/ipc/PFilePicker.ipdl b/dom/ipc/PFilePicker.ipdl new file mode 100644 index 000000000..234a055c6 --- /dev/null +++ b/dom/ipc/PFilePicker.ipdl @@ -0,0 +1,46 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */ +/* 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 protocol PBlob; +include protocol PBrowser; + +using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; + +namespace mozilla { +namespace dom { + +struct InputBlobs +{ + PBlob[] blobs; +}; + +struct InputDirectory +{ + nsString directoryPath; +}; + +union MaybeInputData +{ + InputBlobs; + InputDirectory; + void_t; +}; + +protocol PFilePicker +{ + manager PBrowser; + +parent: + async Open(int16_t selectedType, bool addToRecentDocs, nsString defaultFile, + nsString defaultExtension, nsString[] filters, nsString[] filterNames, + nsString displayDirectory, nsString okButtonLabel); + +child: + async __delete__(MaybeInputData data, int16_t result); +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/PMemoryReportRequest.ipdl b/dom/ipc/PMemoryReportRequest.ipdl new file mode 100644 index 000000000..092d44250 --- /dev/null +++ b/dom/ipc/PMemoryReportRequest.ipdl @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 protocol PContent; + +namespace mozilla { +namespace dom { + +struct MemoryReport { + nsCString process; + nsCString path; + int32_t kind; + int32_t units; + int64_t amount; + nsCString desc; +}; + +protocol PMemoryReportRequest { + manager PContent; + +parent: + async Report(MemoryReport aReport); + async __delete__(); +}; + +} +} diff --git a/dom/ipc/PPluginWidget.ipdl b/dom/ipc/PPluginWidget.ipdl new file mode 100644 index 000000000..d96ea4ed8 --- /dev/null +++ b/dom/ipc/PPluginWidget.ipdl @@ -0,0 +1,61 @@ +/* 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 protocol PBrowser; + +include "mozilla/GfxMessageUtils.h"; + +using nsIntRect from "nsRect.h"; + +namespace mozilla { +namespace plugins { + +/** + * PPluginWidget - a nsIWidget'ish protocol for windowed plugins in e10s. + * On windows and linux we create native widgets in chrome which we then manage + * from content. On the content side there's PluginWidgetProxy which + * implements nsIWidget. We hand this around layout and plugins code. Anything + * not dealt with via PluginWidgetProxy falls through to PuppetWidget. Native + * widget exists on the chrome side (PluginWidgetParent) attached to the + * browser window as a child. Window management calls are forwarded from + * PluginWidgetProxy to PluginWidgetParent over this interface. + * + * Note lifetime management for PluginWidgetProxy (the plugin widget) and the + * connection (PluginWidgetChild) are separated. PluginWidgetChild will + * be torn down first by the tab, followed by the deref'ing of the nsIWidget + * via layout. + */ +sync protocol PPluginWidget { + manager PBrowser; + +parent: + async __delete__(); + + /** + * Used to set the ID of a scroll capture container from the parent process, + * so that we can create a proxy container in the layer tree. + * @param aScrollCaptureId async container ID of the parent container + * @param aPluginInstanceId plugin ID on which to set the scroll capture ID + */ + sync Create() returns (nsresult aResult, uint64_t aScrollCaptureId, + uintptr_t aPluginInstanceId); + async SetFocus(bool aRaise); + + /** + * Returns NS_NATIVE_PLUGIN_PORT and its variants: a sharable native + * window for plugins. On Linux, this returns an XID for a socket widget + * embedded in the chrome side native window. On Windows this returns the + * native HWND of the plugin widget. + */ + sync GetNativePluginPort() returns (uintptr_t value); + + /** + * Sends an NS_NATIVE_CHILD_WINDOW to be adopted by the widget's native window + * on the chrome side. This is only currently used on Windows. + */ + sync SetNativeChildWindow(uintptr_t childWindow); +}; + +} +} diff --git a/dom/ipc/PProcessHangMonitor.ipdl b/dom/ipc/PProcessHangMonitor.ipdl new file mode 100644 index 000000000..07ee7a211 --- /dev/null +++ b/dom/ipc/PProcessHangMonitor.ipdl @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* 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/. */ + +using base::ProcessId from "base/process.h"; +using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h"; + +namespace mozilla { + +struct SlowScriptData +{ + TabId tabId; + nsCString filename; + uint32_t lineno; +}; + +struct PluginHangData +{ + uint32_t pluginId; + ProcessId contentProcessId; +}; + +union HangData +{ + SlowScriptData; + PluginHangData; +}; + +protocol PProcessHangMonitor +{ +parent: + async HangEvidence(HangData data); + async ClearHang(); + +child: + async TerminateScript(); + + async BeginStartingDebugger(); + async EndStartingDebugger(); + + async ForcePaint(TabId tabId, uint64_t aLayerObserverEpoch); +}; + +} // namespace mozilla diff --git a/dom/ipc/PScreenManager.ipdl b/dom/ipc/PScreenManager.ipdl new file mode 100644 index 000000000..a6ad26cfc --- /dev/null +++ b/dom/ipc/PScreenManager.ipdl @@ -0,0 +1,62 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* 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 protocol PBrowser; +include protocol PContent; + +include "mozilla/GfxMessageUtils.h"; + +using nsIntRect from "nsRect.h"; +using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h"; + +namespace mozilla { +namespace dom { + +struct ScreenDetails { + uint32_t id; + nsIntRect rect; + nsIntRect rectDisplayPix; + nsIntRect availRect; + nsIntRect availRectDisplayPix; + int32_t pixelDepth; + int32_t colorDepth; + double contentsScaleFactor; +}; + +nested(upto inside_cpow) sync protocol PScreenManager +{ + manager PContent; + +parent: + nested(inside_sync) sync Refresh() + returns (uint32_t numberOfScreens, + float systemDefaultScale, + bool success); + + nested(inside_cpow) sync ScreenRefresh(uint32_t aId) + returns (ScreenDetails screen, + bool success); + + nested(inside_sync) sync GetPrimaryScreen() + returns (ScreenDetails screen, + bool success); + + nested(inside_sync) sync ScreenForRect(int32_t aLeft, + int32_t aTop, + int32_t aWidth, + int32_t aHeight) + returns (ScreenDetails screen, + bool success); + + nested(inside_cpow) sync ScreenForBrowser(TabId aTabId) + returns (ScreenDetails screen, + bool success); + +child: + async __delete__(); +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/PTabContext.ipdlh b/dom/ipc/PTabContext.ipdlh new file mode 100644 index 000000000..507ef59d9 --- /dev/null +++ b/dom/ipc/PTabContext.ipdlh @@ -0,0 +1,84 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */ +/* 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 protocol PBrowser; +include PBrowserOrId; + +using UIStateChangeType from "nsPIDOMWindow.h"; +using mozilla::DocShellOriginAttributes from "mozilla/ipc/BackgroundUtils.h"; + +namespace mozilla { +namespace dom { + +// An IPCTabContext which corresponds to a PBrowser opened by a child when it +// receives window.open(). +// +// If isMozBrowserElement is false, this PopupIPCTabContext is either a +// <xul:browser> or an app frame. The frame's app-id and app-frame-owner-app-id +// will be equal to the opener's values. For a <xul:browser>, those app IDs +// will be NO_APP_ID. +// +// If isMozBrowserElement is true, the frame's browserFrameOwnerAppId will be +// equal to the opener's app-id. +// +// It's an error to set isMozBrowserElement == false if opener is a mozbrowser +// element. Such a PopupIPCTabContext should be rejected by code which receives +// it. +struct PopupIPCTabContext +{ + PBrowserOrId opener; + bool isMozBrowserElement; +}; + +// An IPCTabContext which corresponds to an app, browser, or normal frame. +struct FrameIPCTabContext +{ + // The originAttributes dictionary. + DocShellOriginAttributes originAttributes; + + // The ID of the app containing this app/browser frame, if applicable. + uint32_t frameOwnerAppId; + + // Whether this is a mozbrowser frame. <iframe mozbrowser mozapp> and + // <xul:browser> are not considered to be mozbrowser frames. + bool isMozBrowserElement; + + // Whether this TabContext should work in prerender mode. + bool isPrerendered; + + // The requested presentation URL. + // This value would be empty if the TabContext isn't created for + // presented content. + nsString presentationURL; + + // Keyboard indicator state inherited from the parent. + UIStateChangeType showAccelerators; + UIStateChangeType showFocusRings; +}; + +// XXXcatalinb: This is only used by ServiceWorkerClients::OpenWindow. +// Because service workers don't have an associated TabChild +// we can't satisfy the security constraints on b2g. As such, the parent +// process will accept this tab context only on desktop. +struct UnsafeIPCTabContext +{ }; + +// IPCTabContext is an analog to mozilla::dom::TabContext. Both specify an +// iframe/PBrowser's own and containing app-ids and tell you whether the +// iframe/PBrowser is a browser frame. But only IPCTabContext is allowed to +// travel over IPC. +// +// We need IPCTabContext (specifically, PopupIPCTabContext) to prevent a +// privilege escalation attack by a compromised child process. +union IPCTabContext +{ + PopupIPCTabContext; + FrameIPCTabContext; + UnsafeIPCTabContext; +}; + +} +} diff --git a/dom/ipc/PermissionMessageUtils.cpp b/dom/ipc/PermissionMessageUtils.cpp new file mode 100644 index 000000000..445124b0a --- /dev/null +++ b/dom/ipc/PermissionMessageUtils.cpp @@ -0,0 +1,69 @@ +/* -*- 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/dom/PermissionMessageUtils.h" +#include "nsISerializable.h" +#include "nsSerializationHelper.h" + +namespace IPC { + +void +ParamTraits<Principal>::Write(Message* aMsg, const paramType& aParam) { + bool isNull = !aParam.mPrincipal; + WriteParam(aMsg, isNull); + if (isNull) { + return; + } + + bool isSerialized = false; + nsCString principalString; + nsCOMPtr<nsISerializable> serializable = do_QueryInterface(aParam.mPrincipal); + if (serializable) { + nsresult rv = NS_SerializeToString(serializable, principalString); + if (NS_SUCCEEDED(rv)) { + isSerialized = true; + } + } + + if (!isSerialized) { + NS_RUNTIMEABORT("Unable to serialize principal."); + return; + } + + WriteParam(aMsg, principalString); +} + +bool +ParamTraits<Principal>::Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) +{ + bool isNull; + if (!ReadParam(aMsg, aIter, &isNull)) { + return false; + } + + if (isNull) { + aResult->mPrincipal = nullptr; + return true; + } + + nsCString principalString; + if (!ReadParam(aMsg, aIter, &principalString)) { + return false; + } + + nsCOMPtr<nsISupports> iSupports; + nsresult rv = NS_DeserializeObject(principalString, getter_AddRefs(iSupports)); + NS_ENSURE_SUCCESS(rv, false); + + nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(iSupports); + NS_ENSURE_TRUE(principal, false); + + principal.swap(aResult->mPrincipal); + return true; +} + +} // namespace IPC + diff --git a/dom/ipc/PermissionMessageUtils.h b/dom/ipc/PermissionMessageUtils.h new file mode 100644 index 000000000..3434c83b9 --- /dev/null +++ b/dom/ipc/PermissionMessageUtils.h @@ -0,0 +1,52 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_permission_message_utils_h__ +#define mozilla_dom_permission_message_utils_h__ + +#include "ipc/IPCMessageUtils.h" +#include "nsCOMPtr.h" +#include "nsIPrincipal.h" + +namespace IPC { + +class Principal +{ + friend struct ParamTraits<Principal>; + +public: + Principal() + : mPrincipal(nullptr) + {} + + explicit Principal(nsIPrincipal* aPrincipal) + : mPrincipal(aPrincipal) + {} + + operator nsIPrincipal*() const { return mPrincipal.get(); } + + Principal& operator=(const Principal& aOther) + { + mPrincipal = aOther.mPrincipal; + return *this; + } + +private: + nsCOMPtr<nsIPrincipal> mPrincipal; +}; + +template <> +struct ParamTraits<Principal> +{ + typedef Principal paramType; + static void Write(Message* aMsg, const paramType& aParam); + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult); +}; + +} // namespace IPC + +#endif // mozilla_dom_permission_message_utils_h__ + diff --git a/dom/ipc/PreallocatedProcessManager.cpp b/dom/ipc/PreallocatedProcessManager.cpp new file mode 100644 index 000000000..58ff84f21 --- /dev/null +++ b/dom/ipc/PreallocatedProcessManager.cpp @@ -0,0 +1,252 @@ +/* -*- 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/PreallocatedProcessManager.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/Preferences.h" +#include "mozilla/Unused.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/ScriptSettings.h" +#include "nsIPropertyBag2.h" +#include "ProcessPriorityManager.h" +#include "nsServiceManagerUtils.h" + +// This number is fairly arbitrary ... the intention is to put off +// launching another app process until the last one has finished +// loading its content, to reduce CPU/memory/IO contention. +#define DEFAULT_ALLOCATE_DELAY 1000 + +using namespace mozilla; +using namespace mozilla::hal; +using namespace mozilla::dom; + +namespace { + +/** + * This singleton class implements the static methods on + * PreallocatedProcessManager. + */ +class PreallocatedProcessManagerImpl final + : public nsIObserver +{ +public: + static PreallocatedProcessManagerImpl* Singleton(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + // See comments on PreallocatedProcessManager for these methods. + void AllocateAfterDelay(); + void AllocateOnIdle(); + void AllocateNow(); + already_AddRefed<ContentParent> Take(); + +private: + static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton; + + PreallocatedProcessManagerImpl(); + ~PreallocatedProcessManagerImpl() {} + DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManagerImpl); + + void Init(); + + void RereadPrefs(); + void Enable(); + void Disable(); + + void ObserveProcessShutdown(nsISupports* aSubject); + + bool mEnabled; + bool mShutdown; + RefPtr<ContentParent> mPreallocatedAppProcess; +}; + +/* static */ StaticRefPtr<PreallocatedProcessManagerImpl> +PreallocatedProcessManagerImpl::sSingleton; + +/* static */ PreallocatedProcessManagerImpl* +PreallocatedProcessManagerImpl::Singleton() +{ + if (!sSingleton) { + sSingleton = new PreallocatedProcessManagerImpl(); + sSingleton->Init(); + ClearOnShutdown(&sSingleton); + } + + return sSingleton; +} + +NS_IMPL_ISUPPORTS(PreallocatedProcessManagerImpl, nsIObserver) + +PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl() + : + mEnabled(false) + , mShutdown(false) +{} + +void +PreallocatedProcessManagerImpl::Init() +{ + Preferences::AddStrongObserver(this, "dom.ipc.processPrelaunch.enabled"); + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); + if (os) { + os->AddObserver(this, "ipc:content-shutdown", + /* weakRef = */ false); + os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, + /* weakRef = */ false); + } + { + RereadPrefs(); + } +} + +NS_IMETHODIMP +PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + if (!strcmp("ipc:content-shutdown", aTopic)) { + ObserveProcessShutdown(aSubject); + } else if (!strcmp("nsPref:changed", aTopic)) { + // The only other observer we registered was for our prefs. + RereadPrefs(); + } else if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) { + mShutdown = true; + } else { + MOZ_ASSERT(false); + } + + return NS_OK; +} + +void +PreallocatedProcessManagerImpl::RereadPrefs() +{ + if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled")) { + Enable(); + } else { + Disable(); + } +} + +already_AddRefed<ContentParent> +PreallocatedProcessManagerImpl::Take() +{ + return mPreallocatedAppProcess.forget(); +} + +void +PreallocatedProcessManagerImpl::Enable() +{ + if (mEnabled) { + return; + } + + mEnabled = true; + AllocateAfterDelay(); +} + +void +PreallocatedProcessManagerImpl::AllocateAfterDelay() +{ + if (!mEnabled || mPreallocatedAppProcess) { + return; + } + + MessageLoop::current()->PostDelayedTask( + NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateOnIdle), + Preferences::GetUint("dom.ipc.processPrelaunch.delayMs", + DEFAULT_ALLOCATE_DELAY)); +} + +void +PreallocatedProcessManagerImpl::AllocateOnIdle() +{ + if (!mEnabled || mPreallocatedAppProcess) { + return; + } + + MessageLoop::current()->PostIdleTask(NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateNow)); +} + +void +PreallocatedProcessManagerImpl::AllocateNow() +{ + if (!mEnabled || mPreallocatedAppProcess) { + return; + } + + mPreallocatedAppProcess = ContentParent::PreallocateAppProcess(); +} + +void +PreallocatedProcessManagerImpl::Disable() +{ + if (!mEnabled) { + return; + } + + mEnabled = false; + + if (mPreallocatedAppProcess) { + mPreallocatedAppProcess->Close(); + mPreallocatedAppProcess = nullptr; + } +} + +void +PreallocatedProcessManagerImpl::ObserveProcessShutdown(nsISupports* aSubject) +{ + if (!mPreallocatedAppProcess) { + return; + } + + nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject); + NS_ENSURE_TRUE_VOID(props); + + uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN; + props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID); + NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN); + + if (childID == mPreallocatedAppProcess->ChildID()) { + mPreallocatedAppProcess = nullptr; + } +} + +inline PreallocatedProcessManagerImpl* GetPPMImpl() +{ + return PreallocatedProcessManagerImpl::Singleton(); +} + +} // namespace + +namespace mozilla { + +/* static */ void +PreallocatedProcessManager::AllocateAfterDelay() +{ + GetPPMImpl()->AllocateAfterDelay(); +} + +/* static */ void +PreallocatedProcessManager::AllocateOnIdle() +{ + GetPPMImpl()->AllocateOnIdle(); +} + +/* static */ void +PreallocatedProcessManager::AllocateNow() +{ + GetPPMImpl()->AllocateNow(); +} + +/* static */ already_AddRefed<ContentParent> +PreallocatedProcessManager::Take() +{ + return GetPPMImpl()->Take(); +} + +} // namespace mozilla diff --git a/dom/ipc/PreallocatedProcessManager.h b/dom/ipc/PreallocatedProcessManager.h new file mode 100644 index 000000000..94acd31fd --- /dev/null +++ b/dom/ipc/PreallocatedProcessManager.h @@ -0,0 +1,88 @@ +/* -*- 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/. */ + +#ifndef mozilla_PreallocatedProcessManager_h +#define mozilla_PreallocatedProcessManager_h + +#include "base/basictypes.h" +#include "nsCOMPtr.h" +#include "nsIObserver.h" + +namespace mozilla { +namespace dom { +class ContentParent; +} // namespace dom + +/** + * This class manages a ContentParent that it starts up ahead of any particular + * need. You can then call Take() to get this process and use it. Since we + * already started it up, it should be ready for use faster than if you'd + * created the process when you needed it. + * + * This class watches the dom.ipc.processPrelaunch.enabled pref. If it changes + * from false to true, it preallocates a process. If it changes from true to + * false, it kills the preallocated process, if any. + * + * We don't expect this pref to flip between true and false in production, but + * flipping the pref is important for tests. + * + * The static methods here are implemented by forwarding calls on to a + * PreallocatedProcessManagerImpl singleton class, so if you add a new static + * method here, you'll need to write a corresponding public method on the + * singleton. + */ +class PreallocatedProcessManager final +{ + typedef mozilla::dom::ContentParent ContentParent; + +public: + /** + * Create a process after a delay. We wait for a period of time (specified + * by the dom.ipc.processPrelaunch.delayMs pref), then wait for this process + * to go idle, then allocate the new process. + * + * If the dom.ipc.processPrelaunch.enabled pref is false, or if we already + * have a preallocated process, this function does nothing. + */ + static void AllocateAfterDelay(); + + /** + * Create a process once this process goes idle. + * + * If the dom.ipc.processPrelaunch.enabled pref is false, or if we already + * have a preallocated process, this function does nothing. + */ + static void AllocateOnIdle(); + + /** + * Create a process right now. + * + * If the dom.ipc.processPrelaunch.enabled pref is false, or if we already + * have a preallocated process, this function does nothing. + */ + static void AllocateNow(); + + /** + * Take the preallocated process, if we have one. If we don't have one, this + * returns null. + * + * If you call Take() twice in a row, the second call is guaranteed to return + * null. + * + * After you Take() the preallocated process, you need to call one of the + * Allocate* functions (or change the dom.ipc.processPrelaunch pref from + * false to true) before we'll create a new process. + */ + static already_AddRefed<ContentParent> Take(); + +private: + PreallocatedProcessManager(); + DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManager); +}; + +} // namespace mozilla + +#endif // defined mozilla_PreallocatedProcessManager_h diff --git a/dom/ipc/ProcessHangMonitor.cpp b/dom/ipc/ProcessHangMonitor.cpp new file mode 100644 index 000000000..b574be61f --- /dev/null +++ b/dom/ipc/ProcessHangMonitor.cpp @@ -0,0 +1,1278 @@ +/* -*- 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/ProcessHangMonitor.h" +#include "mozilla/ProcessHangMonitorIPC.h" + +#include "jsapi.h" +#include "js/GCAPI.h" + +#include "mozilla/Atomics.h" +#include "mozilla/BackgroundHangMonitor.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/ScriptSettings.h" +#include "mozilla/dom/TabChild.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/Monitor.h" +#include "mozilla/plugins/PluginBridge.h" +#include "mozilla/Preferences.h" +#include "mozilla/Unused.h" + +#include "nsIFrameLoader.h" +#include "nsIHangReport.h" +#include "nsITabParent.h" +#include "nsPluginHost.h" +#include "nsThreadUtils.h" +#ifdef MOZ_CRASHREPORTER +#include "nsExceptionHandler.h" +#endif + +#include "base/task.h" +#include "base/thread.h" + +#ifdef XP_WIN +// For IsDebuggerPresent() +#include <windows.h> +#endif + +using namespace mozilla; +using namespace mozilla::dom; + +/* + * Basic architecture: + * + * Each process has its own ProcessHangMonitor singleton. This singleton exists + * as long as there is at least one content process in the system. Each content + * process has a HangMonitorChild and the chrome process has one + * HangMonitorParent per process. Each process (including the chrome process) + * runs a hang monitoring thread. The PHangMonitor actors are bound to this + * thread so that they never block on the main thread. + * + * When the content process detects a hang, it posts a task to its hang thread, + * which sends an IPC message to the hang thread in the parent. The parent + * cancels any ongoing CPOW requests and then posts a runnable to the main + * thread that notifies Firefox frontend code of the hang. The frontend code is + * passed an nsIHangReport, which can be used to terminate the hang. + * + * If the user chooses to terminate a script, a task is posted to the chrome + * process's hang monitoring thread, which sends an IPC message to the hang + * thread in the content process. That thread sets a flag to indicate that JS + * execution should be terminated the next time it hits the interrupt + * callback. A similar scheme is used for debugging slow scripts. If a content + * process or plug-in needs to be terminated, the chrome process does so + * directly, without messaging the content process. + */ + +namespace { + +/* Child process objects */ + +class HangMonitorChild + : public PProcessHangMonitorChild +{ + public: + explicit HangMonitorChild(ProcessHangMonitor* aMonitor); + virtual ~HangMonitorChild(); + + void Open(Transport* aTransport, ProcessId aOtherPid, + MessageLoop* aIOLoop); + + typedef ProcessHangMonitor::SlowScriptAction SlowScriptAction; + SlowScriptAction NotifySlowScript(nsITabChild* aTabChild, + const char* aFileName, + unsigned aLineNo); + void NotifySlowScriptAsync(TabId aTabId, + const nsCString& aFileName, + unsigned aLineNo); + + bool IsDebuggerStartupComplete(); + + void NotifyPluginHang(uint32_t aPluginId); + void NotifyPluginHangAsync(uint32_t aPluginId); + + void ClearHang(); + void ClearHangAsync(); + void ClearForcePaint(); + + virtual bool RecvTerminateScript() override; + virtual bool RecvBeginStartingDebugger() override; + virtual bool RecvEndStartingDebugger() override; + + virtual bool RecvForcePaint(const TabId& aTabId, const uint64_t& aLayerObserverEpoch) override; + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + void InterruptCallback(); + void Shutdown(); + + static HangMonitorChild* Get() { return sInstance; } + + MessageLoop* MonitorLoop() { return mHangMonitor->MonitorLoop(); } + + private: + void ShutdownOnThread(); + + static Atomic<HangMonitorChild*> sInstance; + UniquePtr<BackgroundHangMonitor> mForcePaintMonitor; + + const RefPtr<ProcessHangMonitor> mHangMonitor; + Monitor mMonitor; + + // Main thread-only. + bool mSentReport; + + // These fields must be accessed with mMonitor held. + bool mTerminateScript; + bool mStartDebugger; + bool mFinishedStartingDebugger; + bool mForcePaint; + TabId mForcePaintTab; + MOZ_INIT_OUTSIDE_CTOR uint64_t mForcePaintEpoch; + JSContext* mContext; + bool mShutdownDone; + + // This field is only accessed on the hang thread. + bool mIPCOpen; +}; + +Atomic<HangMonitorChild*> HangMonitorChild::sInstance; + +/* Parent process objects */ + +class HangMonitorParent; + +class HangMonitoredProcess final + : public nsIHangReport +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + + HangMonitoredProcess(HangMonitorParent* aActor, + ContentParent* aContentParent) + : mActor(aActor), mContentParent(aContentParent) {} + + NS_IMETHOD GetHangType(uint32_t* aHangType) override; + NS_IMETHOD GetScriptBrowser(nsIDOMElement** aBrowser) override; + NS_IMETHOD GetScriptFileName(nsACString& aFileName) override; + NS_IMETHOD GetScriptLineNo(uint32_t* aLineNo) override; + + NS_IMETHOD GetPluginName(nsACString& aPluginName) override; + + NS_IMETHOD TerminateScript() override; + NS_IMETHOD BeginStartingDebugger() override; + NS_IMETHOD EndStartingDebugger() override; + NS_IMETHOD TerminatePlugin() override; + NS_IMETHOD UserCanceled() override; + + NS_IMETHOD IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult) override; + + // Called when a content process shuts down. + void Clear() { + mContentParent = nullptr; + mActor = nullptr; + } + + /** + * Sets the information associated with this hang: this includes the ID of + * the plugin which caused the hang as well as the content PID. The ID of + * a minidump taken during the hang can also be provided. + * + * @param aHangData The hang information + * @param aDumpId The ID of a minidump taken when the hang occurred + */ + void SetHangData(const HangData& aHangData, const nsAString& aDumpId) { + mHangData = aHangData; + mDumpId = aDumpId; + } + + void ClearHang() { + mHangData = HangData(); + mDumpId.Truncate(); + } + +private: + ~HangMonitoredProcess() {} + + // Everything here is main thread-only. + HangMonitorParent* mActor; + ContentParent* mContentParent; + HangData mHangData; + nsAutoString mDumpId; +}; + +class HangMonitorParent + : public PProcessHangMonitorParent +{ +public: + explicit HangMonitorParent(ProcessHangMonitor* aMonitor); + virtual ~HangMonitorParent(); + + void Open(Transport* aTransport, ProcessId aPid, MessageLoop* aIOLoop); + + virtual bool RecvHangEvidence(const HangData& aHangData) override; + virtual bool RecvClearHang() override; + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + void SetProcess(HangMonitoredProcess* aProcess) { mProcess = aProcess; } + + void Shutdown(); + + void ForcePaint(dom::TabParent* aTabParent, uint64_t aLayerObserverEpoch); + + void TerminateScript(); + void BeginStartingDebugger(); + void EndStartingDebugger(); + void CleanupPluginHang(uint32_t aPluginId, bool aRemoveFiles); + + /** + * Update the dump for the specified plugin. This method is thread-safe and + * is used to replace a browser minidump with a full minidump. If aDumpId is + * empty this is a no-op. + */ + void UpdateMinidump(uint32_t aPluginId, const nsString& aDumpId); + + MessageLoop* MonitorLoop() { return mHangMonitor->MonitorLoop(); } + +private: + bool TakeBrowserMinidump(const PluginHangData& aPhd, nsString& aCrashId); + + void ForcePaintOnThread(TabId aTabId, uint64_t aLayerObserverEpoch); + + void ShutdownOnThread(); + + const RefPtr<ProcessHangMonitor> mHangMonitor; + + // This field is read-only after construction. + bool mReportHangs; + + // This field is only accessed on the hang thread. + bool mIPCOpen; + + Monitor mMonitor; + + // Must be accessed with mMonitor held. + RefPtr<HangMonitoredProcess> mProcess; + bool mShutdownDone; + // Map from plugin ID to crash dump ID. Protected by mBrowserCrashDumpHashLock. + nsDataHashtable<nsUint32HashKey, nsString> mBrowserCrashDumpIds; + Mutex mBrowserCrashDumpHashLock; +}; + +} // namespace + +/* HangMonitorChild implementation */ + +HangMonitorChild::HangMonitorChild(ProcessHangMonitor* aMonitor) + : mHangMonitor(aMonitor), + mMonitor("HangMonitorChild lock"), + mSentReport(false), + mTerminateScript(false), + mStartDebugger(false), + mFinishedStartingDebugger(false), + mForcePaint(false), + mShutdownDone(false), + mIPCOpen(true) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + mContext = danger::GetJSContext(); + mForcePaintMonitor = + MakeUnique<mozilla::BackgroundHangMonitor>("Gecko_Child_ForcePaint", + 128, /* ms timeout for microhangs */ + 8192 /* ms timeout for permahangs */, + BackgroundHangMonitor::THREAD_PRIVATE); +} + +HangMonitorChild::~HangMonitorChild() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(sInstance == this); + mForcePaintMonitor = nullptr; + sInstance = nullptr; +} + +void +HangMonitorChild::InterruptCallback() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + bool forcePaint; + TabId forcePaintTab; + uint64_t forcePaintEpoch; + + { + MonitorAutoLock lock(mMonitor); + forcePaint = mForcePaint; + forcePaintTab = mForcePaintTab; + forcePaintEpoch = mForcePaintEpoch; + + mForcePaint = false; + } + + if (forcePaint) { + RefPtr<TabChild> tabChild = TabChild::FindTabChild(forcePaintTab); + if (tabChild) { + tabChild->ForcePaint(forcePaintEpoch); + } + } +} + +void +HangMonitorChild::Shutdown() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + MonitorAutoLock lock(mMonitor); + while (!mShutdownDone) { + mMonitor.Wait(); + } +} + +void +HangMonitorChild::ShutdownOnThread() +{ + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + + MonitorAutoLock lock(mMonitor); + mShutdownDone = true; + mMonitor.Notify(); +} + +void +HangMonitorChild::ActorDestroy(ActorDestroyReason aWhy) +{ + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + + mIPCOpen = false; + + // We use a task here to ensure that IPDL is finished with this + // HangMonitorChild before it gets deleted on the main thread. + MonitorLoop()->PostTask(NewNonOwningRunnableMethod(this, &HangMonitorChild::ShutdownOnThread)); +} + +bool +HangMonitorChild::RecvTerminateScript() +{ + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + + MonitorAutoLock lock(mMonitor); + mTerminateScript = true; + return true; +} + +bool +HangMonitorChild::RecvBeginStartingDebugger() +{ + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + + MonitorAutoLock lock(mMonitor); + mStartDebugger = true; + return true; +} + +bool +HangMonitorChild::RecvEndStartingDebugger() +{ + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + + MonitorAutoLock lock(mMonitor); + mFinishedStartingDebugger = true; + return true; +} + +bool +HangMonitorChild::RecvForcePaint(const TabId& aTabId, const uint64_t& aLayerObserverEpoch) +{ + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + + mForcePaintMonitor->NotifyActivity(); + + { + MonitorAutoLock lock(mMonitor); + mForcePaint = true; + mForcePaintTab = aTabId; + mForcePaintEpoch = aLayerObserverEpoch; + } + + JS_RequestInterruptCallback(mContext); + + return true; +} + +void +HangMonitorChild::ClearForcePaint() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + MOZ_RELEASE_ASSERT(XRE_IsContentProcess()); + + mForcePaintMonitor->NotifyWait(); +} + +void +HangMonitorChild::Open(Transport* aTransport, ProcessId aPid, + MessageLoop* aIOLoop) +{ + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + + MOZ_ASSERT(!sInstance); + sInstance = this; + + DebugOnly<bool> ok = PProcessHangMonitorChild::Open(aTransport, aPid, aIOLoop); + MOZ_ASSERT(ok); +} + +void +HangMonitorChild::NotifySlowScriptAsync(TabId aTabId, + const nsCString& aFileName, + unsigned aLineNo) +{ + if (mIPCOpen) { + Unused << SendHangEvidence(SlowScriptData(aTabId, aFileName, aLineNo)); + } +} + +HangMonitorChild::SlowScriptAction +HangMonitorChild::NotifySlowScript(nsITabChild* aTabChild, + const char* aFileName, + unsigned aLineNo) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + mSentReport = true; + + { + MonitorAutoLock lock(mMonitor); + + if (mTerminateScript) { + mTerminateScript = false; + return SlowScriptAction::Terminate; + } + + if (mStartDebugger) { + mStartDebugger = false; + return SlowScriptAction::StartDebugger; + } + } + + TabId id; + if (aTabChild) { + RefPtr<TabChild> tabChild = static_cast<TabChild*>(aTabChild); + id = tabChild->GetTabId(); + } + nsAutoCString filename(aFileName); + + MonitorLoop()->PostTask(NewNonOwningRunnableMethod + <TabId, nsCString, unsigned>(this, + &HangMonitorChild::NotifySlowScriptAsync, + id, filename, aLineNo)); + return SlowScriptAction::Continue; +} + +bool +HangMonitorChild::IsDebuggerStartupComplete() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + MonitorAutoLock lock(mMonitor); + + if (mFinishedStartingDebugger) { + mFinishedStartingDebugger = false; + return true; + } + + return false; +} + +void +HangMonitorChild::NotifyPluginHang(uint32_t aPluginId) +{ + // main thread in the child + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + mSentReport = true; + + // bounce to background thread + MonitorLoop()->PostTask(NewNonOwningRunnableMethod<uint32_t>(this, + &HangMonitorChild::NotifyPluginHangAsync, + aPluginId)); +} + +void +HangMonitorChild::NotifyPluginHangAsync(uint32_t aPluginId) +{ + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + + // bounce back to parent on background thread + if (mIPCOpen) { + Unused << SendHangEvidence(PluginHangData(aPluginId, + base::GetCurrentProcId())); + } +} + +void +HangMonitorChild::ClearHang() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (mSentReport) { + // bounce to background thread + MonitorLoop()->PostTask(NewNonOwningRunnableMethod(this, &HangMonitorChild::ClearHangAsync)); + + MonitorAutoLock lock(mMonitor); + mSentReport = false; + mTerminateScript = false; + mStartDebugger = false; + mFinishedStartingDebugger = false; + } +} + +void +HangMonitorChild::ClearHangAsync() +{ + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + + // bounce back to parent on background thread + if (mIPCOpen) { + Unused << SendClearHang(); + } +} + +/* HangMonitorParent implementation */ + +HangMonitorParent::HangMonitorParent(ProcessHangMonitor* aMonitor) + : mHangMonitor(aMonitor), + mIPCOpen(true), + mMonitor("HangMonitorParent lock"), + mShutdownDone(false), + mBrowserCrashDumpHashLock("mBrowserCrashDumpIds lock") +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + mReportHangs = mozilla::Preferences::GetBool("dom.ipc.reportProcessHangs", false); +} + +HangMonitorParent::~HangMonitorParent() +{ +#ifdef MOZ_CRASHREPORTER + MutexAutoLock lock(mBrowserCrashDumpHashLock); + + for (auto iter = mBrowserCrashDumpIds.Iter(); !iter.Done(); iter.Next()) { + nsString crashId = iter.UserData(); + if (!crashId.IsEmpty()) { + CrashReporter::DeleteMinidumpFilesForID(crashId); + } + } +#endif +} + +void +HangMonitorParent::Shutdown() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + MonitorAutoLock lock(mMonitor); + + if (mProcess) { + mProcess->Clear(); + mProcess = nullptr; + } + + MonitorLoop()->PostTask(NewNonOwningRunnableMethod(this, + &HangMonitorParent::ShutdownOnThread)); + + while (!mShutdownDone) { + mMonitor.Wait(); + } +} + +void +HangMonitorParent::ShutdownOnThread() +{ + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + + // mIPCOpen is only written from this thread, so need need to take the lock + // here. We'd be shooting ourselves in the foot, because ActorDestroy takes + // it. + if (mIPCOpen) { + Close(); + } + + MonitorAutoLock lock(mMonitor); + mShutdownDone = true; + mMonitor.Notify(); +} + +void +HangMonitorParent::ForcePaint(dom::TabParent* aTab, uint64_t aLayerObserverEpoch) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + TabId id = aTab->GetTabId(); + MonitorLoop()->PostTask(NewNonOwningRunnableMethod<TabId, uint64_t>( + this, &HangMonitorParent::ForcePaintOnThread, id, aLayerObserverEpoch)); +} + +void +HangMonitorParent::ForcePaintOnThread(TabId aTabId, uint64_t aLayerObserverEpoch) +{ + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + + if (mIPCOpen) { + Unused << SendForcePaint(aTabId, aLayerObserverEpoch); + } +} + +void +HangMonitorParent::ActorDestroy(ActorDestroyReason aWhy) +{ + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + mIPCOpen = false; +} + +void +HangMonitorParent::Open(Transport* aTransport, ProcessId aPid, + MessageLoop* aIOLoop) +{ + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + + DebugOnly<bool> ok = PProcessHangMonitorParent::Open(aTransport, aPid, aIOLoop); + MOZ_ASSERT(ok); +} + +class HangObserverNotifier final : public Runnable +{ +public: + HangObserverNotifier(HangMonitoredProcess* aProcess, + HangMonitorParent *aParent, + const HangData& aHangData, + const nsString& aBrowserDumpId, + bool aTakeMinidump) + : mProcess(aProcess), + mParent(aParent), + mHangData(aHangData), + mBrowserDumpId(aBrowserDumpId), + mTakeMinidump(aTakeMinidump) + {} + + NS_IMETHOD + Run() override + { + // chrome process, main thread + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + nsString dumpId; + if ((mHangData.type() == HangData::TPluginHangData) && mTakeMinidump) { + // We've been handed a partial minidump; complete it with plugin and + // content process dumps. + const PluginHangData& phd = mHangData.get_PluginHangData(); + plugins::TakeFullMinidump(phd.pluginId(), phd.contentProcessId(), + mBrowserDumpId, dumpId); + mParent->UpdateMinidump(phd.pluginId(), dumpId); + } else { + // We already have a full minidump; go ahead and use it. + dumpId = mBrowserDumpId; + } + + mProcess->SetHangData(mHangData, dumpId); + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + observerService->NotifyObservers(mProcess, "process-hang-report", nullptr); + return NS_OK; + } + +private: + RefPtr<HangMonitoredProcess> mProcess; + HangMonitorParent* mParent; + HangData mHangData; + nsAutoString mBrowserDumpId; + bool mTakeMinidump; +}; + +// Take a minidump of the browser process if one wasn't already taken for the +// plugin that caused the hang. Return false if a dump was already available or +// true if new one has been taken. +bool +HangMonitorParent::TakeBrowserMinidump(const PluginHangData& aPhd, + nsString& aCrashId) +{ +#ifdef MOZ_CRASHREPORTER + MutexAutoLock lock(mBrowserCrashDumpHashLock); + if (!mBrowserCrashDumpIds.Get(aPhd.pluginId(), &aCrashId)) { + nsCOMPtr<nsIFile> browserDump; + if (CrashReporter::TakeMinidump(getter_AddRefs(browserDump), true)) { + if (!CrashReporter::GetIDFromMinidump(browserDump, aCrashId) + || aCrashId.IsEmpty()) { + browserDump->Remove(false); + NS_WARNING("Failed to generate timely browser stack, " + "this is bad for plugin hang analysis!"); + } else { + mBrowserCrashDumpIds.Put(aPhd.pluginId(), aCrashId); + return true; + } + } + } +#endif // MOZ_CRASHREPORTER + + return false; +} + +bool +HangMonitorParent::RecvHangEvidence(const HangData& aHangData) +{ + // chrome process, background thread + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + + if (!mReportHangs) { + return true; + } + +#ifdef XP_WIN + // Don't report hangs if we're debugging the process. You can comment this + // line out for testing purposes. + if (IsDebuggerPresent()) { + return true; + } +#endif + + // Before we wake up the browser main thread we want to take a + // browser minidump. + nsAutoString crashId; + bool takeMinidump = false; + if (aHangData.type() == HangData::TPluginHangData) { + takeMinidump = TakeBrowserMinidump(aHangData.get_PluginHangData(), crashId); + } + + mHangMonitor->InitiateCPOWTimeout(); + + MonitorAutoLock lock(mMonitor); + + nsCOMPtr<nsIRunnable> notifier = + new HangObserverNotifier(mProcess, this, aHangData, crashId, takeMinidump); + NS_DispatchToMainThread(notifier); + + return true; +} + +class ClearHangNotifier final : public Runnable +{ +public: + explicit ClearHangNotifier(HangMonitoredProcess* aProcess) + : mProcess(aProcess) + {} + + NS_IMETHOD + Run() override + { + // chrome process, main thread + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + mProcess->ClearHang(); + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + observerService->NotifyObservers(mProcess, "clear-hang-report", nullptr); + return NS_OK; + } + +private: + RefPtr<HangMonitoredProcess> mProcess; +}; + +bool +HangMonitorParent::RecvClearHang() +{ + // chrome process, background thread + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + + if (!mReportHangs) { + return true; + } + + mHangMonitor->InitiateCPOWTimeout(); + + MonitorAutoLock lock(mMonitor); + + nsCOMPtr<nsIRunnable> notifier = + new ClearHangNotifier(mProcess); + NS_DispatchToMainThread(notifier); + + return true; +} + +void +HangMonitorParent::TerminateScript() +{ + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + + if (mIPCOpen) { + Unused << SendTerminateScript(); + } +} + +void +HangMonitorParent::BeginStartingDebugger() +{ + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + + if (mIPCOpen) { + Unused << SendBeginStartingDebugger(); + } +} + +void +HangMonitorParent::EndStartingDebugger() +{ + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + + if (mIPCOpen) { + Unused << SendEndStartingDebugger(); + } +} + +void +HangMonitorParent::CleanupPluginHang(uint32_t aPluginId, bool aRemoveFiles) +{ + MutexAutoLock lock(mBrowserCrashDumpHashLock); + nsAutoString crashId; + if (!mBrowserCrashDumpIds.Get(aPluginId, &crashId)) { + return; + } + mBrowserCrashDumpIds.Remove(aPluginId); +#ifdef MOZ_CRASHREPORTER + if (aRemoveFiles && !crashId.IsEmpty()) { + CrashReporter::DeleteMinidumpFilesForID(crashId); + } +#endif +} + +void +HangMonitorParent::UpdateMinidump(uint32_t aPluginId, const nsString& aDumpId) +{ + if (aDumpId.IsEmpty()) { + return; + } + + MutexAutoLock lock(mBrowserCrashDumpHashLock); + mBrowserCrashDumpIds.Put(aPluginId, aDumpId); +} + +/* HangMonitoredProcess implementation */ + +NS_IMPL_ISUPPORTS(HangMonitoredProcess, nsIHangReport) + +NS_IMETHODIMP +HangMonitoredProcess::GetHangType(uint32_t* aHangType) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + switch (mHangData.type()) { + case HangData::TSlowScriptData: + *aHangType = SLOW_SCRIPT; + break; + case HangData::TPluginHangData: + *aHangType = PLUGIN_HANG; + break; + default: + MOZ_ASSERT_UNREACHABLE("Unexpected HangData type"); + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +NS_IMETHODIMP +HangMonitoredProcess::GetScriptBrowser(nsIDOMElement** aBrowser) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + if (mHangData.type() != HangData::TSlowScriptData) { + return NS_ERROR_NOT_AVAILABLE; + } + + TabId tabId = mHangData.get_SlowScriptData().tabId(); + if (!mContentParent) { + return NS_ERROR_NOT_AVAILABLE; + } + + nsTArray<PBrowserParent*> tabs; + mContentParent->ManagedPBrowserParent(tabs); + for (size_t i = 0; i < tabs.Length(); i++) { + TabParent* tp = TabParent::GetFrom(tabs[i]); + if (tp->GetTabId() == tabId) { + nsCOMPtr<nsIDOMElement> node = do_QueryInterface(tp->GetOwnerElement()); + node.forget(aBrowser); + return NS_OK; + } + } + + *aBrowser = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +HangMonitoredProcess::GetScriptFileName(nsACString& aFileName) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + if (mHangData.type() != HangData::TSlowScriptData) { + return NS_ERROR_NOT_AVAILABLE; + } + + aFileName = mHangData.get_SlowScriptData().filename(); + return NS_OK; +} + +NS_IMETHODIMP +HangMonitoredProcess::GetScriptLineNo(uint32_t* aLineNo) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + if (mHangData.type() != HangData::TSlowScriptData) { + return NS_ERROR_NOT_AVAILABLE; + } + + *aLineNo = mHangData.get_SlowScriptData().lineno(); + return NS_OK; +} + +NS_IMETHODIMP +HangMonitoredProcess::GetPluginName(nsACString& aPluginName) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + if (mHangData.type() != HangData::TPluginHangData) { + return NS_ERROR_NOT_AVAILABLE; + } + + uint32_t id = mHangData.get_PluginHangData().pluginId(); + + RefPtr<nsPluginHost> host = nsPluginHost::GetInst(); + nsPluginTag* tag = host->PluginWithId(id); + if (!tag) { + return NS_ERROR_UNEXPECTED; + } + + aPluginName = tag->Name(); + return NS_OK; +} + +NS_IMETHODIMP +HangMonitoredProcess::TerminateScript() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + if (mHangData.type() != HangData::TSlowScriptData) { + return NS_ERROR_UNEXPECTED; + } + + if (!mActor) { + return NS_ERROR_UNEXPECTED; + } + + ProcessHangMonitor::Get()->MonitorLoop()->PostTask(NewNonOwningRunnableMethod(mActor, + &HangMonitorParent::TerminateScript)); + return NS_OK; +} + +NS_IMETHODIMP +HangMonitoredProcess::BeginStartingDebugger() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + if (mHangData.type() != HangData::TSlowScriptData) { + return NS_ERROR_UNEXPECTED; + } + + if (!mActor) { + return NS_ERROR_UNEXPECTED; + } + + ProcessHangMonitor::Get()->MonitorLoop()->PostTask(NewNonOwningRunnableMethod(mActor, + &HangMonitorParent::BeginStartingDebugger)); + return NS_OK; +} + +NS_IMETHODIMP +HangMonitoredProcess::EndStartingDebugger() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + if (mHangData.type() != HangData::TSlowScriptData) { + return NS_ERROR_UNEXPECTED; + } + + if (!mActor) { + return NS_ERROR_UNEXPECTED; + } + + ProcessHangMonitor::Get()->MonitorLoop()->PostTask(NewNonOwningRunnableMethod(mActor, + &HangMonitorParent::EndStartingDebugger)); + return NS_OK; +} + +NS_IMETHODIMP +HangMonitoredProcess::TerminatePlugin() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + if (mHangData.type() != HangData::TPluginHangData) { + return NS_ERROR_UNEXPECTED; + } + + // Use the multi-process crash report generated earlier. + uint32_t id = mHangData.get_PluginHangData().pluginId(); + base::ProcessId contentPid = mHangData.get_PluginHangData().contentProcessId(); + plugins::TerminatePlugin(id, contentPid, NS_LITERAL_CSTRING("HangMonitor"), + mDumpId); + + if (mActor) { + mActor->CleanupPluginHang(id, false); + } + return NS_OK; +} + +NS_IMETHODIMP +HangMonitoredProcess::IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + if (!mActor) { + *aResult = false; + return NS_OK; + } + + TabParent* tp = TabParent::GetFrom(aFrameLoader); + if (!tp) { + *aResult = false; + return NS_OK; + } + + *aResult = mContentParent == tp->Manager(); + return NS_OK; +} + +NS_IMETHODIMP +HangMonitoredProcess::UserCanceled() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + if (mHangData.type() != HangData::TPluginHangData) { + return NS_OK; + } + + if (mActor) { + uint32_t id = mHangData.get_PluginHangData().pluginId(); + mActor->CleanupPluginHang(id, true); + } + return NS_OK; +} + +static bool +InterruptCallback(JSContext* cx) +{ + if (HangMonitorChild* child = HangMonitorChild::Get()) { + child->InterruptCallback(); + } + + return true; +} + +ProcessHangMonitor* ProcessHangMonitor::sInstance; + +ProcessHangMonitor::ProcessHangMonitor() + : mCPOWTimeout(false) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + MOZ_COUNT_CTOR(ProcessHangMonitor); + + if (XRE_IsContentProcess()) { + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + obs->AddObserver(this, "xpcom-shutdown", false); + } + + mThread = new base::Thread("ProcessHangMonitor"); + if (!mThread->Start()) { + delete mThread; + mThread = nullptr; + } +} + +ProcessHangMonitor::~ProcessHangMonitor() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + MOZ_COUNT_DTOR(ProcessHangMonitor); + + MOZ_ASSERT(sInstance == this); + sInstance = nullptr; + + delete mThread; +} + +ProcessHangMonitor* +ProcessHangMonitor::GetOrCreate() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + if (!sInstance) { + sInstance = new ProcessHangMonitor(); + } + return sInstance; +} + +NS_IMPL_ISUPPORTS(ProcessHangMonitor, nsIObserver) + +NS_IMETHODIMP +ProcessHangMonitor::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + if (!strcmp(aTopic, "xpcom-shutdown")) { + if (HangMonitorChild* child = HangMonitorChild::Get()) { + child->Shutdown(); + delete child; + } + + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->RemoveObserver(this, "xpcom-shutdown"); + } + } + return NS_OK; +} + +ProcessHangMonitor::SlowScriptAction +ProcessHangMonitor::NotifySlowScript(nsITabChild* aTabChild, + const char* aFileName, + unsigned aLineNo) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + return HangMonitorChild::Get()->NotifySlowScript(aTabChild, aFileName, aLineNo); +} + +bool +ProcessHangMonitor::IsDebuggerStartupComplete() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + return HangMonitorChild::Get()->IsDebuggerStartupComplete(); +} + +bool +ProcessHangMonitor::ShouldTimeOutCPOWs() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + if (mCPOWTimeout) { + mCPOWTimeout = false; + return true; + } + return false; +} + +void +ProcessHangMonitor::InitiateCPOWTimeout() +{ + MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop()); + mCPOWTimeout = true; +} + +void +ProcessHangMonitor::NotifyPluginHang(uint32_t aPluginId) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + return HangMonitorChild::Get()->NotifyPluginHang(aPluginId); +} + +PProcessHangMonitorParent* +mozilla::CreateHangMonitorParent(ContentParent* aContentParent, + mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherPid) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate(); + HangMonitorParent* parent = new HangMonitorParent(monitor); + + HangMonitoredProcess* process = new HangMonitoredProcess(parent, aContentParent); + parent->SetProcess(process); + + monitor->MonitorLoop()->PostTask(NewNonOwningRunnableMethod + <mozilla::ipc::Transport*, + base::ProcessId, + MessageLoop*>(parent, + &HangMonitorParent::Open, + aTransport, aOtherPid, + XRE_GetIOMessageLoop())); + + return parent; +} + +PProcessHangMonitorChild* +mozilla::CreateHangMonitorChild(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherPid) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + JSContext* cx = danger::GetJSContext(); + JS_AddInterruptCallback(cx, InterruptCallback); + + ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate(); + HangMonitorChild* child = new HangMonitorChild(monitor); + + monitor->MonitorLoop()->PostTask(NewNonOwningRunnableMethod + <mozilla::ipc::Transport*, + base::ProcessId, + MessageLoop*>(child, + &HangMonitorChild::Open, + aTransport, aOtherPid, + XRE_GetIOMessageLoop())); + + return child; +} + +MessageLoop* +ProcessHangMonitor::MonitorLoop() +{ + return mThread->message_loop(); +} + +/* static */ void +ProcessHangMonitor::AddProcess(ContentParent* aContentParent) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + if (mozilla::Preferences::GetBool("dom.ipc.processHangMonitor", false)) { + DebugOnly<bool> opened = PProcessHangMonitor::Open(aContentParent); + MOZ_ASSERT(opened); + } +} + +/* static */ void +ProcessHangMonitor::RemoveProcess(PProcessHangMonitorParent* aParent) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + auto parent = static_cast<HangMonitorParent*>(aParent); + parent->Shutdown(); + delete parent; +} + +/* static */ void +ProcessHangMonitor::ClearHang() +{ + MOZ_ASSERT(NS_IsMainThread()); + if (HangMonitorChild* child = HangMonitorChild::Get()) { + child->ClearHang(); + } +} + +/* static */ void +ProcessHangMonitor::ForcePaint(PProcessHangMonitorParent* aParent, + dom::TabParent* aTabParent, + uint64_t aLayerObserverEpoch) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + auto parent = static_cast<HangMonitorParent*>(aParent); + parent->ForcePaint(aTabParent, aLayerObserverEpoch); +} + +/* static */ void +ProcessHangMonitor::ClearForcePaint() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + MOZ_RELEASE_ASSERT(XRE_IsContentProcess()); + + if (HangMonitorChild* child = HangMonitorChild::Get()) { + child->ClearForcePaint(); + } +} diff --git a/dom/ipc/ProcessHangMonitor.h b/dom/ipc/ProcessHangMonitor.h new file mode 100644 index 000000000..5ab81c87e --- /dev/null +++ b/dom/ipc/ProcessHangMonitor.h @@ -0,0 +1,82 @@ +/* -*- 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/. */ + +#ifndef mozilla_ProcessHangMonitor_h +#define mozilla_ProcessHangMonitor_h + +#include "mozilla/Atomics.h" +#include "nsIObserver.h" + +class nsITabChild; + +class MessageLoop; + +namespace base { +class Thread; +} // namespace base + +namespace mozilla { + +namespace dom { +class ContentParent; +class TabParent; +} // namespace dom + +class PProcessHangMonitorParent; + +class ProcessHangMonitor final + : public nsIObserver +{ + private: + ProcessHangMonitor(); + virtual ~ProcessHangMonitor(); + + public: + static ProcessHangMonitor* Get() { return sInstance; } + static ProcessHangMonitor* GetOrCreate(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + static void AddProcess(dom::ContentParent* aContentParent); + static void RemoveProcess(PProcessHangMonitorParent* aParent); + + static void ClearHang(); + + static void ForcePaint(PProcessHangMonitorParent* aParent, + dom::TabParent* aTab, + uint64_t aLayerObserverEpoch); + static void ClearForcePaint(); + + enum SlowScriptAction { + Continue, + Terminate, + StartDebugger + }; + SlowScriptAction NotifySlowScript(nsITabChild* aTabChild, + const char* aFileName, + unsigned aLineNo); + + void NotifyPluginHang(uint32_t aPluginId); + + bool IsDebuggerStartupComplete(); + + void InitiateCPOWTimeout(); + bool ShouldTimeOutCPOWs(); + + MessageLoop* MonitorLoop(); + + private: + static ProcessHangMonitor* sInstance; + + Atomic<bool> mCPOWTimeout; + + base::Thread* mThread; +}; + +} // namespace mozilla + +#endif // mozilla_ProcessHangMonitor_h diff --git a/dom/ipc/ProcessHangMonitorIPC.h b/dom/ipc/ProcessHangMonitorIPC.h new file mode 100644 index 000000000..dbb9f9b76 --- /dev/null +++ b/dom/ipc/ProcessHangMonitorIPC.h @@ -0,0 +1,34 @@ +/* -*- 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/. */ + +#ifndef mozilla_ProcessHangMonitorIPC_h +#define mozilla_ProcessHangMonitorIPC_h + +#include "base/task.h" +#include "base/thread.h" + +#include "mozilla/PProcessHangMonitor.h" +#include "mozilla/PProcessHangMonitorParent.h" +#include "mozilla/PProcessHangMonitorChild.h" + +namespace mozilla { + +namespace dom { +class ContentParent; +} // namespace dom + +PProcessHangMonitorParent* +CreateHangMonitorParent(mozilla::dom::ContentParent* aContentParent, + mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess); + +PProcessHangMonitorChild* +CreateHangMonitorChild(mozilla::ipc::Transport* aTransport, + base::ProcessId aOtherProcess); + +} // namespace mozilla + +#endif // mozilla_ProcessHangMonitorIPC_h diff --git a/dom/ipc/ProcessPriorityManager.cpp b/dom/ipc/ProcessPriorityManager.cpp new file mode 100644 index 000000000..80839796d --- /dev/null +++ b/dom/ipc/ProcessPriorityManager.cpp @@ -0,0 +1,1454 @@ +/* -*- 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 "ProcessPriorityManager.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/Hal.h" +#include "mozilla/IntegerPrintfMacros.h" +#include "mozilla/Preferences.h" +#include "mozilla/Services.h" +#include "mozilla/Unused.h" +#include "AudioChannelService.h" +#include "mozilla/Logging.h" +#include "nsPrintfCString.h" +#include "nsXULAppAPI.h" +#include "nsIFrameLoader.h" +#include "nsIObserverService.h" +#include "StaticPtr.h" +#include "nsIMozBrowserFrame.h" +#include "nsIObserver.h" +#include "nsITimer.h" +#include "nsIPropertyBag2.h" +#include "nsComponentManagerUtils.h" +#include "nsCRT.h" + +using namespace mozilla; +using namespace mozilla::dom; +using namespace mozilla::hal; + +#ifdef XP_WIN +#include <process.h> +#define getpid _getpid +#else +#include <unistd.h> +#endif + +#ifdef LOG +#undef LOG +#endif + +// Use LOGP inside a ParticularProcessPriorityManager method; use LOG +// everywhere else. LOGP prints out information about the particular process +// priority manager. +// +// (Wow, our logging story is a huge mess.) + +// #define ENABLE_LOGGING 1 + +#if defined(ANDROID) && defined(ENABLE_LOGGING) +# include <android/log.h> +# define LOG(fmt, ...) \ + __android_log_print(ANDROID_LOG_INFO, \ + "Gecko:ProcessPriorityManager", \ + fmt, ## __VA_ARGS__) +# define LOGP(fmt, ...) \ + __android_log_print(ANDROID_LOG_INFO, \ + "Gecko:ProcessPriorityManager", \ + "[%schild-id=%" PRIu64 ", pid=%d] " fmt, \ + NameWithComma().get(), \ + static_cast<uint64_t>(ChildID()), Pid(), ## __VA_ARGS__) + +#elif defined(ENABLE_LOGGING) +# define LOG(fmt, ...) \ + printf("ProcessPriorityManager - " fmt "\n", ##__VA_ARGS__) +# define LOGP(fmt, ...) \ + printf("ProcessPriorityManager[%schild-id=%" PRIu64 ", pid=%d] - " \ + fmt "\n", \ + NameWithComma().get(), \ + static_cast<uint64_t>(ChildID()), Pid(), ##__VA_ARGS__) +#else + static LogModule* + GetPPMLog() + { + static LazyLogModule sLog("ProcessPriorityManager"); + return sLog; + } +# define LOG(fmt, ...) \ + MOZ_LOG(GetPPMLog(), LogLevel::Debug, \ + ("ProcessPriorityManager - " fmt, ##__VA_ARGS__)) +# define LOGP(fmt, ...) \ + MOZ_LOG(GetPPMLog(), LogLevel::Debug, \ + ("ProcessPriorityManager[%schild-id=%" PRIu64 ", pid=%d] - " fmt, \ + NameWithComma().get(), \ + static_cast<uint64_t>(ChildID()), Pid(), ##__VA_ARGS__)) +#endif + +namespace { + +class ParticularProcessPriorityManager; + +class ProcessLRUPool final +{ +public: + /** + * Creates a new process LRU pool for the specified priority. + */ + explicit ProcessLRUPool(ProcessPriority aPriority); + + /** + * Used to remove a particular process priority manager from the LRU pool + * when the associated ContentParent is destroyed or its priority changes. + */ + void Remove(ParticularProcessPriorityManager* aParticularManager); + + /** + * Used to add a particular process priority manager into the LRU pool when + * the associated ContentParent's priority changes. + */ + void Add(ParticularProcessPriorityManager* aParticularManager); + +private: + ProcessPriority mPriority; + uint32_t mLRUPoolLevels; + nsTArray<ParticularProcessPriorityManager*> mLRUPool; + + uint32_t CalculateLRULevel(uint32_t aLRUPoolIndex); + + void AdjustLRUValues( + nsTArray<ParticularProcessPriorityManager*>::index_type aStart, + bool removed); + + DISALLOW_EVIL_CONSTRUCTORS(ProcessLRUPool); +}; + +/** + * This singleton class does the work to implement the process priority manager + * in the main process. This class may not be used in child processes. (You + * can call StaticInit, but it won't do anything, and GetSingleton() will + * return null.) + * + * ProcessPriorityManager::CurrentProcessIsForeground() and + * ProcessPriorityManager::AnyProcessHasHighPriority() which can be called in + * any process, are handled separately, by the ProcessPriorityManagerChild + * class. + */ +class ProcessPriorityManagerImpl final + : public nsIObserver + , public WakeLockObserver + , public nsSupportsWeakReference +{ +public: + /** + * If we're in the main process, get the ProcessPriorityManagerImpl + * singleton. If we're in a child process, return null. + */ + static ProcessPriorityManagerImpl* GetSingleton(); + + static void StaticInit(); + static bool PrefsEnabled(); + static bool TestMode(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + /** + * This function implements ProcessPriorityManager::SetProcessPriority. + */ + void SetProcessPriority(ContentParent* aContentParent, + ProcessPriority aPriority, + uint32_t aLRU = 0); + + /** + * If a magic testing-only pref is set, notify the observer service on the + * given topic with the given data. This is used for testing + */ + void FireTestOnlyObserverNotification(const char* aTopic, + const nsACString& aData = EmptyCString()); + + /** + * Does one of the child processes have priority FOREGROUND_HIGH? + */ + bool ChildProcessHasHighPriority(); + + /** + * This must be called by a ParticularProcessPriorityManager when it changes + * its priority. + */ + void NotifyProcessPriorityChanged( + ParticularProcessPriorityManager* aParticularManager, + hal::ProcessPriority aOldPriority); + + /** + * Implements WakeLockObserver, used to monitor wake lock changes in the + * main process. + */ + virtual void Notify(const WakeLockInformation& aInfo) override; + + /** + * Prevents processes from changing priority until unfrozen. + */ + void Freeze(); + + /** + * Allow process' priorities to change again. This will immediately adjust + * processes whose priority change did not happen because of the freeze. + */ + void Unfreeze(); + + /** + * Call ShutDown before destroying the ProcessPriorityManager because + * WakeLockObserver hols a strong reference to it. + */ + void ShutDown(); + +private: + static bool sPrefsEnabled; + static bool sRemoteTabsDisabled; + static bool sTestMode; + static bool sPrefListenersRegistered; + static bool sInitialized; + static bool sFrozen; + static StaticRefPtr<ProcessPriorityManagerImpl> sSingleton; + + static void PrefChangedCallback(const char* aPref, void* aClosure); + + ProcessPriorityManagerImpl(); + ~ProcessPriorityManagerImpl(); + DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManagerImpl); + + void Init(); + + already_AddRefed<ParticularProcessPriorityManager> + GetParticularProcessPriorityManager(ContentParent* aContentParent); + + void ObserveContentParentCreated(nsISupports* aContentParent); + void ObserveContentParentDestroyed(nsISupports* aSubject); + void ObserveScreenStateChanged(const char16_t* aData); + + nsDataHashtable<nsUint64HashKey, RefPtr<ParticularProcessPriorityManager> > + mParticularManagers; + + /** True if the main process is holding a high-priority wakelock */ + bool mHighPriority; + + /** Contains the PIDs of child processes holding high-priority wakelocks */ + nsTHashtable<nsUint64HashKey> mHighPriorityChildIDs; + + /** Contains a pseudo-LRU list of background processes */ + ProcessLRUPool mBackgroundLRUPool; + + /** Contains a pseudo-LRU list of background-perceivable processes */ + ProcessLRUPool mBackgroundPerceivableLRUPool; +}; + +/** + * This singleton class implements the parts of the process priority manager + * that are available from all processes. + */ +class ProcessPriorityManagerChild final + : public nsIObserver +{ +public: + static void StaticInit(); + static ProcessPriorityManagerChild* Singleton(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + bool CurrentProcessIsForeground(); + bool CurrentProcessIsHighPriority(); + +private: + static StaticRefPtr<ProcessPriorityManagerChild> sSingleton; + + ProcessPriorityManagerChild(); + ~ProcessPriorityManagerChild() {} + DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManagerChild); + + void Init(); + + hal::ProcessPriority mCachedPriority; +}; + +/** + * This class manages the priority of one particular process. It is + * main-process only. + */ +class ParticularProcessPriorityManager final + : public WakeLockObserver + , public nsIObserver + , public nsITimerCallback + , public nsSupportsWeakReference +{ + ~ParticularProcessPriorityManager(); +public: + explicit ParticularProcessPriorityManager(ContentParent* aContentParent, + bool aFrozen = false); + + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + NS_DECL_NSITIMERCALLBACK + + virtual void Notify(const WakeLockInformation& aInfo) override; + static void StaticInit(); + void Init(); + + int32_t Pid() const; + uint64_t ChildID() const; + bool IsPreallocated() const; + + /** + * Used in logging, this method returns the ContentParent's name followed by + * ", ". If we can't get the ContentParent's name for some reason, it + * returns an empty string. + * + * The reference returned here is guaranteed to be live until the next call + * to NameWithComma() or until the ParticularProcessPriorityManager is + * destroyed, whichever comes first. + */ + const nsAutoCString& NameWithComma(); + + bool HasAppType(const char* aAppType); + bool IsExpectingSystemMessage(); + + void OnAudioChannelProcessChanged(nsISupports* aSubject); + void OnRemoteBrowserFrameShown(nsISupports* aSubject); + void OnTabParentDestroyed(nsISupports* aSubject); + void OnFrameloaderVisibleChanged(nsISupports* aSubject); + void OnActivityOpened(const char16_t* aData); + void OnActivityClosed(const char16_t* aData); + + ProcessPriority CurrentPriority(); + ProcessPriority ComputePriority(); + + enum TimeoutPref { + BACKGROUND_PERCEIVABLE_GRACE_PERIOD, + BACKGROUND_GRACE_PERIOD, + }; + + void ScheduleResetPriority(TimeoutPref aTimeoutPref); + void ResetPriority(); + void ResetPriorityNow(); + void SetPriorityNow(ProcessPriority aPriority, uint32_t aLRU = 0); + void Freeze(); + void Unfreeze(); + + void ShutDown(); + +private: + static uint32_t sBackgroundPerceivableGracePeriodMS; + static uint32_t sBackgroundGracePeriodMS; + + void FireTestOnlyObserverNotification( + const char* aTopic, + const nsACString& aData = EmptyCString()); + + void FireTestOnlyObserverNotification( + const char* aTopic, + const char* aData = nullptr); + + ContentParent* mContentParent; + uint64_t mChildID; + ProcessPriority mPriority; + uint32_t mLRU; + bool mHoldsCPUWakeLock; + bool mHoldsHighPriorityWakeLock; + bool mIsActivityOpener; + bool mFrozen; + + /** + * Used to implement NameWithComma(). + */ + nsAutoCString mNameWithComma; + + nsCOMPtr<nsITimer> mResetPriorityTimer; +}; + +/* static */ bool ProcessPriorityManagerImpl::sInitialized = false; +/* static */ bool ProcessPriorityManagerImpl::sPrefsEnabled = false; +/* static */ bool ProcessPriorityManagerImpl::sRemoteTabsDisabled = true; +/* static */ bool ProcessPriorityManagerImpl::sTestMode = false; +/* static */ bool ProcessPriorityManagerImpl::sPrefListenersRegistered = false; +/* static */ bool ProcessPriorityManagerImpl::sFrozen = false; +/* static */ StaticRefPtr<ProcessPriorityManagerImpl> + ProcessPriorityManagerImpl::sSingleton; +/* static */ uint32_t ParticularProcessPriorityManager::sBackgroundPerceivableGracePeriodMS = 0; +/* static */ uint32_t ParticularProcessPriorityManager::sBackgroundGracePeriodMS = 0; + +NS_IMPL_ISUPPORTS(ProcessPriorityManagerImpl, + nsIObserver, + nsISupportsWeakReference); + +/* static */ void +ProcessPriorityManagerImpl::PrefChangedCallback(const char* aPref, + void* aClosure) +{ + StaticInit(); + if (!PrefsEnabled() && sSingleton) { + sSingleton->ShutDown(); + sSingleton = nullptr; + sInitialized = false; + } +} + +/* static */ bool +ProcessPriorityManagerImpl::PrefsEnabled() +{ + return sPrefsEnabled && !sRemoteTabsDisabled; +} + +/* static */ bool +ProcessPriorityManagerImpl::TestMode() +{ + return sTestMode; +} + +/* static */ void +ProcessPriorityManagerImpl::StaticInit() +{ + if (sInitialized) { + return; + } + + // The process priority manager is main-process only. + if (!XRE_IsParentProcess()) { + sInitialized = true; + return; + } + + if (!sPrefListenersRegistered) { + Preferences::AddBoolVarCache(&sPrefsEnabled, + "dom.ipc.processPriorityManager.enabled"); + Preferences::AddBoolVarCache(&sRemoteTabsDisabled, + "dom.ipc.tabs.disabled"); + Preferences::AddBoolVarCache(&sTestMode, + "dom.ipc.processPriorityManager.testMode"); + } + + // If IPC tabs aren't enabled at startup, don't bother with any of this. + if (!PrefsEnabled()) { + LOG("InitProcessPriorityManager bailing due to prefs."); + + // Run StaticInit() again if the prefs change. We don't expect this to + // happen in normal operation, but it happens during testing. + if (!sPrefListenersRegistered) { + sPrefListenersRegistered = true; + Preferences::RegisterCallback(PrefChangedCallback, + "dom.ipc.processPriorityManager.enabled"); + Preferences::RegisterCallback(PrefChangedCallback, + "dom.ipc.tabs.disabled"); + } + return; + } + + sInitialized = true; + + sSingleton = new ProcessPriorityManagerImpl(); + sSingleton->Init(); + ClearOnShutdown(&sSingleton); +} + +/* static */ ProcessPriorityManagerImpl* +ProcessPriorityManagerImpl::GetSingleton() +{ + if (!sSingleton) { + StaticInit(); + } + + return sSingleton; +} + +ProcessPriorityManagerImpl::ProcessPriorityManagerImpl() + : mHighPriority(false) + , mBackgroundLRUPool(PROCESS_PRIORITY_BACKGROUND) + , mBackgroundPerceivableLRUPool(PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + RegisterWakeLockObserver(this); +} + +ProcessPriorityManagerImpl::~ProcessPriorityManagerImpl() +{ + ShutDown(); +} + +void +ProcessPriorityManagerImpl::ShutDown() +{ + UnregisterWakeLockObserver(this); +} + +void +ProcessPriorityManagerImpl::Init() +{ + LOG("Starting up. This is the master process."); + + // The master process's priority never changes; set it here and then forget + // about it. We'll manage only subprocesses' priorities using the process + // priority manager. + hal::SetProcessPriority(getpid(), PROCESS_PRIORITY_MASTER); + + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); + if (os) { + os->AddObserver(this, "ipc:content-created", /* ownsWeak */ true); + os->AddObserver(this, "ipc:content-shutdown", /* ownsWeak */ true); + os->AddObserver(this, "screen-state-changed", /* ownsWeak */ true); + } +} + +NS_IMETHODIMP +ProcessPriorityManagerImpl::Observe( + nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + nsDependentCString topic(aTopic); + if (topic.EqualsLiteral("ipc:content-created")) { + ObserveContentParentCreated(aSubject); + } else if (topic.EqualsLiteral("ipc:content-shutdown")) { + ObserveContentParentDestroyed(aSubject); + } else if (topic.EqualsLiteral("screen-state-changed")) { + ObserveScreenStateChanged(aData); + } else { + MOZ_ASSERT(false); + } + + return NS_OK; +} + +already_AddRefed<ParticularProcessPriorityManager> +ProcessPriorityManagerImpl::GetParticularProcessPriorityManager( + ContentParent* aContentParent) +{ + RefPtr<ParticularProcessPriorityManager> pppm; + uint64_t cpId = aContentParent->ChildID(); + mParticularManagers.Get(cpId, &pppm); + if (!pppm) { + pppm = new ParticularProcessPriorityManager(aContentParent, sFrozen); + pppm->Init(); + mParticularManagers.Put(cpId, pppm); + + FireTestOnlyObserverNotification("process-created", + nsPrintfCString("%lld", cpId)); + } + + return pppm.forget(); +} + +void +ProcessPriorityManagerImpl::SetProcessPriority(ContentParent* aContentParent, + ProcessPriority aPriority, + uint32_t aLRU) +{ + MOZ_ASSERT(aContentParent); + RefPtr<ParticularProcessPriorityManager> pppm = + GetParticularProcessPriorityManager(aContentParent); + if (pppm) { + pppm->SetPriorityNow(aPriority, aLRU); + } +} + +void +ProcessPriorityManagerImpl::ObserveContentParentCreated( + nsISupports* aContentParent) +{ + // Do nothing; it's sufficient to get the PPPM. But assign to nsRefPtr so we + // don't leak the already_AddRefed object. + nsCOMPtr<nsIContentParent> cp = do_QueryInterface(aContentParent); + RefPtr<ParticularProcessPriorityManager> pppm = + GetParticularProcessPriorityManager(cp->AsContentParent()); +} + +void +ProcessPriorityManagerImpl::ObserveContentParentDestroyed(nsISupports* aSubject) +{ + nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject); + NS_ENSURE_TRUE_VOID(props); + + uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN; + props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID); + NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN); + + RefPtr<ParticularProcessPriorityManager> pppm; + mParticularManagers.Get(childID, &pppm); + if (pppm) { + // Unconditionally remove the manager from the pools + mBackgroundLRUPool.Remove(pppm); + mBackgroundPerceivableLRUPool.Remove(pppm); + + pppm->ShutDown(); + + mParticularManagers.Remove(childID); + + mHighPriorityChildIDs.RemoveEntry(childID); + } +} + +void +ProcessPriorityManagerImpl::ObserveScreenStateChanged(const char16_t* aData) +{ + if (NS_LITERAL_STRING("on").Equals(aData)) { + sFrozen = false; + for (auto iter = mParticularManagers.Iter(); !iter.Done(); iter.Next()) { + iter.UserData()->Unfreeze(); + } + } else { + sFrozen = true; + for (auto iter = mParticularManagers.Iter(); !iter.Done(); iter.Next()) { + iter.UserData()->Freeze(); + } + } +} + +bool +ProcessPriorityManagerImpl::ChildProcessHasHighPriority( void ) +{ + return mHighPriorityChildIDs.Count() > 0; +} + +void +ProcessPriorityManagerImpl::NotifyProcessPriorityChanged( + ParticularProcessPriorityManager* aParticularManager, + ProcessPriority aOldPriority) +{ + ProcessPriority newPriority = aParticularManager->CurrentPriority(); + bool isPreallocated = aParticularManager->IsPreallocated(); + + if (newPriority == PROCESS_PRIORITY_BACKGROUND && + aOldPriority != PROCESS_PRIORITY_BACKGROUND && + !isPreallocated) { + mBackgroundLRUPool.Add(aParticularManager); + } else if (newPriority != PROCESS_PRIORITY_BACKGROUND && + aOldPriority == PROCESS_PRIORITY_BACKGROUND && + !isPreallocated) { + mBackgroundLRUPool.Remove(aParticularManager); + } + + if (newPriority == PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE && + aOldPriority != PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) { + mBackgroundPerceivableLRUPool.Add(aParticularManager); + } else if (newPriority != PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE && + aOldPriority == PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) { + mBackgroundPerceivableLRUPool.Remove(aParticularManager); + } + + if (newPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH && + aOldPriority < PROCESS_PRIORITY_FOREGROUND_HIGH) { + mHighPriorityChildIDs.PutEntry(aParticularManager->ChildID()); + } else if (newPriority < PROCESS_PRIORITY_FOREGROUND_HIGH && + aOldPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH) { + mHighPriorityChildIDs.RemoveEntry(aParticularManager->ChildID()); + } +} + +/* virtual */ void +ProcessPriorityManagerImpl::Notify(const WakeLockInformation& aInfo) +{ + /* The main process always has an ID of 0, if it is present in the wake-lock + * information then we explicitly requested a high-priority wake-lock for the + * main process. */ + if (aInfo.topic().EqualsLiteral("high-priority")) { + if (aInfo.lockingProcesses().Contains((uint64_t)0)) { + mHighPriority = true; + } else { + mHighPriority = false; + } + + LOG("Got wake lock changed event. " + "Now mHighPriorityParent = %d\n", mHighPriority); + } +} + +NS_IMPL_ISUPPORTS(ParticularProcessPriorityManager, + nsIObserver, + nsITimerCallback, + nsISupportsWeakReference); + +ParticularProcessPriorityManager::ParticularProcessPriorityManager( + ContentParent* aContentParent, bool aFrozen) + : mContentParent(aContentParent) + , mChildID(aContentParent->ChildID()) + , mPriority(PROCESS_PRIORITY_UNKNOWN) + , mLRU(0) + , mHoldsCPUWakeLock(false) + , mHoldsHighPriorityWakeLock(false) + , mIsActivityOpener(false) + , mFrozen(aFrozen) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + LOGP("Creating ParticularProcessPriorityManager."); +} + +void +ParticularProcessPriorityManager::StaticInit() +{ + Preferences::AddUintVarCache(&sBackgroundPerceivableGracePeriodMS, + "dom.ipc.processPriorityManager.backgroundPerceivableGracePeriodMS"); + Preferences::AddUintVarCache(&sBackgroundGracePeriodMS, + "dom.ipc.processPriorityManager.backgroundGracePeriodMS"); +} + +void +ParticularProcessPriorityManager::Init() +{ + RegisterWakeLockObserver(this); + + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); + if (os) { + os->AddObserver(this, "audio-channel-process-changed", /* ownsWeak */ true); + os->AddObserver(this, "remote-browser-shown", /* ownsWeak */ true); + os->AddObserver(this, "ipc:browser-destroyed", /* ownsWeak */ true); + os->AddObserver(this, "frameloader-visible-changed", /* ownsWeak */ true); + os->AddObserver(this, "activity-opened", /* ownsWeak */ true); + os->AddObserver(this, "activity-closed", /* ownsWeak */ true); + } + + // This process may already hold the CPU lock; for example, our parent may + // have acquired it on our behalf. + WakeLockInformation info1, info2; + GetWakeLockInfo(NS_LITERAL_STRING("cpu"), &info1); + mHoldsCPUWakeLock = info1.lockingProcesses().Contains(ChildID()); + + GetWakeLockInfo(NS_LITERAL_STRING("high-priority"), &info2); + mHoldsHighPriorityWakeLock = info2.lockingProcesses().Contains(ChildID()); + LOGP("Done starting up. mHoldsCPUWakeLock=%d, mHoldsHighPriorityWakeLock=%d", + mHoldsCPUWakeLock, mHoldsHighPriorityWakeLock); +} + +ParticularProcessPriorityManager::~ParticularProcessPriorityManager() +{ + LOGP("Destroying ParticularProcessPriorityManager."); + + // Unregister our wake lock observer if ShutDown hasn't been called. (The + // wake lock observer takes raw refs, so we don't want to take chances here!) + // We don't call UnregisterWakeLockObserver unconditionally because the code + // will print a warning if it's called unnecessarily. + + if (mContentParent) { + UnregisterWakeLockObserver(this); + } +} + +/* virtual */ void +ParticularProcessPriorityManager::Notify(const WakeLockInformation& aInfo) +{ + if (!mContentParent) { + // We've been shut down. + return; + } + + bool* dest = nullptr; + if (aInfo.topic().EqualsLiteral("cpu")) { + dest = &mHoldsCPUWakeLock; + } else if (aInfo.topic().EqualsLiteral("high-priority")) { + dest = &mHoldsHighPriorityWakeLock; + } + + if (dest) { + bool thisProcessLocks = aInfo.lockingProcesses().Contains(ChildID()); + if (thisProcessLocks != *dest) { + *dest = thisProcessLocks; + LOGP("Got wake lock changed event. " + "Now mHoldsCPUWakeLock=%d, mHoldsHighPriorityWakeLock=%d", + mHoldsCPUWakeLock, mHoldsHighPriorityWakeLock); + ResetPriority(); + } + } +} + +NS_IMETHODIMP +ParticularProcessPriorityManager::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + if (!mContentParent) { + // We've been shut down. + return NS_OK; + } + + nsDependentCString topic(aTopic); + + if (topic.EqualsLiteral("audio-channel-process-changed")) { + OnAudioChannelProcessChanged(aSubject); + } else if (topic.EqualsLiteral("remote-browser-shown")) { + OnRemoteBrowserFrameShown(aSubject); + } else if (topic.EqualsLiteral("ipc:browser-destroyed")) { + OnTabParentDestroyed(aSubject); + } else if (topic.EqualsLiteral("frameloader-visible-changed")) { + OnFrameloaderVisibleChanged(aSubject); + } else if (topic.EqualsLiteral("activity-opened")) { + OnActivityOpened(aData); + } else if (topic.EqualsLiteral("activity-closed")) { + OnActivityClosed(aData); + } else { + MOZ_ASSERT(false); + } + + return NS_OK; +} + +uint64_t +ParticularProcessPriorityManager::ChildID() const +{ + // We have to cache mContentParent->ChildID() instead of getting it from the + // ContentParent each time because after ShutDown() is called, mContentParent + // is null. If we didn't cache ChildID(), then we wouldn't be able to run + // LOGP() after ShutDown(). + return mChildID; +} + +int32_t +ParticularProcessPriorityManager::Pid() const +{ + return mContentParent ? mContentParent->Pid() : -1; +} + +bool +ParticularProcessPriorityManager::IsPreallocated() const +{ + return mContentParent ? mContentParent->IsPreallocated() : false; +} + +const nsAutoCString& +ParticularProcessPriorityManager::NameWithComma() +{ + mNameWithComma.Truncate(); + if (!mContentParent) { + return mNameWithComma; // empty string + } + + nsAutoString name; + mContentParent->FriendlyName(name); + if (name.IsEmpty()) { + return mNameWithComma; // empty string + } + + mNameWithComma = NS_ConvertUTF16toUTF8(name); + mNameWithComma.AppendLiteral(", "); + return mNameWithComma; +} + +void +ParticularProcessPriorityManager::OnAudioChannelProcessChanged(nsISupports* aSubject) +{ + nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject); + NS_ENSURE_TRUE_VOID(props); + + uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN; + props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID); + if (childID == ChildID()) { + ResetPriority(); + } +} + +void +ParticularProcessPriorityManager::OnRemoteBrowserFrameShown(nsISupports* aSubject) +{ + nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject); + NS_ENSURE_TRUE_VOID(fl); + + TabParent* tp = TabParent::GetFrom(fl); + NS_ENSURE_TRUE_VOID(tp); + + MOZ_ASSERT(XRE_IsParentProcess()); + if (tp->Manager() != mContentParent) { + return; + } + + // Ignore notifications that aren't from a BrowserOrApp + bool isMozBrowserOrApp; + fl->GetOwnerIsMozBrowserOrAppFrame(&isMozBrowserOrApp); + if (isMozBrowserOrApp) { + ResetPriority(); + } + + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); + if (os) { + os->RemoveObserver(this, "remote-browser-shown"); + } +} + +void +ParticularProcessPriorityManager::OnTabParentDestroyed(nsISupports* aSubject) +{ + nsCOMPtr<nsITabParent> tp = do_QueryInterface(aSubject); + NS_ENSURE_TRUE_VOID(tp); + + MOZ_ASSERT(XRE_IsParentProcess()); + if (TabParent::GetFrom(tp)->Manager() != mContentParent) { + return; + } + + ResetPriority(); +} + +void +ParticularProcessPriorityManager::OnFrameloaderVisibleChanged(nsISupports* aSubject) +{ + nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject); + NS_ENSURE_TRUE_VOID(fl); + + if (mFrozen) { + return; // Ignore visibility changes when the screen is off + } + + TabParent* tp = TabParent::GetFrom(fl); + if (!tp) { + return; + } + + MOZ_ASSERT(XRE_IsParentProcess()); + if (tp->Manager() != mContentParent) { + return; + } + + // Most of the time when something changes in a process we call + // ResetPriority(), giving a grace period before downgrading its priority. + // But notice that here don't give a grace period: We call ResetPriorityNow() + // instead. + // + // We do this because we're reacting here to a setVisibility() call, which is + // an explicit signal from the process embedder that we should re-prioritize + // a process. If we gave a grace period in response to setVisibility() + // calls, it would be impossible for the embedder to explicitly prioritize + // processes and prevent e.g. the case where we switch which process is in + // the foreground and, during the old fg processs's grace period, it OOMs the + // new fg process. + + ResetPriorityNow(); +} + +void +ParticularProcessPriorityManager::OnActivityOpened(const char16_t* aData) +{ + uint64_t childID = nsCRT::atoll(NS_ConvertUTF16toUTF8(aData).get()); + + if (ChildID() == childID) { + LOGP("Marking as activity opener"); + mIsActivityOpener = true; + ResetPriority(); + } +} + +void +ParticularProcessPriorityManager::OnActivityClosed(const char16_t* aData) +{ + uint64_t childID = nsCRT::atoll(NS_ConvertUTF16toUTF8(aData).get()); + + if (ChildID() == childID) { + LOGP("Unmarking as activity opener"); + mIsActivityOpener = false; + ResetPriority(); + } +} + +void +ParticularProcessPriorityManager::ResetPriority() +{ + ProcessPriority processPriority = ComputePriority(); + if (mPriority == PROCESS_PRIORITY_UNKNOWN || + mPriority > processPriority) { + // Apps set at a perceivable background priority are often playing media. + // Most media will have short gaps while changing tracks between songs, + // switching videos, etc. Give these apps a longer grace period so they + // can get their next track started, if there is one, before getting + // downgraded. + if (mPriority == PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) { + ScheduleResetPriority(BACKGROUND_PERCEIVABLE_GRACE_PERIOD); + } else { + ScheduleResetPriority(BACKGROUND_GRACE_PERIOD); + } + return; + } + + SetPriorityNow(processPriority); +} + +void +ParticularProcessPriorityManager::ResetPriorityNow() +{ + SetPriorityNow(ComputePriority()); +} + +void +ParticularProcessPriorityManager::ScheduleResetPriority(TimeoutPref aTimeoutPref) +{ + if (mResetPriorityTimer) { + LOGP("ScheduleResetPriority bailing; the timer is already running."); + return; + } + + uint32_t timeout = 0; + switch (aTimeoutPref) { + case BACKGROUND_PERCEIVABLE_GRACE_PERIOD: + timeout = sBackgroundPerceivableGracePeriodMS; + break; + case BACKGROUND_GRACE_PERIOD: + timeout = sBackgroundGracePeriodMS; + break; + default: + MOZ_ASSERT(false, "Unrecognized timeout pref"); + break; + } + + LOGP("Scheduling reset timer to fire in %dms.", timeout); + mResetPriorityTimer = do_CreateInstance(NS_TIMER_CONTRACTID); + mResetPriorityTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT); +} + +NS_IMETHODIMP +ParticularProcessPriorityManager::Notify(nsITimer* aTimer) +{ + LOGP("Reset priority timer callback; about to ResetPriorityNow."); + ResetPriorityNow(); + mResetPriorityTimer = nullptr; + return NS_OK; +} + +bool +ParticularProcessPriorityManager::HasAppType(const char* aAppType) +{ + const ManagedContainer<PBrowserParent>& browsers = + mContentParent->ManagedPBrowserParent(); + for (auto iter = browsers.ConstIter(); !iter.Done(); iter.Next()) { + nsAutoString appType; + TabParent::GetFrom(iter.Get()->GetKey())->GetAppType(appType); + if (appType.EqualsASCII(aAppType)) { + return true; + } + } + + return false; +} + +bool +ParticularProcessPriorityManager::IsExpectingSystemMessage() +{ + const ManagedContainer<PBrowserParent>& browsers = + mContentParent->ManagedPBrowserParent(); + for (auto iter = browsers.ConstIter(); !iter.Done(); iter.Next()) { + TabParent* tp = TabParent::GetFrom(iter.Get()->GetKey()); + nsCOMPtr<nsIMozBrowserFrame> bf = do_QueryInterface(tp->GetOwnerElement()); + if (!bf) { + continue; + } + } + + return false; +} + +ProcessPriority +ParticularProcessPriorityManager::CurrentPriority() +{ + return mPriority; +} + +ProcessPriority +ParticularProcessPriorityManager::ComputePriority() +{ + if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) && + HasAppType("critical")) { + return PROCESS_PRIORITY_FOREGROUND_HIGH; + } + + bool isVisible = false; + const ManagedContainer<PBrowserParent>& browsers = + mContentParent->ManagedPBrowserParent(); + for (auto iter = browsers.ConstIter(); !iter.Done(); iter.Next()) { + if (TabParent::GetFrom(iter.Get()->GetKey())->IsVisible()) { + isVisible = true; + break; + } + } + + if (isVisible) { + return HasAppType("inputmethod") ? + PROCESS_PRIORITY_FOREGROUND_KEYBOARD : + PROCESS_PRIORITY_FOREGROUND; + } + + if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) && + IsExpectingSystemMessage()) { + return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE; + } + + RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate(); + if (service && service->ProcessContentOrNormalChannelIsActive(ChildID())) { + return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE; + } + + return mIsActivityOpener ? PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE + : PROCESS_PRIORITY_BACKGROUND; +} + +void +ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority, + uint32_t aLRU) +{ + if (aPriority == PROCESS_PRIORITY_UNKNOWN) { + MOZ_ASSERT(false); + return; + } + + if (!ProcessPriorityManagerImpl::PrefsEnabled() || + !mContentParent || + mFrozen || + ((mPriority == aPriority) && (mLRU == aLRU))) { + return; + } + + if ((mPriority == aPriority) && (mLRU != aLRU)) { + mLRU = aLRU; + hal::SetProcessPriority(Pid(), mPriority, aLRU); + + nsPrintfCString processPriorityWithLRU("%s:%d", + ProcessPriorityToString(mPriority), aLRU); + + FireTestOnlyObserverNotification("process-priority-with-LRU-set", + processPriorityWithLRU.get()); + return; + } + + LOGP("Changing priority from %s to %s.", + ProcessPriorityToString(mPriority), + ProcessPriorityToString(aPriority)); + + ProcessPriority oldPriority = mPriority; + + mPriority = aPriority; + hal::SetProcessPriority(Pid(), mPriority); + + if (oldPriority != mPriority) { + ProcessPriorityManagerImpl::GetSingleton()-> + NotifyProcessPriorityChanged(this, oldPriority); + + Unused << mContentParent->SendNotifyProcessPriorityChanged(mPriority); + } + + FireTestOnlyObserverNotification("process-priority-set", + ProcessPriorityToString(mPriority)); +} + +void +ParticularProcessPriorityManager::Freeze() +{ + mFrozen = true; +} + +void +ParticularProcessPriorityManager::Unfreeze() +{ + mFrozen = false; +} + +void +ParticularProcessPriorityManager::ShutDown() +{ + MOZ_ASSERT(mContentParent); + + UnregisterWakeLockObserver(this); + + if (mResetPriorityTimer) { + mResetPriorityTimer->Cancel(); + mResetPriorityTimer = nullptr; + } + + mContentParent = nullptr; +} + +void +ProcessPriorityManagerImpl::FireTestOnlyObserverNotification( + const char* aTopic, + const nsACString& aData /* = EmptyCString() */) +{ + if (!TestMode()) { + return; + } + + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); + NS_ENSURE_TRUE_VOID(os); + + nsPrintfCString topic("process-priority-manager:TEST-ONLY:%s", aTopic); + + LOG("Notifying observer %s, data %s", + topic.get(), PromiseFlatCString(aData).get()); + os->NotifyObservers(nullptr, topic.get(), NS_ConvertUTF8toUTF16(aData).get()); +} + +void +ParticularProcessPriorityManager::FireTestOnlyObserverNotification( + const char* aTopic, + const char* aData /* = nullptr */ ) +{ + if (!ProcessPriorityManagerImpl::TestMode()) { + return; + } + + nsAutoCString data; + if (aData) { + data.AppendASCII(aData); + } + + FireTestOnlyObserverNotification(aTopic, data); +} + +void +ParticularProcessPriorityManager::FireTestOnlyObserverNotification( + const char* aTopic, + const nsACString& aData /* = EmptyCString() */) +{ + if (!ProcessPriorityManagerImpl::TestMode()) { + return; + } + + nsAutoCString data(nsPrintfCString("%lld", ChildID())); + if (!aData.IsEmpty()) { + data.Append(':'); + data.Append(aData); + } + + // ProcessPriorityManagerImpl::GetSingleton() is guaranteed not to return + // null, since ProcessPriorityManagerImpl is the only class which creates + // ParticularProcessPriorityManagers. + + ProcessPriorityManagerImpl::GetSingleton()-> + FireTestOnlyObserverNotification(aTopic, data); +} + +StaticRefPtr<ProcessPriorityManagerChild> +ProcessPriorityManagerChild::sSingleton; + +/* static */ void +ProcessPriorityManagerChild::StaticInit() +{ + if (!sSingleton) { + sSingleton = new ProcessPriorityManagerChild(); + sSingleton->Init(); + ClearOnShutdown(&sSingleton); + } +} + +/* static */ ProcessPriorityManagerChild* +ProcessPriorityManagerChild::Singleton() +{ + StaticInit(); + return sSingleton; +} + +NS_IMPL_ISUPPORTS(ProcessPriorityManagerChild, + nsIObserver) + +ProcessPriorityManagerChild::ProcessPriorityManagerChild() +{ + if (XRE_IsParentProcess()) { + mCachedPriority = PROCESS_PRIORITY_MASTER; + } else { + mCachedPriority = PROCESS_PRIORITY_UNKNOWN; + } +} + +void +ProcessPriorityManagerChild::Init() +{ + // The process priority should only be changed in child processes; don't even + // bother listening for changes if we're in the main process. + if (!XRE_IsParentProcess()) { + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); + NS_ENSURE_TRUE_VOID(os); + os->AddObserver(this, "ipc:process-priority-changed", /* weak = */ false); + } +} + +NS_IMETHODIMP +ProcessPriorityManagerChild::Observe( + nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + MOZ_ASSERT(!strcmp(aTopic, "ipc:process-priority-changed")); + + nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject); + NS_ENSURE_TRUE(props, NS_OK); + + int32_t priority = static_cast<int32_t>(PROCESS_PRIORITY_UNKNOWN); + props->GetPropertyAsInt32(NS_LITERAL_STRING("priority"), &priority); + NS_ENSURE_TRUE(ProcessPriority(priority) != PROCESS_PRIORITY_UNKNOWN, NS_OK); + + mCachedPriority = static_cast<ProcessPriority>(priority); + + return NS_OK; +} + +bool +ProcessPriorityManagerChild::CurrentProcessIsForeground() +{ + return mCachedPriority == PROCESS_PRIORITY_UNKNOWN || + mCachedPriority >= PROCESS_PRIORITY_FOREGROUND; +} + +bool +ProcessPriorityManagerChild::CurrentProcessIsHighPriority() +{ + return mCachedPriority == PROCESS_PRIORITY_UNKNOWN || + mCachedPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH; +} + +ProcessLRUPool::ProcessLRUPool(ProcessPriority aPriority) + : mPriority(aPriority) + , mLRUPoolLevels(1) +{ + // We set mLRUPoolLevels according to our pref. + // This value is used to set background process LRU pool + const char* str = ProcessPriorityToString(aPriority); + nsPrintfCString pref("dom.ipc.processPriorityManager.%s.LRUPoolLevels", str); + + Preferences::GetUint(pref.get(), &mLRUPoolLevels); + + // GonkHal defines OOM_ADJUST_MAX is 15 and b2g.js defines + // PROCESS_PRIORITY_BACKGROUND's oom_score_adj is 667 and oom_adj is 10. + // This means we can only have at most (15 -10 + 1) = 6 background LRU levels. + // Similarly we can have at most 4 background perceivable LRU levels. We + // should really be getting rid of oom_adj and just rely on oom_score_adj + // only which would lift this constraint. + MOZ_ASSERT(aPriority != PROCESS_PRIORITY_BACKGROUND || mLRUPoolLevels <= 6); + MOZ_ASSERT(aPriority != PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE || + mLRUPoolLevels <= 4); + + // LRU pool size = 2 ^ (number of background LRU pool levels) - 1 + uint32_t LRUPoolSize = (1 << mLRUPoolLevels) - 1; + + LOG("Making %s LRU pool with size(%d)", str, LRUPoolSize); +} + +uint32_t +ProcessLRUPool::CalculateLRULevel(uint32_t aLRU) +{ + // This is used to compute the LRU adjustment for the specified LRU position. + // We use power-of-two groups with increasing adjustments that look like the + // following: + + // Priority : LRU0, LRU1 + // Priority+1: LRU2, LRU3 + // Priority+2: LRU4, LRU5, LRU6, LRU7 + // Priority+3: LRU8, LRU9, LRU10, LRU11, LRU12, LRU12, LRU13, LRU14, LRU15 + // ... + // Priority+L-1: 2^(number of LRU pool levels - 1) + // (End of buffer) + + int exp; + Unused << frexp(static_cast<double>(aLRU), &exp); + uint32_t level = std::max(exp - 1, 0); + + return std::min(mLRUPoolLevels - 1, level); +} + +void +ProcessLRUPool::Remove(ParticularProcessPriorityManager* aParticularManager) +{ + nsTArray<ParticularProcessPriorityManager*>::index_type index = + mLRUPool.IndexOf(aParticularManager); + + if (index == nsTArray<ParticularProcessPriorityManager*>::NoIndex) { + return; + } + + mLRUPool.RemoveElementAt(index); + AdjustLRUValues(index, /* removed */ true); + + LOG("Remove ChildID(%" PRIu64 ") from %s LRU pool", + static_cast<uint64_t>(aParticularManager->ChildID()), + ProcessPriorityToString(mPriority)); +} + +/* + * Adjust the LRU values of all the processes in an LRU pool. When true the + * `removed` parameter indicates that the processes were shifted left because + * an element was removed; otherwise it means the elements were shifted right + * as an element was added. + */ +void +ProcessLRUPool::AdjustLRUValues( + nsTArray<ParticularProcessPriorityManager*>::index_type aStart, + bool removed) +{ + uint32_t adj = (removed ? 2 : 1); + + for (nsTArray<ParticularProcessPriorityManager*>::index_type i = aStart; + i < mLRUPool.Length(); + i++) { + /* Check whether i is a power of two. If so, then it crossed a LRU group + * boundary and we need to assign its new process priority LRU. Note that + * depending on the direction and the bias this test will pick different + * elements. */ + if (((i + adj) & (i + adj - 1)) == 0) { + mLRUPool[i]->SetPriorityNow(mPriority, CalculateLRULevel(i + 1)); + } + } +} + +void +ProcessLRUPool::Add(ParticularProcessPriorityManager* aParticularManager) +{ + // Shift the list in the pool, so we have room at index 0 for the newly added + // manager + mLRUPool.InsertElementAt(0, aParticularManager); + AdjustLRUValues(1, /* removed */ false); + + LOG("Add ChildID(%" PRIu64 ") into %s LRU pool", + static_cast<uint64_t>(aParticularManager->ChildID()), + ProcessPriorityToString(mPriority)); +} + +} // namespace + +namespace mozilla { + +/* static */ void +ProcessPriorityManager::Init() +{ + ProcessPriorityManagerImpl::StaticInit(); + ProcessPriorityManagerChild::StaticInit(); + ParticularProcessPriorityManager::StaticInit(); +} + +/* static */ void +ProcessPriorityManager::SetProcessPriority(ContentParent* aContentParent, + ProcessPriority aPriority) +{ + MOZ_ASSERT(aContentParent); + + ProcessPriorityManagerImpl* singleton = + ProcessPriorityManagerImpl::GetSingleton(); + if (singleton) { + singleton->SetProcessPriority(aContentParent, aPriority); + } +} + +/* static */ bool +ProcessPriorityManager::CurrentProcessIsForeground() +{ + return ProcessPriorityManagerChild::Singleton()-> + CurrentProcessIsForeground(); +} + +/* static */ bool +ProcessPriorityManager::AnyProcessHasHighPriority() +{ + ProcessPriorityManagerImpl* singleton = + ProcessPriorityManagerImpl::GetSingleton(); + + if (singleton) { + return singleton->ChildProcessHasHighPriority(); + } else { + return ProcessPriorityManagerChild::Singleton()-> + CurrentProcessIsHighPriority(); + } +} + +} // namespace mozilla diff --git a/dom/ipc/ProcessPriorityManager.h b/dom/ipc/ProcessPriorityManager.h new file mode 100644 index 000000000..5e02c9c32 --- /dev/null +++ b/dom/ipc/ProcessPriorityManager.h @@ -0,0 +1,84 @@ +/* -*- 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/. */ + +#ifndef mozilla_ProcessPriorityManager_h_ +#define mozilla_ProcessPriorityManager_h_ + +#include "mozilla/HalTypes.h" + +namespace mozilla { +namespace dom { +class ContentParent; +} // namespace dom + +/** + * This class sets the priority of subprocesses in response to explicit + * requests and events in the system. + * + * A process's priority changes e.g. when it goes into the background via + * mozbrowser's setVisible(false). Process priority affects CPU scheduling and + * also which processes get killed when we run out of memory. + * + * After you call Initialize(), the only thing you probably have to do is call + * SetProcessPriority on processes immediately after creating them in order to + * set their initial priority. The ProcessPriorityManager takes care of the + * rest. + */ +class ProcessPriorityManager final +{ +public: + /** + * Initialize the ProcessPriorityManager machinery, causing the + * ProcessPriorityManager to actively manage the priorities of all + * subprocesses. You should call this before creating any subprocesses. + * + * You should also call this function even if you're in a child process, + * since it will initialize ProcessPriorityManagerChild. + */ + static void Init(); + + /** + * Set the process priority of a given ContentParent's process. + * + * Note that because this method takes a ContentParent*, you can only set the + * priority of your subprocesses. In fact, because we don't support nested + * content processes (bug 761935), you can only call this method from the + * main process. + * + * It probably only makes sense to call this function immediately after a + * process is created. At this point, the process priority manager doesn't + * have enough context about the processs to know what its priority should + * be. + * + * Eventually whatever priority you set here can and probably will be + * overwritten by the process priority manager. + */ + static void SetProcessPriority(dom::ContentParent* aContentParent, + hal::ProcessPriority aPriority); + + /** + * Returns true iff this process's priority is FOREGROUND*. + * + * Note that because process priorities are set in the main process, it's + * possible for this method to return a stale value. So be careful about + * what you use this for. + */ + static bool CurrentProcessIsForeground(); + + /** + * Returns true if one or more processes with FOREGROUND_HIGH priority are + * present, false otherwise. + */ + static bool AnyProcessHasHighPriority(); + +private: + ProcessPriorityManager(); + DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManager); +}; + +} // namespace mozilla + +#endif diff --git a/dom/ipc/ScreenManagerParent.cpp b/dom/ipc/ScreenManagerParent.cpp new file mode 100644 index 000000000..ddc2eb4a2 --- /dev/null +++ b/dom/ipc/ScreenManagerParent.cpp @@ -0,0 +1,229 @@ +/* -*- 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/dom/ContentParent.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/Unused.h" +#include "nsIWidget.h" +#include "nsServiceManagerUtils.h" +#include "ScreenManagerParent.h" +#include "ContentProcessManager.h" + +namespace mozilla { +namespace dom { + +static const char *sScreenManagerContractID = "@mozilla.org/gfx/screenmanager;1"; + +ScreenManagerParent::ScreenManagerParent(uint32_t* aNumberOfScreens, + float* aSystemDefaultScale, + bool* aSuccess) +{ + mScreenMgr = do_GetService(sScreenManagerContractID); + if (!mScreenMgr) { + MOZ_CRASH("Couldn't get nsIScreenManager from ScreenManagerParent."); + } + + Unused << RecvRefresh(aNumberOfScreens, aSystemDefaultScale, aSuccess); +} + +bool +ScreenManagerParent::RecvRefresh(uint32_t* aNumberOfScreens, + float* aSystemDefaultScale, + bool* aSuccess) +{ + *aSuccess = false; + + nsresult rv = mScreenMgr->GetNumberOfScreens(aNumberOfScreens); + if (NS_FAILED(rv)) { + return true; + } + + rv = mScreenMgr->GetSystemDefaultScale(aSystemDefaultScale); + if (NS_FAILED(rv)) { + return true; + } + + *aSuccess = true; + return true; +} + +bool +ScreenManagerParent::RecvScreenRefresh(const uint32_t& aId, + ScreenDetails* aRetVal, + bool* aSuccess) +{ + *aSuccess = false; + + nsCOMPtr<nsIScreen> screen; + nsresult rv = mScreenMgr->ScreenForId(aId, getter_AddRefs(screen)); + if (NS_FAILED(rv)) { + return true; + } + + ScreenDetails details; + Unused << ExtractScreenDetails(screen, details); + + *aRetVal = details; + *aSuccess = true; + return true; +} + +bool +ScreenManagerParent::RecvGetPrimaryScreen(ScreenDetails* aRetVal, + bool* aSuccess) +{ + *aSuccess = false; + + nsCOMPtr<nsIScreen> screen; + nsresult rv = mScreenMgr->GetPrimaryScreen(getter_AddRefs(screen)); + + NS_ENSURE_SUCCESS(rv, true); + + ScreenDetails details; + if (!ExtractScreenDetails(screen, details)) { + return true; + } + + *aRetVal = details; + *aSuccess = true; + return true; +} + +bool +ScreenManagerParent::RecvScreenForRect(const int32_t& aLeft, + const int32_t& aTop, + const int32_t& aWidth, + const int32_t& aHeight, + ScreenDetails* aRetVal, + bool* aSuccess) +{ + *aSuccess = false; + + nsCOMPtr<nsIScreen> screen; + nsresult rv = mScreenMgr->ScreenForRect(aLeft, aTop, aWidth, aHeight, getter_AddRefs(screen)); + + NS_ENSURE_SUCCESS(rv, true); + + ScreenDetails details; + if (!ExtractScreenDetails(screen, details)) { + return true; + } + + *aRetVal = details; + *aSuccess = true; + return true; +} + +bool +ScreenManagerParent::RecvScreenForBrowser(const TabId& aTabId, + ScreenDetails* aRetVal, + bool* aSuccess) +{ + *aSuccess = false; +#ifdef MOZ_VALGRIND + // Zero this so that Valgrind doesn't complain when we send it to another + // process. + memset(aRetVal, 0, sizeof(ScreenDetails)); +#endif + + // Find the mWidget associated with the tabparent, and then return + // the nsIScreen it's on. + ContentParent* cp = static_cast<ContentParent*>(this->Manager()); + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + RefPtr<TabParent> tabParent = + cpm->GetTopLevelTabParentByProcessAndTabId(cp->ChildID(), aTabId); + if(!tabParent){ + return false; + } + + nsCOMPtr<nsIWidget> widget = tabParent->GetWidget(); + + nsCOMPtr<nsIScreen> screen; + if (widget && widget->GetNativeData(NS_NATIVE_WINDOW)) { + mScreenMgr->ScreenForNativeWidget(widget->GetNativeData(NS_NATIVE_WINDOW), + getter_AddRefs(screen)); + } else { + nsresult rv = mScreenMgr->GetPrimaryScreen(getter_AddRefs(screen)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return true; + } + } + + NS_ENSURE_TRUE(screen, true); + + ScreenDetails details; + if (!ExtractScreenDetails(screen, details)) { + return true; + } + + *aRetVal = details; + *aSuccess = true; + return true; +} + +bool +ScreenManagerParent::ExtractScreenDetails(nsIScreen* aScreen, ScreenDetails &aDetails) +{ + if (!aScreen) { + return false; + } + + uint32_t id; + nsresult rv = aScreen->GetId(&id); + NS_ENSURE_SUCCESS(rv, false); + aDetails.id() = id; + + nsIntRect rect; + rv = aScreen->GetRect(&rect.x, &rect.y, &rect.width, &rect.height); + NS_ENSURE_SUCCESS(rv, false); + aDetails.rect() = rect; + + nsIntRect rectDisplayPix; + rv = aScreen->GetRectDisplayPix(&rectDisplayPix.x, &rectDisplayPix.y, + &rectDisplayPix.width, &rectDisplayPix.height); + NS_ENSURE_SUCCESS(rv, false); + aDetails.rectDisplayPix() = rectDisplayPix; + + nsIntRect availRect; + rv = aScreen->GetAvailRect(&availRect.x, &availRect.y, &availRect.width, + &availRect.height); + NS_ENSURE_SUCCESS(rv, false); + aDetails.availRect() = availRect; + + nsIntRect availRectDisplayPix; + rv = aScreen->GetAvailRectDisplayPix(&availRectDisplayPix.x, + &availRectDisplayPix.y, + &availRectDisplayPix.width, + &availRectDisplayPix.height); + NS_ENSURE_SUCCESS(rv, false); + aDetails.availRectDisplayPix() = availRectDisplayPix; + + int32_t pixelDepth = 0; + rv = aScreen->GetPixelDepth(&pixelDepth); + NS_ENSURE_SUCCESS(rv, false); + aDetails.pixelDepth() = pixelDepth; + + int32_t colorDepth = 0; + rv = aScreen->GetColorDepth(&colorDepth); + NS_ENSURE_SUCCESS(rv, false); + aDetails.colorDepth() = colorDepth; + + double contentsScaleFactor = 1.0; + rv = aScreen->GetContentsScaleFactor(&contentsScaleFactor); + NS_ENSURE_SUCCESS(rv, false); + aDetails.contentsScaleFactor() = contentsScaleFactor; + + return true; +} + +void +ScreenManagerParent::ActorDestroy(ActorDestroyReason why) +{ +} + +} // namespace dom +} // namespace mozilla + diff --git a/dom/ipc/ScreenManagerParent.h b/dom/ipc/ScreenManagerParent.h new file mode 100644 index 000000000..cd07c12b7 --- /dev/null +++ b/dom/ipc/ScreenManagerParent.h @@ -0,0 +1,56 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_ScreenManagerParent_h +#define mozilla_dom_ScreenManagerParent_h + +#include "mozilla/dom/PScreenManagerParent.h" +#include "nsIScreenManager.h" + +namespace mozilla { +namespace dom { + +class ScreenManagerParent : public PScreenManagerParent +{ + public: + ScreenManagerParent(uint32_t* aNumberOfScreens, + float* aSystemDefaultScale, + bool* aSuccess); + ~ScreenManagerParent() {}; + + virtual bool RecvRefresh(uint32_t* aNumberOfScreens, + float* aSystemDefaultScale, + bool* aSuccess) override; + + virtual bool RecvScreenRefresh(const uint32_t& aId, + ScreenDetails* aRetVal, + bool* aSuccess) override; + + virtual void ActorDestroy(ActorDestroyReason aWhy) override; + + virtual bool RecvGetPrimaryScreen(ScreenDetails* aRetVal, + bool* aSuccess) override; + + virtual bool RecvScreenForRect(const int32_t& aLeft, + const int32_t& aTop, + const int32_t& aWidth, + const int32_t& aHeight, + ScreenDetails* aRetVal, + bool* aSuccess) override; + + virtual bool RecvScreenForBrowser(const TabId& aTabId, + ScreenDetails* aRetVal, + bool* aSuccess) override; + + private: + bool ExtractScreenDetails(nsIScreen* aScreen, ScreenDetails &aDetails); + nsCOMPtr<nsIScreenManager> mScreenMgr; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_ScreenManagerParent_h diff --git a/dom/ipc/ServiceWorkerConfiguration.ipdlh b/dom/ipc/ServiceWorkerConfiguration.ipdlh new file mode 100644 index 000000000..16e0d46ea --- /dev/null +++ b/dom/ipc/ServiceWorkerConfiguration.ipdlh @@ -0,0 +1,18 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */ +/* 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 ServiceWorkerRegistrarTypes; + +namespace mozilla { +namespace dom { + +struct ServiceWorkerConfiguration +{ + ServiceWorkerRegistrationData[] serviceWorkerRegistrations; +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/StructuredCloneData.cpp b/dom/ipc/StructuredCloneData.cpp new file mode 100644 index 000000000..98f56904f --- /dev/null +++ b/dom/ipc/StructuredCloneData.cpp @@ -0,0 +1,133 @@ +/* -*- 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 "StructuredCloneData.h" + +#include "nsIDOMDOMException.h" +#include "nsIMutable.h" +#include "nsIXPConnect.h" + +#include "ipc/IPCMessageUtils.h" +#include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/BlobBinding.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/ToJSValue.h" +#include "nsContentUtils.h" +#include "nsJSEnvironment.h" +#include "MainThreadUtils.h" +#include "StructuredCloneTags.h" +#include "jsapi.h" + +namespace mozilla { +namespace dom { +namespace ipc { + +bool +StructuredCloneData::Copy(const StructuredCloneData& aData) +{ + if (!aData.mInitialized) { + return true; + } + + if (aData.SharedData()) { + mSharedData = aData.SharedData(); + } else { + mSharedData = + SharedJSAllocatedData::CreateFromExternalData(aData.Data()); + NS_ENSURE_TRUE(mSharedData, false); + } + + PortIdentifiers().AppendElements(aData.PortIdentifiers()); + + MOZ_ASSERT(BlobImpls().IsEmpty()); + BlobImpls().AppendElements(aData.BlobImpls()); + + MOZ_ASSERT(GetSurfaces().IsEmpty()); + MOZ_ASSERT(WasmModules().IsEmpty()); + + mInitialized = true; + + return true; +} + +void +StructuredCloneData::Read(JSContext* aCx, + JS::MutableHandle<JS::Value> aValue, + ErrorResult &aRv) +{ + MOZ_ASSERT(mInitialized); + + nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx)); + MOZ_ASSERT(global); + + ReadFromBuffer(global, aCx, Data(), aValue, aRv); +} + +void +StructuredCloneData::Write(JSContext* aCx, + JS::Handle<JS::Value> aValue, + ErrorResult &aRv) +{ + Write(aCx, aValue, JS::UndefinedHandleValue, aRv); +} + +void +StructuredCloneData::Write(JSContext* aCx, + JS::Handle<JS::Value> aValue, + JS::Handle<JS::Value> aTransfer, + ErrorResult &aRv) +{ + MOZ_ASSERT(!mInitialized); + + StructuredCloneHolder::Write(aCx, aValue, aTransfer, + JS::CloneDataPolicy().denySharedArrayBuffer(), aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + JSStructuredCloneData data; + mBuffer->abandon(); + mBuffer->steal(&data); + mBuffer = nullptr; + mSharedData = new SharedJSAllocatedData(Move(data)); + mInitialized = true; +} + +void +StructuredCloneData::WriteIPCParams(IPC::Message* aMsg) const +{ + WriteParam(aMsg, Data()); +} + +bool +StructuredCloneData::ReadIPCParams(const IPC::Message* aMsg, + PickleIterator* aIter) +{ + MOZ_ASSERT(!mInitialized); + JSStructuredCloneData data; + if (!ReadParam(aMsg, aIter, &data)) { + return false; + } + mSharedData = new SharedJSAllocatedData(Move(data)); + mInitialized = true; + return true; +} + +bool +StructuredCloneData::CopyExternalData(const char* aData, + size_t aDataLength) +{ + MOZ_ASSERT(!mInitialized); + mSharedData = SharedJSAllocatedData::CreateFromExternalData(aData, + aDataLength); + NS_ENSURE_TRUE(mSharedData, false); + mInitialized = true; + return true; +} + +} // namespace ipc +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/StructuredCloneData.h b/dom/ipc/StructuredCloneData.h new file mode 100644 index 000000000..9e427e938 --- /dev/null +++ b/dom/ipc/StructuredCloneData.h @@ -0,0 +1,160 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_ipc_StructuredCloneData_h +#define mozilla_dom_ipc_StructuredCloneData_h + +#include <algorithm> +#include "mozilla/RefPtr.h" +#include "mozilla/dom/StructuredCloneHolder.h" +#include "nsISupportsImpl.h" + +namespace IPC { +class Message; +} +class PickleIterator; + +namespace mozilla { +namespace dom { +namespace ipc { + +class SharedJSAllocatedData final +{ +public: + explicit SharedJSAllocatedData(JSStructuredCloneData&& aData) + : mData(Move(aData)) + { } + + static already_AddRefed<SharedJSAllocatedData> + CreateFromExternalData(const char* aData, size_t aDataLength) + { + JSStructuredCloneData buf; + buf.WriteBytes(aData, aDataLength); + RefPtr<SharedJSAllocatedData> sharedData = + new SharedJSAllocatedData(Move(buf)); + return sharedData.forget(); + } + + static already_AddRefed<SharedJSAllocatedData> + CreateFromExternalData(const JSStructuredCloneData& aData) + { + JSStructuredCloneData buf; + auto iter = aData.Iter(); + while (!iter.Done()) { + buf.WriteBytes(iter.Data(), iter.RemainingInSegment()); + iter.Advance(aData, iter.RemainingInSegment()); + } + RefPtr<SharedJSAllocatedData> sharedData = + new SharedJSAllocatedData(Move(buf)); + return sharedData.forget(); + } + + NS_INLINE_DECL_REFCOUNTING(SharedJSAllocatedData) + + JSStructuredCloneData& Data() { return mData; } + size_t DataLength() const { return mData.Size(); } + +private: + ~SharedJSAllocatedData() { } + + JSStructuredCloneData mData; +}; + +class StructuredCloneData : public StructuredCloneHolder +{ +public: + StructuredCloneData() + : StructuredCloneHolder(StructuredCloneHolder::CloningSupported, + StructuredCloneHolder::TransferringSupported, + StructuredCloneHolder::StructuredCloneScope::DifferentProcess) + , mInitialized(false) + {} + + StructuredCloneData(const StructuredCloneData&) = delete; + + StructuredCloneData(StructuredCloneData&& aOther) = default; + + ~StructuredCloneData() + {} + + StructuredCloneData& + operator=(const StructuredCloneData& aOther) = delete; + + StructuredCloneData& + operator=(StructuredCloneData&& aOther) = default; + + const nsTArray<RefPtr<BlobImpl>>& BlobImpls() const + { + return mBlobImplArray; + } + + nsTArray<RefPtr<BlobImpl>>& BlobImpls() + { + return mBlobImplArray; + } + + bool Copy(const StructuredCloneData& aData); + + void Read(JSContext* aCx, + JS::MutableHandle<JS::Value> aValue, + ErrorResult &aRv); + + void Write(JSContext* aCx, + JS::Handle<JS::Value> aValue, + ErrorResult &aRv); + + void Write(JSContext* aCx, + JS::Handle<JS::Value> aValue, + JS::Handle<JS::Value> aTransfers, + ErrorResult &aRv); + + bool UseExternalData(const JSStructuredCloneData& aData) + { + auto iter = aData.Iter(); + bool success = false; + mExternalData = + aData.Borrow<js::SystemAllocPolicy>(iter, aData.Size(), &success); + mInitialized = true; + return success; + } + + bool CopyExternalData(const char* aData, size_t aDataLength); + + JSStructuredCloneData& Data() + { + return mSharedData ? mSharedData->Data() : mExternalData; + } + + const JSStructuredCloneData& Data() const + { + return mSharedData ? mSharedData->Data() : mExternalData; + } + + size_t DataLength() const + { + return mSharedData ? mSharedData->DataLength() : mExternalData.Size(); + } + + SharedJSAllocatedData* SharedData() const + { + return mSharedData; + } + + // For IPC serialization + void WriteIPCParams(IPC::Message* aMessage) const; + bool ReadIPCParams(const IPC::Message* aMessage, PickleIterator* aIter); + +private: + JSStructuredCloneData mExternalData; + RefPtr<SharedJSAllocatedData> mSharedData; + bool mInitialized; +}; + +} // namespace ipc +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_ipc_StructuredCloneData_h diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp new file mode 100644 index 000000000..eaf4a32ed --- /dev/null +++ b/dom/ipc/TabChild.cpp @@ -0,0 +1,3371 @@ +/* -*- 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 "base/basictypes.h" + +#include "TabChild.h" + +#include "gfxPrefs.h" +#ifdef ACCESSIBILITY +#include "mozilla/a11y/DocAccessibleChild.h" +#endif +#include "Layers.h" +#include "ContentChild.h" +#include "TabParent.h" +#include "mozilla/Preferences.h" +#include "mozilla/BrowserElementParent.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/EventListenerManager.h" +#include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h" +#include "mozilla/plugins/PluginWidgetChild.h" +#include "mozilla/IMEStateManager.h" +#include "mozilla/ipc/DocumentRendererChild.h" +#include "mozilla/ipc/URIUtils.h" +#include "mozilla/layers/APZChild.h" +#include "mozilla/layers/APZCCallbackHelper.h" +#include "mozilla/layers/APZCTreeManager.h" +#include "mozilla/layers/APZEventState.h" +#include "mozilla/layers/ContentProcessController.h" +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/DoubleTapToZoom.h" +#include "mozilla/layers/ImageBridgeChild.h" +#include "mozilla/layers/InputAPZContext.h" +#include "mozilla/layers/ShadowLayers.h" +#include "mozilla/layout/RenderFrameChild.h" +#include "mozilla/layout/RenderFrameParent.h" +#include "mozilla/LookAndFeel.h" +#include "mozilla/MouseEvents.h" +#include "mozilla/Move.h" +#include "mozilla/ProcessHangMonitor.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/TextEvents.h" +#include "mozilla/TouchEvents.h" +#include "mozilla/Unused.h" +#include "mozIApplication.h" +#include "nsContentUtils.h" +#include "nsCSSFrameConstructor.h" +#include "nsDocShell.h" +#include "nsEmbedCID.h" +#include "nsGlobalWindow.h" +#include <algorithm> +#ifdef MOZ_CRASHREPORTER +#include "nsExceptionHandler.h" +#endif +#include "nsFilePickerProxy.h" +#include "mozilla/dom/Element.h" +#include "nsGlobalWindow.h" +#include "nsIBaseWindow.h" +#include "nsIBrowserDOMWindow.h" +#include "nsIDocumentInlines.h" +#include "nsIDocShellTreeOwner.h" +#include "nsIDOMChromeWindow.h" +#include "nsIDOMDocument.h" +#include "nsIDOMEvent.h" +#include "nsIDOMWindow.h" +#include "nsIDOMWindowUtils.h" +#include "nsFocusManager.h" +#include "EventStateManager.h" +#include "nsIDocShell.h" +#include "nsIFrame.h" +#include "nsIURI.h" +#include "nsIURIFixup.h" +#include "nsCDefaultURIFixup.h" +#include "nsIWebBrowser.h" +#include "nsIWebBrowserFocus.h" +#include "nsIWebBrowserSetup.h" +#include "nsIWebProgress.h" +#include "nsIXULRuntime.h" +#include "nsPIDOMWindow.h" +#include "nsPIWindowRoot.h" +#include "nsLayoutUtils.h" +#include "nsPrintfCString.h" +#include "nsThreadUtils.h" +#include "nsViewManager.h" +#include "nsWeakReference.h" +#include "nsWindowWatcher.h" +#include "PermissionMessageUtils.h" +#include "PuppetWidget.h" +#include "StructuredCloneData.h" +#include "nsViewportInfo.h" +#include "nsILoadContext.h" +#include "ipc/nsGUIEventIPC.h" +#include "mozilla/gfx/Matrix.h" +#include "UnitTransforms.h" +#include "ClientLayerManager.h" +#include "LayersLogging.h" +#include "nsDOMClassInfoID.h" +#include "nsColorPickerProxy.h" +#include "nsDatePickerProxy.h" +#include "nsContentPermissionHelper.h" +#include "nsPresShell.h" +#include "nsIAppsService.h" +#include "nsNetUtil.h" +#include "nsIPermissionManager.h" +#include "nsIURILoader.h" +#include "nsIScriptError.h" +#include "mozilla/EventForwards.h" +#include "nsDeviceContext.h" +#include "nsSandboxFlags.h" +#include "FrameLayerBuilder.h" +#include "VRManagerChild.h" +#include "nsICommandParams.h" +#include "nsISHistory.h" +#include "nsQueryObject.h" +#include "GroupedSHistory.h" +#include "nsIHttpChannel.h" +#include "mozilla/dom/DocGroup.h" + +#ifdef NS_PRINTING +#include "nsIPrintSession.h" +#include "nsIPrintSettings.h" +#include "nsIPrintSettingsService.h" +#include "nsIWebBrowserPrint.h" +#endif + +#define BROWSER_ELEMENT_CHILD_SCRIPT \ + NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js") + +#define TABC_LOG(...) +// #define TABC_LOG(...) printf_stderr("TABC: " __VA_ARGS__) + +using namespace mozilla; +using namespace mozilla::dom; +using namespace mozilla::dom::ipc; +using namespace mozilla::dom::workers; +using namespace mozilla::ipc; +using namespace mozilla::layers; +using namespace mozilla::layout; +using namespace mozilla::docshell; +using namespace mozilla::widget; +using namespace mozilla::jsipc; +using mozilla::layers::GeckoContentController; + +NS_IMPL_ISUPPORTS(ContentListener, nsIDOMEventListener) +NS_IMPL_ISUPPORTS(TabChildSHistoryListener, + nsISHistoryListener, + nsIPartialSHistoryListener, + nsISupportsWeakReference) + +static const CSSSize kDefaultViewportSize(980, 480); + +static const char BEFORE_FIRST_PAINT[] = "before-first-paint"; + +typedef nsDataHashtable<nsUint64HashKey, TabChild*> TabChildMap; +static TabChildMap* sTabChildren; + +TabChildBase::TabChildBase() + : mTabChildGlobal(nullptr) +{ + mozilla::HoldJSObjects(this); +} + +TabChildBase::~TabChildBase() +{ + mAnonymousGlobalScopes.Clear(); + mozilla::DropJSObjects(this); +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(TabChildBase) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TabChildBase) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mTabChildGlobal) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousGlobalScopes) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebBrowserChrome) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TabChildBase) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTabChildGlobal) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebBrowserChrome) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(TabChildBase) + tmp->nsMessageManagerScriptExecutor::Trace(aCallbacks, aClosure); +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TabChildBase) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(TabChildBase) +NS_IMPL_CYCLE_COLLECTING_RELEASE(TabChildBase) + +already_AddRefed<nsIDocument> +TabChildBase::GetDocument() const +{ + nsCOMPtr<nsIDOMDocument> domDoc; + WebNavigation()->GetDocument(getter_AddRefs(domDoc)); + nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc)); + return doc.forget(); +} + +already_AddRefed<nsIPresShell> +TabChildBase::GetPresShell() const +{ + nsCOMPtr<nsIPresShell> result; + if (nsCOMPtr<nsIDocument> doc = GetDocument()) { + result = doc->GetShell(); + } + return result.forget(); +} + +void +TabChildBase::DispatchMessageManagerMessage(const nsAString& aMessageName, + const nsAString& aJSONData) +{ + AutoSafeJSContext cx; + JS::Rooted<JS::Value> json(cx, JS::NullValue()); + StructuredCloneData data; + if (JS_ParseJSON(cx, + static_cast<const char16_t*>(aJSONData.BeginReading()), + aJSONData.Length(), + &json)) { + ErrorResult rv; + data.Write(cx, json, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return; + } + } + + nsCOMPtr<nsIXPConnectJSObjectHolder> kungFuDeathGrip(GetGlobal()); + // Let the BrowserElementScrolling helper (if it exists) for this + // content manipulate the frame state. + RefPtr<nsFrameMessageManager> mm = + static_cast<nsFrameMessageManager*>(mTabChildGlobal->mMessageManager.get()); + mm->ReceiveMessage(static_cast<EventTarget*>(mTabChildGlobal), nullptr, + aMessageName, false, &data, nullptr, nullptr, nullptr); +} + +bool +TabChildBase::UpdateFrameHandler(const FrameMetrics& aFrameMetrics) +{ + MOZ_ASSERT(aFrameMetrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID); + + if (aFrameMetrics.IsRootContent()) { + if (nsCOMPtr<nsIPresShell> shell = GetPresShell()) { + // Guard against stale updates (updates meant for a pres shell which + // has since been torn down and destroyed). + if (aFrameMetrics.GetPresShellId() == shell->GetPresShellId()) { + ProcessUpdateFrame(aFrameMetrics); + return true; + } + } + } else { + // aFrameMetrics.mIsRoot is false, so we are trying to update a subframe. + // This requires special handling. + FrameMetrics newSubFrameMetrics(aFrameMetrics); + APZCCallbackHelper::UpdateSubFrame(newSubFrameMetrics); + return true; + } + return true; +} + +void +TabChildBase::ProcessUpdateFrame(const FrameMetrics& aFrameMetrics) +{ + if (!mGlobal || !mTabChildGlobal) { + return; + } + + FrameMetrics newMetrics = aFrameMetrics; + APZCCallbackHelper::UpdateRootFrame(newMetrics); +} + +NS_IMETHODIMP +ContentListener::HandleEvent(nsIDOMEvent* aEvent) +{ + RemoteDOMEvent remoteEvent; + remoteEvent.mEvent = do_QueryInterface(aEvent); + NS_ENSURE_STATE(remoteEvent.mEvent); + mTabChild->SendEvent(remoteEvent); + return NS_OK; +} + +class TabChild::DelayedDeleteRunnable final + : public Runnable +{ + RefPtr<TabChild> mTabChild; + +public: + explicit DelayedDeleteRunnable(TabChild* aTabChild) + : mTabChild(aTabChild) + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aTabChild); + } + +private: + ~DelayedDeleteRunnable() + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mTabChild); + } + + NS_IMETHOD + Run() override + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mTabChild); + + // Check in case ActorDestroy was called after RecvDestroy message. + if (mTabChild->IPCOpen()) { + Unused << PBrowserChild::Send__delete__(mTabChild); + } + + mTabChild = nullptr; + return NS_OK; + } +}; + +namespace { +StaticRefPtr<TabChild> sPreallocatedTab; + +std::map<TabId, RefPtr<TabChild>>& +NestedTabChildMap() +{ + MOZ_ASSERT(NS_IsMainThread()); + static std::map<TabId, RefPtr<TabChild>> sNestedTabChildMap; + return sNestedTabChildMap; +} +} // namespace + +already_AddRefed<TabChild> +TabChild::FindTabChild(const TabId& aTabId) +{ + auto iter = NestedTabChildMap().find(aTabId); + if (iter == NestedTabChildMap().end()) { + return nullptr; + } + RefPtr<TabChild> tabChild = iter->second; + return tabChild.forget(); +} + +static void +PreloadSlowThingsPostFork(void* aUnused) +{ + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + observerService->NotifyObservers(nullptr, "preload-postfork", nullptr); + + MOZ_ASSERT(sPreallocatedTab); + // Initialize initial reflow of the PresShell has to happen after fork + // because about:blank content viewer is created in the above observer + // notification. + nsCOMPtr<nsIDocShell> docShell = + do_GetInterface(sPreallocatedTab->WebNavigation()); + if (nsIPresShell* presShell = docShell->GetPresShell()) { + // Initialize and do an initial reflow of the about:blank + // PresShell to let it preload some things for us. + presShell->Initialize(0, 0); + nsIDocument* doc = presShell->GetDocument(); + doc->FlushPendingNotifications(Flush_Layout); + // ... but after it's done, make sure it doesn't do any more + // work. + presShell->MakeZombie(); + } + +} + +static bool sPreloaded = false; + +/*static*/ void +TabChild::PreloadSlowThings() +{ + if (sPreloaded) { + // If we are alredy initialized in Nuwa, don't redo preloading. + return; + } + sPreloaded = true; + + // Pass nullptr to aManager since at this point the TabChild is + // not connected to any manager. Any attempt to use the TabChild + // in IPC will crash. + RefPtr<TabChild> tab(new TabChild(nullptr, + TabId(0), + TabContext(), /* chromeFlags */ 0)); + if (!NS_SUCCEEDED(tab->Init()) || + !tab->InitTabChildGlobal(DONT_LOAD_SCRIPTS)) { + return; + } + + // Just load and compile these scripts, but don't run them. + tab->TryCacheLoadAndCompileScript(BROWSER_ELEMENT_CHILD_SCRIPT, true); + // Load, compile, and run these scripts. + tab->RecvLoadRemoteScript( + NS_LITERAL_STRING("chrome://global/content/preload.js"), + true); + + sPreallocatedTab = tab; + ClearOnShutdown(&sPreallocatedTab); + + PreloadSlowThingsPostFork(nullptr); +} + +/*static*/ already_AddRefed<TabChild> +TabChild::Create(nsIContentChild* aManager, + const TabId& aTabId, + const TabContext &aContext, + uint32_t aChromeFlags) +{ + if (sPreallocatedTab && + sPreallocatedTab->mChromeFlags == aChromeFlags && + aContext.IsMozBrowserOrApp()) { + + RefPtr<TabChild> child = sPreallocatedTab.get(); + sPreallocatedTab = nullptr; + + MOZ_ASSERT(!child->mTriedBrowserInit); + + child->mManager = aManager; + child->SetTabId(aTabId); + child->SetTabContext(aContext); + child->NotifyTabContextUpdated(true); + return child.forget(); + } + + RefPtr<TabChild> iframe = new TabChild(aManager, aTabId, + aContext, aChromeFlags); + return NS_SUCCEEDED(iframe->Init()) ? iframe.forget() : nullptr; +} + +TabChild::TabChild(nsIContentChild* aManager, + const TabId& aTabId, + const TabContext& aContext, + uint32_t aChromeFlags) + : TabContext(aContext) + , mRemoteFrame(nullptr) + , mManager(aManager) + , mChromeFlags(aChromeFlags) + , mActiveSuppressDisplayport(0) + , mLayersId(0) + , mDidFakeShow(false) + , mNotified(false) + , mTriedBrowserInit(false) + , mOrientation(eScreenOrientation_PortraitPrimary) + , mIgnoreKeyPressEvent(false) + , mHasValidInnerSize(false) + , mDestroyed(false) + , mUniqueId(aTabId) + , mDPI(0) + , mRounding(0) + , mDefaultScale(0) + , mIsTransparent(false) + , mIPCOpen(false) + , mParentIsActive(false) + , mDidSetRealShowInfo(false) + , mDidLoadURLInit(false) + , mIsFreshProcess(false) + , mLayerObserverEpoch(0) +#if defined(XP_WIN) && defined(ACCESSIBILITY) + , mNativeWindowHandle(0) +#endif +{ + // In the general case having the TabParent tell us if APZ is enabled or not + // doesn't really work because the TabParent itself may not have a reference + // to the owning widget during initialization. Instead we assume that this + // TabChild corresponds to a widget type that would have APZ enabled, and just + // check the other conditions necessary for enabling APZ. + mAsyncPanZoomEnabled = gfxPlatform::AsyncPanZoomEnabled(); + + nsWeakPtr weakPtrThis(do_GetWeakReference(static_cast<nsITabChild*>(this))); // for capture by the lambda + mSetAllowedTouchBehaviorCallback = [weakPtrThis](uint64_t aInputBlockId, + const nsTArray<TouchBehaviorFlags>& aFlags) + { + if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(weakPtrThis)) { + static_cast<TabChild*>(tabChild.get())->SetAllowedTouchBehavior(aInputBlockId, aFlags); + } + }; + + // preloaded TabChild should not be added to child map + if (mUniqueId) { + MOZ_ASSERT(NestedTabChildMap().find(mUniqueId) == NestedTabChildMap().end()); + NestedTabChildMap()[mUniqueId] = this; + } + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + + if (observerService) { + const nsAttrValue::EnumTable* table = + AudioChannelService::GetAudioChannelTable(); + + nsAutoCString topic; + for (uint32_t i = 0; table[i].tag; ++i) { + topic.Assign("audiochannel-activity-"); + topic.Append(table[i].tag); + + observerService->AddObserver(this, topic.get(), false); + } + } + + for (uint32_t idx = 0; idx < NUMBER_OF_AUDIO_CHANNELS; idx++) { + mAudioChannelsActive.AppendElement(false); + } +} + +NS_IMETHODIMP +TabChild::Observe(nsISupports *aSubject, + const char *aTopic, + const char16_t *aData) +{ + if (!strcmp(aTopic, BEFORE_FIRST_PAINT)) { + if (AsyncPanZoomEnabled()) { + nsCOMPtr<nsIDocument> subject(do_QueryInterface(aSubject)); + nsCOMPtr<nsIDocument> doc(GetDocument()); + + if (SameCOMIdentity(subject, doc)) { + nsCOMPtr<nsIPresShell> shell(doc->GetShell()); + if (shell) { + shell->SetIsFirstPaint(true); + } + + APZCCallbackHelper::InitializeRootDisplayport(shell); + } + } + } + + const nsAttrValue::EnumTable* table = + AudioChannelService::GetAudioChannelTable(); + + nsAutoCString topic; + int16_t audioChannel = -1; + for (uint32_t i = 0; table[i].tag; ++i) { + topic.Assign("audiochannel-activity-"); + topic.Append(table[i].tag); + + if (topic.Equals(aTopic)) { + audioChannel = table[i].value; + break; + } + } + + if (audioChannel != -1 && mIPCOpen) { + // If the subject is not a wrapper, it is sent by the TabParent and we + // should ignore it. + nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject); + if (!wrapper) { + return NS_OK; + } + + // We must have a window in order to compare the windowID contained into the + // wrapper. + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + if (!window) { + return NS_OK; + } + + uint64_t windowID = 0; + nsresult rv = wrapper->GetData(&windowID); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // In theory a tabChild should contain just 1 top window, but let's double + // check it comparing the windowID. + if (window->WindowID() != windowID) { + MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug, + ("TabChild, Observe, different windowID, owner ID = %lld, " + "ID from wrapper = %lld", window->WindowID(), windowID)); + return NS_OK; + } + + nsAutoString activeStr(aData); + bool active = activeStr.EqualsLiteral("active"); + if (active != mAudioChannelsActive[audioChannel]) { + mAudioChannelsActive[audioChannel] = active; + Unused << SendAudioChannelActivityNotification(audioChannel, active); + } + } + + return NS_OK; +} + +void +TabChild::ContentReceivedInputBlock(const ScrollableLayerGuid& aGuid, + uint64_t aInputBlockId, + bool aPreventDefault) const +{ + if (mApzcTreeManager) { + mApzcTreeManager->ContentReceivedInputBlock(aInputBlockId, aPreventDefault); + } +} + +void +TabChild::SetTargetAPZC(uint64_t aInputBlockId, + const nsTArray<ScrollableLayerGuid>& aTargets) const +{ + if (mApzcTreeManager) { + mApzcTreeManager->SetTargetAPZC(aInputBlockId, aTargets); + } +} + +void +TabChild::SetAllowedTouchBehavior(uint64_t aInputBlockId, + const nsTArray<TouchBehaviorFlags>& aTargets) const +{ + if (mApzcTreeManager) { + mApzcTreeManager->SetAllowedTouchBehavior(aInputBlockId, aTargets); + } +} + +bool +TabChild::DoUpdateZoomConstraints(const uint32_t& aPresShellId, + const ViewID& aViewId, + const Maybe<ZoomConstraints>& aConstraints) +{ + if (sPreallocatedTab == this) { + // If we're the preallocated tab, bail out because doing IPC will crash. + // Once we get used for something we'll get another zoom constraints update + // and all will be well. + return true; + } + + if (!mApzcTreeManager) { + return false; + } + + ScrollableLayerGuid guid = ScrollableLayerGuid(mLayersId, aPresShellId, aViewId); + + mApzcTreeManager->UpdateZoomConstraints(guid, aConstraints); + return true; +} + +nsresult +TabChild::Init() +{ + nsCOMPtr<nsIWebBrowser> webBrowser = do_CreateInstance(NS_WEBBROWSER_CONTRACTID); + if (!webBrowser) { + NS_ERROR("Couldn't create a nsWebBrowser?"); + return NS_ERROR_FAILURE; + } + + webBrowser->SetContainerWindow(this); + webBrowser->SetOriginAttributes(OriginAttributesRef()); + mWebNav = do_QueryInterface(webBrowser); + NS_ASSERTION(mWebNav, "nsWebBrowser doesn't implement nsIWebNavigation?"); + + nsCOMPtr<nsIDocShellTreeItem> docShellItem(do_QueryInterface(WebNavigation())); + docShellItem->SetItemType(nsIDocShellTreeItem::typeContentWrapper); + + nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation()); + if (!baseWindow) { + NS_ERROR("mWebNav doesn't QI to nsIBaseWindow"); + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIWidget> widget = nsIWidget::CreatePuppetWidget(this); + mPuppetWidget = static_cast<PuppetWidget*>(widget.get()); + if (!mPuppetWidget) { + NS_ERROR("couldn't create fake widget"); + return NS_ERROR_FAILURE; + } + mPuppetWidget->InfallibleCreate( + nullptr, 0, // no parents + LayoutDeviceIntRect(0, 0, 0, 0), + nullptr // HandleWidgetEvent + ); + + baseWindow->InitWindow(0, mPuppetWidget, 0, 0, 0, 0); + baseWindow->Create(); + + // Set the tab context attributes then pass to docShell + NotifyTabContextUpdated(false); + + // IPC uses a WebBrowser object for which DNS prefetching is turned off + // by default. But here we really want it, so enable it explicitly + nsCOMPtr<nsIWebBrowserSetup> webBrowserSetup = + do_QueryInterface(baseWindow); + if (webBrowserSetup) { + webBrowserSetup->SetProperty(nsIWebBrowserSetup::SETUP_ALLOW_DNS_PREFETCH, + true); + } else { + NS_WARNING("baseWindow doesn't QI to nsIWebBrowserSetup, skipping " + "DNS prefetching enable step."); + } + + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + MOZ_ASSERT(docShell); + + docShell->SetAffectPrivateSessionLifetime( + mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME); + nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(WebNavigation()); + MOZ_ASSERT(loadContext); + loadContext->SetPrivateBrowsing(OriginAttributesRef().mPrivateBrowsingId > 0); + loadContext->SetRemoteTabs( + mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW); + + // Few lines before, baseWindow->Create() will end up creating a new + // window root in nsGlobalWindow::SetDocShell. + // Then this chrome event handler, will be inherited to inner windows. + // We want to also set it to the docshell so that inner windows + // and any code that has access to the docshell + // can all listen to the same chrome event handler. + // XXX: ideally, we would set a chrome event handler earlier, + // and all windows, even the root one, will use the docshell one. + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); + nsCOMPtr<EventTarget> chromeHandler = + do_QueryInterface(window->GetChromeEventHandler()); + docShell->SetChromeEventHandler(chromeHandler); + + if (window->GetCurrentInnerWindow()) { + window->SetKeyboardIndicators(ShowAccelerators(), ShowFocusRings()); + } else { + // Skip ShouldShowFocusRing check if no inner window is available + window->SetInitialKeyboardIndicators(ShowAccelerators(), ShowFocusRings()); + } + + // Set prerender flag if necessary. + if (mIsPrerendered) { + docShell->SetIsPrerendered(); + } + + nsContentUtils::SetScrollbarsVisibility(window->GetDocShell(), + !!(mChromeFlags & nsIWebBrowserChrome::CHROME_SCROLLBARS)); + + nsWeakPtr weakPtrThis = do_GetWeakReference(static_cast<nsITabChild*>(this)); // for capture by the lambda + ContentReceivedInputBlockCallback callback( + [weakPtrThis](const ScrollableLayerGuid& aGuid, + uint64_t aInputBlockId, + bool aPreventDefault) + { + if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(weakPtrThis)) { + static_cast<TabChild*>(tabChild.get())->ContentReceivedInputBlock(aGuid, aInputBlockId, aPreventDefault); + } + }); + mAPZEventState = new APZEventState(mPuppetWidget, Move(callback)); + + mIPCOpen = true; + + if (GroupedSHistory::GroupedHistoryEnabled()) { + // Set session history listener. + nsCOMPtr<nsISHistory> shistory; + mWebNav->GetSessionHistory(getter_AddRefs(shistory)); + if (!shistory) { + return NS_ERROR_FAILURE; + } + mHistoryListener = new TabChildSHistoryListener(this); + nsCOMPtr<nsISHistoryListener> listener(do_QueryObject(mHistoryListener)); + shistory->AddSHistoryListener(listener); + nsCOMPtr<nsIPartialSHistoryListener> partialListener(do_QueryObject(mHistoryListener)); + shistory->SetPartialSHistoryListener(partialListener); + } + + return NS_OK; +} + +void +TabChild::NotifyTabContextUpdated(bool aIsPreallocated) +{ + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + MOZ_ASSERT(docShell); + + if (!docShell) { + return; + } + + UpdateFrameType(); + + if (aIsPreallocated) { + nsDocShell::Cast(docShell)->SetOriginAttributes(OriginAttributesRef()); + } + + // Set SANDBOXED_AUXILIARY_NAVIGATION flag if this is a receiver page. + if (!PresentationURL().IsEmpty()) { + docShell->SetSandboxFlags(SANDBOXED_AUXILIARY_NAVIGATION); + } +} + +void +TabChild::UpdateFrameType() +{ + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + MOZ_ASSERT(docShell); + + // TODO: Bug 1252794 - remove frameType from nsIDocShell.idl + docShell->SetFrameType(IsMozBrowserElement() ? nsIDocShell::FRAME_TYPE_BROWSER : + HasOwnApp() ? nsIDocShell::FRAME_TYPE_APP : + nsIDocShell::FRAME_TYPE_REGULAR); +} + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TabChild) + NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome) + NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome2) + NS_INTERFACE_MAP_ENTRY(nsIEmbeddingSiteWindow) + NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChromeFocus) + NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) + NS_INTERFACE_MAP_ENTRY(nsIWindowProvider) + NS_INTERFACE_MAP_ENTRY(nsITabChild) + NS_INTERFACE_MAP_ENTRY(nsIObserver) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY(nsITooltipListener) +NS_INTERFACE_MAP_END_INHERITING(TabChildBase) + +NS_IMPL_ADDREF_INHERITED(TabChild, TabChildBase); +NS_IMPL_RELEASE_INHERITED(TabChild, TabChildBase); + +NS_IMETHODIMP +TabChild::SetStatus(uint32_t aStatusType, const char16_t* aStatus) +{ + return SetStatusWithContext(aStatusType, + aStatus ? static_cast<const nsString &>(nsDependentString(aStatus)) + : EmptyString(), + nullptr); +} + +NS_IMETHODIMP +TabChild::GetWebBrowser(nsIWebBrowser** aWebBrowser) +{ + NS_WARNING("TabChild::GetWebBrowser not supported in TabChild"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::SetWebBrowser(nsIWebBrowser* aWebBrowser) +{ + NS_WARNING("TabChild::SetWebBrowser not supported in TabChild"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::GetChromeFlags(uint32_t* aChromeFlags) +{ + *aChromeFlags = mChromeFlags; + return NS_OK; +} + +NS_IMETHODIMP +TabChild::SetChromeFlags(uint32_t aChromeFlags) +{ + NS_WARNING("trying to SetChromeFlags from content process?"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::DestroyBrowserWindow() +{ + NS_WARNING("TabChild::DestroyBrowserWindow not supported in TabChild"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::RemoteSizeShellTo(int32_t aWidth, int32_t aHeight, + int32_t aShellItemWidth, int32_t aShellItemHeight) +{ + nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation()); + nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(ourDocShell)); + int32_t width, height; + docShellAsWin->GetSize(&width, &height); + + uint32_t flags = 0; + if (width == aWidth) { + flags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CX; + } + + if (height == aHeight) { + flags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CY; + } + + bool sent = SendSizeShellTo(flags, aWidth, aHeight, aShellItemWidth, aShellItemHeight); + + return sent ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +TabChild::RemoteDropLinks(uint32_t aLinksCount, nsIDroppedLinkItem** aLinks) +{ + nsTArray<nsString> linksArray; + nsresult rv = NS_OK; + for (uint32_t i = 0; i < aLinksCount; i++) { + nsString tmp; + rv = aLinks[i]->GetUrl(tmp); + if (NS_FAILED(rv)) { + return rv; + } + linksArray.AppendElement(tmp); + + rv = aLinks[i]->GetName(tmp); + if (NS_FAILED(rv)) { + return rv; + } + linksArray.AppendElement(tmp); + + rv = aLinks[i]->GetType(tmp); + if (NS_FAILED(rv)) { + return rv; + } + linksArray.AppendElement(tmp); + } + + bool sent = SendDropLinks(linksArray); + + return sent ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +TabChild::SizeBrowserTo(int32_t aWidth, int32_t aHeight) +{ + NS_WARNING("TabChild::SizeBrowserTo not supported in TabChild"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::ShowAsModal() +{ + NS_WARNING("TabChild::ShowAsModal not supported in TabChild"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::IsWindowModal(bool* aRetVal) +{ + *aRetVal = false; + return NS_OK; +} + +NS_IMETHODIMP +TabChild::ExitModalEventLoop(nsresult aStatus) +{ + NS_WARNING("TabChild::ExitModalEventLoop not supported in TabChild"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::SetStatusWithContext(uint32_t aStatusType, + const nsAString& aStatusText, + nsISupports* aStatusContext) +{ + // We can only send the status after the ipc machinery is set up, + // mRemoteFrame is a good indicator. + if (mRemoteFrame) + SendSetStatus(aStatusType, nsString(aStatusText)); + return NS_OK; +} + +NS_IMETHODIMP +TabChild::SetDimensions(uint32_t aFlags, int32_t aX, int32_t aY, + int32_t aCx, int32_t aCy) +{ + // The parent is in charge of the dimension changes. If JS code wants to + // change the dimensions (moveTo, screenX, etc.) we send a message to the + // parent about the new requested dimension, the parent does the resize/move + // then send a message to the child to update itself. For APIs like screenX + // this function is called with the current value for the non-changed values. + // In a series of calls like window.screenX = 10; window.screenY = 10; for + // the second call, since screenX is not yet updated we might accidentally + // reset back screenX to it's old value. To avoid this if a parameter did not + // change we want the parent to ignore its value. + int32_t x, y, cx, cy; + GetDimensions(aFlags, &x, &y, &cx, &cy); + + if (x == aX) { + aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_X; + } + + if (y == aY) { + aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_Y; + } + + if (cx == aCx) { + aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CX; + } + + if (cy == aCy) { + aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CY; + } + + Unused << SendSetDimensions(aFlags, aX, aY, aCx, aCy); + + return NS_OK; +} + +NS_IMETHODIMP +TabChild::GetDimensions(uint32_t aFlags, int32_t* aX, + int32_t* aY, int32_t* aCx, int32_t* aCy) +{ + ScreenIntRect rect = GetOuterRect(); + if (aX) { + *aX = rect.x; + } + if (aY) { + *aY = rect.y; + } + if (aCx) { + *aCx = rect.width; + } + if (aCy) { + *aCy = rect.height; + } + + return NS_OK; +} + +NS_IMETHODIMP +TabChild::SetFocus() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::GetVisibility(bool* aVisibility) +{ + *aVisibility = true; + return NS_OK; +} + +NS_IMETHODIMP +TabChild::SetVisibility(bool aVisibility) +{ + // should the platform support this? Bug 666365 + return NS_OK; +} + +NS_IMETHODIMP +TabChild::GetTitle(char16_t** aTitle) +{ + NS_WARNING("TabChild::GetTitle not supported in TabChild"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::SetTitle(const char16_t* aTitle) +{ + // JavaScript sends the "DOMTitleChanged" event to the parent + // via the message manager. + return NS_OK; +} + +NS_IMETHODIMP +TabChild::GetSiteWindow(void** aSiteWindow) +{ + NS_WARNING("TabChild::GetSiteWindow not supported in TabChild"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::Blur() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChild::FocusNextElement(bool aForDocumentNavigation) +{ + SendMoveFocus(true, aForDocumentNavigation); + return NS_OK; +} + +NS_IMETHODIMP +TabChild::FocusPrevElement(bool aForDocumentNavigation) +{ + SendMoveFocus(false, aForDocumentNavigation); + return NS_OK; +} + +NS_IMETHODIMP +TabChild::GetInterface(const nsIID & aIID, void **aSink) +{ + if (aIID.Equals(NS_GET_IID(nsIWebBrowserChrome3))) { + NS_IF_ADDREF(((nsISupports *) (*aSink = mWebBrowserChrome))); + return NS_OK; + } + + // XXXbz should we restrict the set of interfaces we hand out here? + // See bug 537429 + return QueryInterface(aIID, aSink); +} + +NS_IMETHODIMP +TabChild::ProvideWindow(mozIDOMWindowProxy* aParent, + uint32_t aChromeFlags, + bool aCalledFromJS, + bool aPositionSpecified, bool aSizeSpecified, + nsIURI* aURI, const nsAString& aName, + const nsACString& aFeatures, bool aForceNoOpener, + bool* aWindowIsNew, mozIDOMWindowProxy** aReturn) +{ + *aReturn = nullptr; + + // If aParent is inside an <iframe mozbrowser> or <iframe mozapp> and this + // isn't a request to open a modal-type window, we're going to create a new + // <iframe mozbrowser/mozapp> and return its window here. + nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent); + bool iframeMoz = (docshell && docshell->GetIsInMozBrowserOrApp() && + !(aChromeFlags & (nsIWebBrowserChrome::CHROME_MODAL | + nsIWebBrowserChrome::CHROME_OPENAS_DIALOG | + nsIWebBrowserChrome::CHROME_OPENAS_CHROME))); + + if (!iframeMoz) { + int32_t openLocation = + nsWindowWatcher::GetWindowOpenLocation(nsPIDOMWindowOuter::From(aParent), + aChromeFlags, aCalledFromJS, + aPositionSpecified, aSizeSpecified); + + // If it turns out we're opening in the current browser, just hand over the + // current browser's docshell. + if (openLocation == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) { + nsCOMPtr<nsIWebBrowser> browser = do_GetInterface(WebNavigation()); + *aWindowIsNew = false; + return browser->GetContentDOMWindow(aReturn); + } + } + + // Note that ProvideWindowCommon may return NS_ERROR_ABORT if the + // open window call was canceled. It's important that we pass this error + // code back to our caller. + ContentChild* cc = ContentChild::GetSingleton(); + return cc->ProvideWindowCommon(this, + aParent, + iframeMoz, + aChromeFlags, + aCalledFromJS, + aPositionSpecified, + aSizeSpecified, + aURI, + aName, + aFeatures, + aForceNoOpener, + aWindowIsNew, + aReturn); +} + +void +TabChild::DestroyWindow() +{ + nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation()); + if (baseWindow) + baseWindow->Destroy(); + + // NB: the order of mPuppetWidget->Destroy() and mRemoteFrame->Destroy() + // is important: we want to kill off remote layers before their + // frames + if (mPuppetWidget) { + mPuppetWidget->Destroy(); + } + + if (mRemoteFrame) { + mRemoteFrame->Destroy(); + mRemoteFrame = nullptr; + } + + + if (mLayersId != 0) { + MOZ_ASSERT(sTabChildren); + sTabChildren->Remove(mLayersId); + if (!sTabChildren->Count()) { + delete sTabChildren; + sTabChildren = nullptr; + } + mLayersId = 0; + } +} + +void +TabChild::ActorDestroy(ActorDestroyReason why) +{ + mIPCOpen = false; + + DestroyWindow(); + + if (mTabChildGlobal) { + // We should have a message manager if the global is alive, but it + // seems sometimes we don't. Assert in aurora/nightly, but don't + // crash in release builds. + MOZ_DIAGNOSTIC_ASSERT(mTabChildGlobal->mMessageManager); + if (mTabChildGlobal->mMessageManager) { + // The messageManager relays messages via the TabChild which + // no longer exists. + static_cast<nsFrameMessageManager*> + (mTabChildGlobal->mMessageManager.get())->Disconnect(); + mTabChildGlobal->mMessageManager = nullptr; + } + } + + CompositorBridgeChild* compositorChild = static_cast<CompositorBridgeChild*>(CompositorBridgeChild::Get()); + compositorChild->CancelNotifyAfterRemotePaint(this); + + if (GetTabId() != 0) { + NestedTabChildMap().erase(GetTabId()); + } +} + +TabChild::~TabChild() +{ + DestroyWindow(); + + nsCOMPtr<nsIWebBrowser> webBrowser = do_QueryInterface(WebNavigation()); + if (webBrowser) { + webBrowser->SetContainerWindow(nullptr); + } + + if (mHistoryListener) { + mHistoryListener->ClearTabChild(); + } +} + +void +TabChild::SetProcessNameToAppName() +{ + nsCOMPtr<mozIApplication> app = GetOwnApp(); + if (!app) { + return; + } + + nsAutoString appName; + nsresult rv = app->GetName(appName); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to retrieve app name"); + return; + } + + ContentChild::GetSingleton()->SetProcessName(appName, true); +} + +bool +TabChild::RecvLoadURL(const nsCString& aURI, + const ShowInfo& aInfo) +{ + if (!mDidLoadURLInit) { + mDidLoadURLInit = true; + if (!InitTabChildGlobal()) { + return false; + } + + ApplyShowInfo(aInfo); + + SetProcessNameToAppName(); + } + + nsresult rv = + WebNavigation()->LoadURI(NS_ConvertUTF8toUTF16(aURI).get(), + nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP | + nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL, + nullptr, nullptr, nullptr); + if (NS_FAILED(rv)) { + NS_WARNING("WebNavigation()->LoadURI failed. Eating exception, what else can I do?"); + } + +#ifdef MOZ_CRASHREPORTER + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("URL"), aURI); +#endif + + return true; +} + +void +TabChild::DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier, + const uint64_t& aLayersId, + PRenderFrameChild* aRenderFrame, const ShowInfo& aShowInfo) +{ + RecvShow(ScreenIntSize(0, 0), aShowInfo, aTextureFactoryIdentifier, + aLayersId, aRenderFrame, mParentIsActive, nsSizeMode_Normal); + mDidFakeShow = true; +} + +void +TabChild::ApplyShowInfo(const ShowInfo& aInfo) +{ + if (mDidSetRealShowInfo) { + return; + } + + if (!aInfo.fakeShowInfo()) { + // Once we've got one ShowInfo from parent, no need to update the values + // anymore. + mDidSetRealShowInfo = true; + } + + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + if (docShell) { + nsCOMPtr<nsIDocShellTreeItem> item = do_GetInterface(docShell); + if (IsMozBrowserOrApp()) { + // B2G allows window.name to be set by changing the name attribute on the + // <iframe mozbrowser> element. window.open calls cause this attribute to + // be set to the correct value. A normal <xul:browser> element has no such + // attribute. The data we get here comes from reading the attribute, so we + // shouldn't trust it for <xul:browser> elements. + item->SetName(aInfo.name()); + } + docShell->SetFullscreenAllowed(aInfo.fullscreenAllowed()); + if (aInfo.isPrivate()) { + nsCOMPtr<nsILoadContext> context = do_GetInterface(docShell); + // No need to re-set private browsing mode. + if (!context->UsePrivateBrowsing()) { + if (docShell->GetHasLoadedNonBlankURI()) { + nsContentUtils::ReportToConsoleNonLocalized( + NS_LITERAL_STRING("We should not switch to Private Browsing after loading a document."), + nsIScriptError::warningFlag, + NS_LITERAL_CSTRING("mozprivatebrowsing"), + nullptr); + } else { + DocShellOriginAttributes attrs(nsDocShell::Cast(docShell)->GetOriginAttributes()); + attrs.SyncAttributesWithPrivateBrowsing(true); + nsDocShell::Cast(docShell)->SetOriginAttributes(attrs); + } + } + } + } + mDPI = aInfo.dpi(); + mRounding = aInfo.widgetRounding(); + mDefaultScale = aInfo.defaultScale(); + mIsTransparent = aInfo.isTransparent(); +} + +bool +TabChild::RecvShow(const ScreenIntSize& aSize, + const ShowInfo& aInfo, + const TextureFactoryIdentifier& aTextureFactoryIdentifier, + const uint64_t& aLayersId, + PRenderFrameChild* aRenderFrame, + const bool& aParentIsActive, + const nsSizeMode& aSizeMode) +{ + MOZ_ASSERT((!mDidFakeShow && aRenderFrame) || (mDidFakeShow && !aRenderFrame)); + + mPuppetWidget->SetSizeMode(aSizeMode); + if (mDidFakeShow) { + ApplyShowInfo(aInfo); + RecvParentActivated(aParentIsActive); + return true; + } + + nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation()); + if (!baseWindow) { + NS_ERROR("WebNavigation() doesn't QI to nsIBaseWindow"); + return false; + } + + if (!InitRenderingState(aTextureFactoryIdentifier, aLayersId, aRenderFrame)) { + // We can fail to initialize our widget if the <browser + // remote> has already been destroyed, and we couldn't hook + // into the parent-process's layer system. That's not a fatal + // error. + return true; + } + + baseWindow->SetVisibility(true); + + bool res = InitTabChildGlobal(); + ApplyShowInfo(aInfo); + RecvParentActivated(aParentIsActive); + + return res; +} + +bool +TabChild::RecvUpdateDimensions(const CSSRect& rect, const CSSSize& size, + const ScreenOrientationInternal& orientation, + const LayoutDeviceIntPoint& clientOffset, + const LayoutDeviceIntPoint& chromeDisp) +{ + if (!mRemoteFrame) { + return true; + } + + mUnscaledOuterRect = rect; + mClientOffset = clientOffset; + mChromeDisp = chromeDisp; + + mOrientation = orientation; + SetUnscaledInnerSize(size); + if (!mHasValidInnerSize && size.width != 0 && size.height != 0) { + mHasValidInnerSize = true; + } + + ScreenIntSize screenSize = GetInnerSize(); + ScreenIntRect screenRect = GetOuterRect(); + + // Set the size on the document viewer before we update the widget and + // trigger a reflow. Otherwise the MobileViewportManager reads the stale + // size from the content viewer when it computes a new CSS viewport. + nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation()); + baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height, + nsIBaseWindow::eRepaint); + + mPuppetWidget->Resize(screenRect.x + clientOffset.x + chromeDisp.x, + screenRect.y + clientOffset.y + chromeDisp.y, + screenSize.width, screenSize.height, true); + + return true; +} + +bool +TabChild::RecvSizeModeChanged(const nsSizeMode& aSizeMode) +{ + mPuppetWidget->SetSizeMode(aSizeMode); + if (!mPuppetWidget->IsVisible()) { + return true; + } + nsCOMPtr<nsIDocument> document(GetDocument()); + nsCOMPtr<nsIPresShell> presShell = document->GetShell(); + if (presShell) { + nsPresContext* presContext = presShell->GetPresContext(); + if (presContext) { + presContext->SizeModeChanged(aSizeMode); + } + } + return true; +} + +bool +TabChild::UpdateFrame(const FrameMetrics& aFrameMetrics) +{ + return TabChildBase::UpdateFrameHandler(aFrameMetrics); +} + +bool +TabChild::RecvSuppressDisplayport(const bool& aEnabled) +{ + if (aEnabled) { + mActiveSuppressDisplayport++; + } else { + mActiveSuppressDisplayport--; + } + + MOZ_ASSERT(mActiveSuppressDisplayport >= 0); + APZCCallbackHelper::SuppressDisplayport(aEnabled, GetPresShell()); + return true; +} + +void +TabChild::HandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, + const ScrollableLayerGuid& aGuid) +{ + TABC_LOG("Handling double tap at %s with %p %p\n", + Stringify(aPoint).c_str(), mGlobal.get(), mTabChildGlobal.get()); + + if (!mGlobal || !mTabChildGlobal) { + return; + } + + // Note: there is nothing to do with the modifiers here, as we are not + // synthesizing any sort of mouse event. + nsCOMPtr<nsIDocument> document = GetDocument(); + CSSRect zoomToRect = CalculateRectToZoomTo(document, aPoint); + // The double-tap can be dispatched by any scroll frame (so |aGuid| could be + // the guid of any scroll frame), but the zoom-to-rect operation must be + // performed by the root content scroll frame, so query its identifiers + // for the SendZoomToRect() call rather than using the ones from |aGuid|. + uint32_t presShellId; + ViewID viewId; + if (APZCCallbackHelper::GetOrCreateScrollIdentifiers( + document->GetDocumentElement(), &presShellId, &viewId) && mApzcTreeManager) { + ScrollableLayerGuid guid(mLayersId, presShellId, viewId); + + mApzcTreeManager->ZoomToRect(guid, zoomToRect, DEFAULT_BEHAVIOR); + } +} + +bool +TabChild::RecvHandleTap(const GeckoContentController::TapType& aType, + const LayoutDevicePoint& aPoint, + const Modifiers& aModifiers, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) +{ + nsCOMPtr<nsIPresShell> presShell = GetPresShell(); + if (!presShell) { + return true; + } + if (!presShell->GetPresContext()) { + return true; + } + CSSToLayoutDeviceScale scale(presShell->GetPresContext()->CSSToDevPixelScale()); + CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint / scale, aGuid); + + switch (aType) { + case GeckoContentController::TapType::eSingleTap: + if (mGlobal && mTabChildGlobal) { + mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 1); + } + break; + case GeckoContentController::TapType::eDoubleTap: + HandleDoubleTap(point, aModifiers, aGuid); + break; + case GeckoContentController::TapType::eSecondTap: + if (mGlobal && mTabChildGlobal) { + mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 2); + } + break; + case GeckoContentController::TapType::eLongTap: + if (mGlobal && mTabChildGlobal) { + mAPZEventState->ProcessLongTap(presShell, point, scale, aModifiers, aGuid, + aInputBlockId); + } + break; + case GeckoContentController::TapType::eLongTapUp: + if (mGlobal && mTabChildGlobal) { + mAPZEventState->ProcessLongTapUp(presShell, point, scale, aModifiers); + } + break; + case GeckoContentController::TapType::eSentinel: + // Should never happen, but we need to handle this case to make the compiler + // happy. + MOZ_ASSERT(false); + break; + } + return true; +} + +bool +TabChild::NotifyAPZStateChange(const ViewID& aViewId, + const layers::GeckoContentController::APZStateChange& aChange, + const int& aArg) +{ + mAPZEventState->ProcessAPZStateChange(aViewId, aChange, aArg); + if (aChange == layers::GeckoContentController::APZStateChange::eTransformEnd) { + // This is used by tests to determine when the APZ is done doing whatever + // it's doing. XXX generify this as needed when writing additional tests. + nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService(); + observerService->NotifyObservers(nullptr, "APZ:TransformEnd", nullptr); + } + return true; +} + +void +TabChild::StartScrollbarDrag(const layers::AsyncDragMetrics& aDragMetrics) +{ + ScrollableLayerGuid guid(mLayersId, aDragMetrics.mPresShellId, + aDragMetrics.mViewId); + + if (mApzcTreeManager) { + mApzcTreeManager->StartScrollbarDrag(guid, aDragMetrics); + } +} + +void +TabChild::ZoomToRect(const uint32_t& aPresShellId, + const FrameMetrics::ViewID& aViewId, + const CSSRect& aRect, + const uint32_t& aFlags) +{ + ScrollableLayerGuid guid(mLayersId, aPresShellId, aViewId); + + if (mApzcTreeManager) { + mApzcTreeManager->ZoomToRect(guid, aRect, aFlags); + } +} + +bool +TabChild::RecvActivate() +{ + nsCOMPtr<nsIWebBrowserFocus> browser = do_QueryInterface(WebNavigation()); + browser->Activate(); + return true; +} + +bool TabChild::RecvDeactivate() +{ + nsCOMPtr<nsIWebBrowserFocus> browser = do_QueryInterface(WebNavigation()); + browser->Deactivate(); + return true; +} + +bool TabChild::RecvParentActivated(const bool& aActivated) +{ + mParentIsActive = aActivated; + + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + NS_ENSURE_TRUE(fm, true); + + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + fm->ParentActivated(window, aActivated); + return true; +} + +bool TabChild::RecvSetKeyboardIndicators(const UIStateChangeType& aShowAccelerators, + const UIStateChangeType& aShowFocusRings) +{ + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + NS_ENSURE_TRUE(window, true); + + window->SetKeyboardIndicators(aShowAccelerators, aShowFocusRings); + return true; +} + +bool +TabChild::RecvStopIMEStateManagement() +{ + IMEStateManager::StopIMEStateManagement(); + return true; +} + +bool +TabChild::RecvMenuKeyboardListenerInstalled(const bool& aInstalled) +{ + IMEStateManager::OnInstalledMenuKeyboardListener(aInstalled); + return true; +} + +bool +TabChild::RecvNotifyAttachGroupedSessionHistory(const uint32_t& aOffset) +{ + // nsISHistory uses int32_t + if (NS_WARN_IF(aOffset > INT32_MAX)) { + return false; + } + + nsCOMPtr<nsISHistory> shistory; + mWebNav->GetSessionHistory(getter_AddRefs(shistory)); + NS_ENSURE_TRUE(shistory, false); + + return NS_SUCCEEDED(shistory->OnAttachGroupedSessionHistory(aOffset)); +} + +bool +TabChild::RecvNotifyPartialSessionHistoryActive(const uint32_t& aGlobalLength, + const uint32_t& aTargetLocalIndex) +{ + // nsISHistory uses int32_t + if (NS_WARN_IF(aGlobalLength > INT32_MAX || aTargetLocalIndex > INT32_MAX)) { + return false; + } + + nsCOMPtr<nsISHistory> shistory; + mWebNav->GetSessionHistory(getter_AddRefs(shistory)); + NS_ENSURE_TRUE(shistory, false); + + return NS_SUCCEEDED(shistory->OnPartialSessionHistoryActive(aGlobalLength, + aTargetLocalIndex)); +} + +bool +TabChild::RecvNotifyPartialSessionHistoryDeactive() +{ + nsCOMPtr<nsISHistory> shistory; + mWebNav->GetSessionHistory(getter_AddRefs(shistory)); + NS_ENSURE_TRUE(shistory, false); + + return NS_SUCCEEDED(shistory->OnPartialSessionHistoryDeactive()); +} + +bool +TabChild::RecvMouseEvent(const nsString& aType, + const float& aX, + const float& aY, + const int32_t& aButton, + const int32_t& aClickCount, + const int32_t& aModifiers, + const bool& aIgnoreRootScrollFrame) +{ + APZCCallbackHelper::DispatchMouseEvent(GetPresShell(), aType, CSSPoint(aX, aY), + aButton, aClickCount, aModifiers, aIgnoreRootScrollFrame, nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN); + return true; +} + +bool +TabChild::RecvRealMouseMoveEvent(const WidgetMouseEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) +{ + return RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId); +} + +bool +TabChild::RecvSynthMouseMoveEvent(const WidgetMouseEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) +{ + return RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId); +} + +bool +TabChild::RecvRealMouseButtonEvent(const WidgetMouseEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) +{ + // Mouse events like eMouseEnterIntoWidget, that are created in the parent + // process EventStateManager code, have an input block id which they get from + // the InputAPZContext in the parent process stack. However, they did not + // actually go through the APZ code and so their mHandledByAPZ flag is false. + // Since thos events didn't go through APZ, we don't need to send notifications + // for them. + if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) { + nsCOMPtr<nsIDocument> document(GetDocument()); + APZCCallbackHelper::SendSetTargetAPZCNotification( + mPuppetWidget, document, aEvent, aGuid, aInputBlockId); + } + + nsEventStatus unused; + InputAPZContext context(aGuid, aInputBlockId, unused); + + WidgetMouseEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid, + mPuppetWidget->GetDefaultScale()); + APZCCallbackHelper::DispatchWidgetEvent(localEvent); + + if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) { + mAPZEventState->ProcessMouseEvent(aEvent, aGuid, aInputBlockId); + } + return true; +} + +bool +TabChild::RecvMouseWheelEvent(const WidgetWheelEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) +{ + if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) { + nsCOMPtr<nsIDocument> document(GetDocument()); + APZCCallbackHelper::SendSetTargetAPZCNotification( + mPuppetWidget, document, aEvent, aGuid, aInputBlockId); + } + + WidgetWheelEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid, + mPuppetWidget->GetDefaultScale()); + APZCCallbackHelper::DispatchWidgetEvent(localEvent); + + if (localEvent.mCanTriggerSwipe) { + SendRespondStartSwipeEvent(aInputBlockId, localEvent.TriggersSwipe()); + } + + if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) { + mAPZEventState->ProcessWheelEvent(localEvent, aGuid, aInputBlockId); + } + return true; +} + +bool +TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId, + const nsEventStatus& aApzResponse) +{ + TABC_LOG("Receiving touch event of type %d\n", aEvent.mMessage); + + WidgetTouchEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + + APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid, + mPuppetWidget->GetDefaultScale()); + + if (localEvent.mMessage == eTouchStart && AsyncPanZoomEnabled()) { + nsCOMPtr<nsIDocument> document = GetDocument(); + if (gfxPrefs::TouchActionEnabled()) { + APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(mPuppetWidget, + document, localEvent, aInputBlockId, mSetAllowedTouchBehaviorCallback); + } + APZCCallbackHelper::SendSetTargetAPZCNotification(mPuppetWidget, document, + localEvent, aGuid, aInputBlockId); + } + + // Dispatch event to content (potentially a long-running operation) + nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent); + + if (!AsyncPanZoomEnabled()) { + // We shouldn't have any e10s platforms that have touch events enabled + // without APZ. + MOZ_ASSERT(false); + return true; + } + + mAPZEventState->ProcessTouchEvent(localEvent, aGuid, aInputBlockId, + aApzResponse, status); + return true; +} + +bool +TabChild::RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId, + const nsEventStatus& aApzResponse) +{ + return RecvRealTouchEvent(aEvent, aGuid, aInputBlockId, aApzResponse); +} + +bool +TabChild::RecvRealDragEvent(const WidgetDragEvent& aEvent, + const uint32_t& aDragAction, + const uint32_t& aDropEffect) +{ + WidgetDragEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + + nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession(); + if (dragSession) { + dragSession->SetDragAction(aDragAction); + nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer; + dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer)); + if (initialDataTransfer) { + initialDataTransfer->SetDropEffectInt(aDropEffect); + } + } + + if (aEvent.mMessage == eDrop) { + bool canDrop = true; + if (!dragSession || NS_FAILED(dragSession->GetCanDrop(&canDrop)) || + !canDrop) { + localEvent.mMessage = eDragExit; + } + } else if (aEvent.mMessage == eDragOver) { + nsCOMPtr<nsIDragService> dragService = + do_GetService("@mozilla.org/widget/dragservice;1"); + if (dragService) { + // This will dispatch 'drag' event at the source if the + // drag transaction started in this process. + dragService->FireDragEventAtSource(eDrag); + } + } + + APZCCallbackHelper::DispatchWidgetEvent(localEvent); + return true; +} + +bool +TabChild::RecvPluginEvent(const WidgetPluginEvent& aEvent) +{ + WidgetPluginEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent); + if (status != nsEventStatus_eConsumeNoDefault) { + // If not consumed, we should call default action + SendDefaultProcOfPluginEvent(aEvent); + } + return true; +} + +void +TabChild::RequestNativeKeyBindings(AutoCacheNativeKeyCommands* aAutoCache, + const WidgetKeyboardEvent* aEvent) +{ + MaybeNativeKeyBinding maybeBindings; + if (!SendRequestNativeKeyBindings(*aEvent, &maybeBindings)) { + return; + } + + if (maybeBindings.type() == MaybeNativeKeyBinding::TNativeKeyBinding) { + const NativeKeyBinding& bindings = maybeBindings; + aAutoCache->Cache(bindings.singleLineCommands(), + bindings.multiLineCommands(), + bindings.richTextCommands()); + } else { + aAutoCache->CacheNoCommands(); + } +} + +bool +TabChild::RecvNativeSynthesisResponse(const uint64_t& aObserverId, + const nsCString& aResponse) +{ + mozilla::widget::AutoObserverNotifier::NotifySavedObserver(aObserverId, aResponse.get()); + return true; +} + +bool +TabChild::RecvRealKeyEvent(const WidgetKeyboardEvent& event, + const MaybeNativeKeyBinding& aBindings) +{ + AutoCacheNativeKeyCommands autoCache(mPuppetWidget); + + if (event.mMessage == eKeyPress) { + // If content code called preventDefault() on a keydown event, then we don't + // want to process any following keypress events. + if (mIgnoreKeyPressEvent) { + return true; + } + if (aBindings.type() == MaybeNativeKeyBinding::TNativeKeyBinding) { + const NativeKeyBinding& bindings = aBindings; + autoCache.Cache(bindings.singleLineCommands(), + bindings.multiLineCommands(), + bindings.richTextCommands()); + } else { + autoCache.CacheNoCommands(); + } + } + + WidgetKeyboardEvent localEvent(event); + localEvent.mWidget = mPuppetWidget; + nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent); + + if (event.mMessage == eKeyDown) { + mIgnoreKeyPressEvent = status == nsEventStatus_eConsumeNoDefault; + } + + if (localEvent.mFlags.mIsSuppressedOrDelayed) { + localEvent.PreventDefault(); + } + + // If a response is desired from the content process, resend the key event. + // If mAccessKeyForwardedToChild is set, then don't resend the key event yet + // as RecvHandleAccessKey will do this. + if (localEvent.mFlags.mWantReplyFromContentProcess) { + SendReplyKeyEvent(localEvent); + } + + if (localEvent.mAccessKeyForwardedToChild) { + SendAccessKeyNotHandled(localEvent); + } + + if (PresShell::BeforeAfterKeyboardEventEnabled()) { + SendDispatchAfterKeyboardEvent(localEvent); + } + + return true; +} + +bool +TabChild::RecvKeyEvent(const nsString& aType, + const int32_t& aKeyCode, + const int32_t& aCharCode, + const int32_t& aModifiers, + const bool& aPreventDefault) +{ + bool ignored = false; + nsContentUtils::SendKeyEvent(mPuppetWidget, aType, aKeyCode, aCharCode, + aModifiers, aPreventDefault, &ignored); + return true; +} + +bool +TabChild::RecvCompositionEvent(const WidgetCompositionEvent& event) +{ + WidgetCompositionEvent localEvent(event); + localEvent.mWidget = mPuppetWidget; + APZCCallbackHelper::DispatchWidgetEvent(localEvent); + Unused << SendOnEventNeedingAckHandled(event.mMessage); + return true; +} + +bool +TabChild::RecvSelectionEvent(const WidgetSelectionEvent& event) +{ + WidgetSelectionEvent localEvent(event); + localEvent.mWidget = mPuppetWidget; + APZCCallbackHelper::DispatchWidgetEvent(localEvent); + Unused << SendOnEventNeedingAckHandled(event.mMessage); + return true; +} + +bool +TabChild::RecvPasteTransferable(const IPCDataTransfer& aDataTransfer, + const bool& aIsPrivateData, + const IPC::Principal& aRequestingPrincipal) +{ + nsresult rv; + 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, nullptr, this); + NS_ENSURE_SUCCESS(rv, true); + + nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation()); + if (NS_WARN_IF(!ourDocShell)) { + return true; + } + + nsCOMPtr<nsICommandParams> params = do_CreateInstance("@mozilla.org/embedcomp/command-params;1", &rv); + NS_ENSURE_SUCCESS(rv, true); + + rv = params->SetISupportsValue("transferable", trans); + NS_ENSURE_SUCCESS(rv, true); + + ourDocShell->DoCommandWithParams("cmd_pasteTransferable", params); + return true; +} + + +a11y::PDocAccessibleChild* +TabChild::AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&, + const uint32_t&, const IAccessibleHolder&) +{ + MOZ_ASSERT(false, "should never call this!"); + return nullptr; +} + +bool +TabChild::DeallocPDocAccessibleChild(a11y::PDocAccessibleChild* aChild) +{ +#ifdef ACCESSIBILITY + delete static_cast<mozilla::a11y::DocAccessibleChild*>(aChild); +#endif + return true; +} + +PDocumentRendererChild* +TabChild::AllocPDocumentRendererChild(const nsRect& documentRect, + const mozilla::gfx::Matrix& transform, + const nsString& bgcolor, + const uint32_t& renderFlags, + const bool& flushLayout, + const nsIntSize& renderSize) +{ + return new DocumentRendererChild(); +} + +bool +TabChild::DeallocPDocumentRendererChild(PDocumentRendererChild* actor) +{ + delete actor; + return true; +} + +bool +TabChild::RecvPDocumentRendererConstructor(PDocumentRendererChild* actor, + const nsRect& documentRect, + const mozilla::gfx::Matrix& transform, + const nsString& bgcolor, + const uint32_t& renderFlags, + const bool& flushLayout, + const nsIntSize& renderSize) +{ + DocumentRendererChild *render = static_cast<DocumentRendererChild *>(actor); + + nsCOMPtr<nsIWebBrowser> browser = do_QueryInterface(WebNavigation()); + if (!browser) + return true; // silently ignore + nsCOMPtr<mozIDOMWindowProxy> window; + if (NS_FAILED(browser->GetContentDOMWindow(getter_AddRefs(window))) || + !window) + { + return true; // silently ignore + } + + nsCString data; + bool ret = render->RenderDocument(nsPIDOMWindowOuter::From(window), + documentRect, transform, + bgcolor, + renderFlags, flushLayout, + renderSize, data); + if (!ret) + return true; // silently ignore + + return PDocumentRendererChild::Send__delete__(actor, renderSize, data); +} + +PColorPickerChild* +TabChild::AllocPColorPickerChild(const nsString&, const nsString&) +{ + NS_RUNTIMEABORT("unused"); + return nullptr; +} + +bool +TabChild::DeallocPColorPickerChild(PColorPickerChild* aColorPicker) +{ + nsColorPickerProxy* picker = static_cast<nsColorPickerProxy*>(aColorPicker); + NS_RELEASE(picker); + return true; +} + +PDatePickerChild* +TabChild::AllocPDatePickerChild(const nsString&, const nsString&) +{ + NS_RUNTIMEABORT("unused"); + return nullptr; +} + +bool +TabChild::DeallocPDatePickerChild(PDatePickerChild* aDatePicker) +{ + nsDatePickerProxy* picker = static_cast<nsDatePickerProxy*>(aDatePicker); + NS_RELEASE(picker); + return true; +} + +PFilePickerChild* +TabChild::AllocPFilePickerChild(const nsString&, const int16_t&) +{ + NS_RUNTIMEABORT("unused"); + return nullptr; +} + +bool +TabChild::DeallocPFilePickerChild(PFilePickerChild* actor) +{ + nsFilePickerProxy* filePicker = static_cast<nsFilePickerProxy*>(actor); + NS_RELEASE(filePicker); + return true; +} + +auto +TabChild::AllocPIndexedDBPermissionRequestChild(const Principal& aPrincipal) + -> PIndexedDBPermissionRequestChild* +{ + MOZ_CRASH("PIndexedDBPermissionRequestChild actors should always be created " + "manually!"); +} + +bool +TabChild::DeallocPIndexedDBPermissionRequestChild( + PIndexedDBPermissionRequestChild* aActor) +{ + MOZ_ASSERT(aActor); + delete aActor; + return true; +} + +bool +TabChild::RecvActivateFrameEvent(const nsString& aType, const bool& capture) +{ + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + NS_ENSURE_TRUE(window, true); + nsCOMPtr<EventTarget> chromeHandler = + do_QueryInterface(window->GetChromeEventHandler()); + NS_ENSURE_TRUE(chromeHandler, true); + RefPtr<ContentListener> listener = new ContentListener(this); + chromeHandler->AddEventListener(aType, listener, capture); + return true; +} + +bool +TabChild::RecvLoadRemoteScript(const nsString& aURL, const bool& aRunInGlobalScope) +{ + if (!mGlobal && !InitTabChildGlobal()) + // This can happen if we're half-destroyed. It's not a fatal + // error. + return true; + + LoadScriptInternal(aURL, aRunInGlobalScope); + return true; +} + +bool +TabChild::RecvAsyncMessage(const nsString& aMessage, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData) +{ + if (!mTabChildGlobal) { + return true; + } + + // We should have a message manager if the global is alive, but it + // seems sometimes we don't. Assert in aurora/nightly, but don't + // crash in release builds. + MOZ_DIAGNOSTIC_ASSERT(mTabChildGlobal->mMessageManager); + if (!mTabChildGlobal->mMessageManager) { + return true; + } + + nsCOMPtr<nsIXPConnectJSObjectHolder> kungFuDeathGrip(GetGlobal()); + StructuredCloneData data; + UnpackClonedMessageDataForChild(aData, data); + RefPtr<nsFrameMessageManager> mm = + static_cast<nsFrameMessageManager*>(mTabChildGlobal->mMessageManager.get()); + CrossProcessCpowHolder cpows(Manager(), aCpows); + mm->ReceiveMessage(static_cast<EventTarget*>(mTabChildGlobal), nullptr, + aMessage, false, &data, &cpows, aPrincipal, nullptr); + return true; +} + +bool +TabChild::RecvSwappedWithOtherRemoteLoader(const IPCTabContext& aContext) +{ + nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation()); + if (NS_WARN_IF(!ourDocShell)) { + return true; + } + + nsCOMPtr<nsPIDOMWindowOuter> ourWindow = ourDocShell->GetWindow(); + if (NS_WARN_IF(!ourWindow)) { + return true; + } + + RefPtr<nsDocShell> docShell = static_cast<nsDocShell*>(ourDocShell.get()); + + nsCOMPtr<EventTarget> ourEventTarget = ourWindow->GetParentTarget(); + + docShell->SetInFrameSwap(true); + + nsContentUtils::FirePageShowEvent(ourDocShell, ourEventTarget, false); + nsContentUtils::FirePageHideEvent(ourDocShell, ourEventTarget); + + // Owner content type may have changed, so store the possibly updated context + // and notify others. + MaybeInvalidTabContext maybeContext(aContext); + if (!maybeContext.IsValid()) { + NS_ERROR(nsPrintfCString("Received an invalid TabContext from " + "the parent process. (%s)", + maybeContext.GetInvalidReason()).get()); + MOZ_CRASH("Invalid TabContext received from the parent process."); + } + + if (!UpdateTabContextAfterSwap(maybeContext.GetTabContext())) { + MOZ_CRASH("Update to TabContext after swap was denied."); + } + + // Since mIsMozBrowserElement may change in UpdateTabContextAfterSwap, so we + // call UpdateFrameType here to make sure the frameType on the docshell is + // correct. + UpdateFrameType(); + + // Ignore previous value of mTriedBrowserInit since owner content has changed. + mTriedBrowserInit = true; + // Initialize the child side of the browser element machinery, if appropriate. + if (IsMozBrowserOrApp()) { + RecvLoadRemoteScript(BROWSER_ELEMENT_CHILD_SCRIPT, true); + } + + nsContentUtils::FirePageShowEvent(ourDocShell, ourEventTarget, true); + + docShell->SetInFrameSwap(false); + + return true; +} + +bool +TabChild::RecvHandleAccessKey(const WidgetKeyboardEvent& aEvent, + nsTArray<uint32_t>&& aCharCodes, + const int32_t& aModifierMask) +{ + nsCOMPtr<nsIDocument> document(GetDocument()); + nsCOMPtr<nsIPresShell> presShell = document->GetShell(); + if (presShell) { + nsPresContext* pc = presShell->GetPresContext(); + if (pc) { + if (!pc->EventStateManager()-> + HandleAccessKey(&(const_cast<WidgetKeyboardEvent&>(aEvent)), + pc, aCharCodes, + aModifierMask, true)) { + // If no accesskey was found, inform the parent so that accesskeys on + // menus can be handled. + WidgetKeyboardEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + SendAccessKeyNotHandled(localEvent); + } + } + } + + return true; +} + +bool +TabChild::RecvAudioChannelChangeNotification(const uint32_t& aAudioChannel, + const float& aVolume, + const bool& aMuted) +{ + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + if (window) { + RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate(); + MOZ_ASSERT(service); + + service->SetAudioChannelVolume(window, + static_cast<AudioChannel>(aAudioChannel), + aVolume); + service->SetAudioChannelMuted(window, + static_cast<AudioChannel>(aAudioChannel), + aMuted); + } + + return true; +} + +bool +TabChild::RecvSetUseGlobalHistory(const bool& aUse) +{ + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + MOZ_ASSERT(docShell); + + nsresult rv = docShell->SetUseGlobalHistory(aUse); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to set UseGlobalHistory on TabChild docShell"); + } + + return true; +} + +bool +TabChild::RecvPrint(const uint64_t& aOuterWindowID, const PrintData& aPrintData) +{ +#ifdef NS_PRINTING + nsGlobalWindow* outerWindow = + nsGlobalWindow::GetOuterWindowWithId(aOuterWindowID); + if (NS_WARN_IF(!outerWindow)) { + return true; + } + + nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint = + do_GetInterface(outerWindow->AsOuter()); + if (NS_WARN_IF(!webBrowserPrint)) { + return true; + } + + nsCOMPtr<nsIPrintSettingsService> printSettingsSvc = + do_GetService("@mozilla.org/gfx/printsettings-service;1"); + if (NS_WARN_IF(!printSettingsSvc)) { + return true; + } + + nsCOMPtr<nsIPrintSettings> printSettings; + nsresult rv = + printSettingsSvc->GetNewPrintSettings(getter_AddRefs(printSettings)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return true; + } + + nsCOMPtr<nsIPrintSession> printSession = + do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return true; + } + + printSettings->SetPrintSession(printSession); + printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings); + rv = webBrowserPrint->Print(printSettings, nullptr); + if (NS_WARN_IF(NS_FAILED(rv))) { + return true; + } + +#endif + return true; +} + +bool +TabChild::RecvUpdateNativeWindowHandle(const uintptr_t& aNewHandle) +{ +#if defined(XP_WIN) && defined(ACCESSIBILITY) + mNativeWindowHandle = aNewHandle; + return true; +#else + return false; +#endif +} + +bool +TabChild::RecvDestroy() +{ + MOZ_ASSERT(mDestroyed == false); + mDestroyed = true; + + nsTArray<PContentPermissionRequestChild*> childArray = + nsContentPermissionUtils::GetContentPermissionRequestChildById(GetTabId()); + + // Need to close undeleted ContentPermissionRequestChilds before tab is closed. + for (auto& permissionRequestChild : childArray) { + auto child = static_cast<RemotePermissionRequest*>(permissionRequestChild); + child->Destroy(); + } + + while (mActiveSuppressDisplayport > 0) { + APZCCallbackHelper::SuppressDisplayport(false, nullptr); + mActiveSuppressDisplayport--; + } + + if (mTabChildGlobal) { + // Message handlers are called from the event loop, so it better be safe to + // run script. + MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); + mTabChildGlobal->DispatchTrustedEvent(NS_LITERAL_STRING("unload")); + } + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + + observerService->RemoveObserver(this, BEFORE_FIRST_PAINT); + + const nsAttrValue::EnumTable* table = + AudioChannelService::GetAudioChannelTable(); + + nsAutoCString topic; + for (uint32_t i = 0; table[i].tag; ++i) { + topic.Assign("audiochannel-activity-"); + topic.Append(table[i].tag); + + observerService->RemoveObserver(this, topic.get()); + } + + // XXX what other code in ~TabChild() should we be running here? + DestroyWindow(); + + // Bounce through the event loop once to allow any delayed teardown runnables + // that were just generated to have a chance to run. + nsCOMPtr<nsIRunnable> deleteRunnable = new DelayedDeleteRunnable(this); + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(deleteRunnable)); + + return true; +} + +bool +TabChild::RecvSetDocShellIsActive(const bool& aIsActive, + const bool& aPreserveLayers, + const uint64_t& aLayerObserverEpoch) +{ + // Since SetDocShellIsActive requests come in from both the hang monitor + // channel and the PContent channel, we have an ordering problem. This code + // ensures that we respect the order in which the requests were made and + // ignore stale requests. + if (mLayerObserverEpoch >= aLayerObserverEpoch) { + return true; + } + mLayerObserverEpoch = aLayerObserverEpoch; + + MOZ_ASSERT(mPuppetWidget); + MOZ_ASSERT(mPuppetWidget->GetLayerManager()); + MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() == + LayersBackend::LAYERS_CLIENT); + + auto clearForcePaint = MakeScopeExit([&] { + // We might force a paint, or we might already have painted and this is a + // no-op. In either case, once we exit this scope, we need to alert the + // ProcessHangMonitor that we've finished responding to what might have + // been a request to force paint. This is so that the BackgroundHangMonitor + // for force painting can be made to wait again. + if (aIsActive) { + ProcessHangMonitor::ClearForcePaint(); + } + }); + + // We send the current layer observer epoch to the compositor so that + // TabParent knows whether a layer update notification corresponds to the + // latest SetDocShellIsActive request that was made. + if (ClientLayerManager* clm = mPuppetWidget->GetLayerManager()->AsClientLayerManager()) { + clm->SetLayerObserverEpoch(aLayerObserverEpoch); + } + + // docshell is consider prerendered only if not active yet + mIsPrerendered &= !aIsActive; + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); + if (docShell) { + bool wasActive; + docShell->GetIsActive(&wasActive); + if (aIsActive && wasActive) { + // This request is a no-op. In this case, we still want a MozLayerTreeReady + // notification to fire in the parent (so that it knows that the child has + // updated its epoch). ForcePaintNoOp does that. + if (IPCOpen()) { + Unused << SendForcePaintNoOp(aLayerObserverEpoch); + return true; + } + } + + docShell->SetIsActive(aIsActive); + } + + if (aIsActive) { + MakeVisible(); + + // We don't use TabChildBase::GetPresShell() here because that would create + // a content viewer if one doesn't exist yet. Creating a content viewer can + // cause JS to run, which we want to avoid. nsIDocShell::GetPresShell + // returns null if no content viewer exists yet. + if (nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell()) { + if (nsIFrame* root = presShell->FrameConstructor()->GetRootFrame()) { + FrameLayerBuilder::InvalidateAllLayersForFrame( + nsLayoutUtils::GetDisplayRootFrame(root)); + root->SchedulePaint(); + } + + Telemetry::AutoTimer<Telemetry::TABCHILD_PAINT_TIME> timer; + // If we need to repaint, let's do that right away. No sense waiting until + // we get back to the event loop again. We suppress the display port so that + // we only paint what's visible. This ensures that the tab we're switching + // to paints as quickly as possible. + APZCCallbackHelper::SuppressDisplayport(true, presShell); + if (nsContentUtils::IsSafeToRunScript()) { + WebWidget()->PaintNowIfNeeded(); + } else { + RefPtr<nsViewManager> vm = presShell->GetViewManager(); + if (nsView* view = vm->GetRootView()) { + presShell->Paint(view, view->GetBounds(), + nsIPresShell::PAINT_LAYERS); + } + } + APZCCallbackHelper::SuppressDisplayport(false, presShell); + } + } else if (!aPreserveLayers) { + MakeHidden(); + } + + return true; +} + +bool +TabChild::RecvNavigateByKey(const bool& aForward, const bool& aForDocumentNavigation) +{ + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + if (fm) { + nsCOMPtr<nsIDOMElement> result; + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + + // Move to the first or last document. + uint32_t type = aForward ? + (aForDocumentNavigation ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FIRSTDOC) : + static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_ROOT)) : + (aForDocumentNavigation ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_LASTDOC) : + static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_LAST)); + fm->MoveFocus(window, nullptr, type, + nsIFocusManager::FLAG_BYKEY, getter_AddRefs(result)); + + // No valid root element was found, so move to the first focusable element. + if (!result && aForward && !aForDocumentNavigation) { + fm->MoveFocus(window, nullptr, nsIFocusManager::MOVEFOCUS_FIRST, + nsIFocusManager::FLAG_BYKEY, getter_AddRefs(result)); + } + + SendRequestFocus(false); + } + + return true; +} + +bool +TabChild::RecvHandledWindowedPluginKeyEvent( + const NativeEventData& aKeyEventData, + const bool& aIsConsumed) +{ + if (NS_WARN_IF(!mPuppetWidget)) { + return true; + } + mPuppetWidget->HandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed); + return true; +} + +PRenderFrameChild* +TabChild::AllocPRenderFrameChild() +{ + return new RenderFrameChild(); +} + +bool +TabChild::DeallocPRenderFrameChild(PRenderFrameChild* aFrame) +{ + delete aFrame; + return true; +} + +bool +TabChild::InitTabChildGlobal(FrameScriptLoading aScriptLoading) +{ + if (!mGlobal && !mTabChildGlobal) { + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation()); + NS_ENSURE_TRUE(window, false); + nsCOMPtr<EventTarget> chromeHandler = + do_QueryInterface(window->GetChromeEventHandler()); + NS_ENSURE_TRUE(chromeHandler, false); + + RefPtr<TabChildGlobal> scope = new TabChildGlobal(this); + mTabChildGlobal = scope; + + nsISupports* scopeSupports = NS_ISUPPORTS_CAST(EventTarget*, scope); + + NS_NAMED_LITERAL_CSTRING(globalId, "outOfProcessTabChildGlobal"); + NS_ENSURE_TRUE(InitChildGlobalInternal(scopeSupports, globalId), false); + + scope->Init(); + + nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(chromeHandler); + NS_ENSURE_TRUE(root, false); + root->SetParentTarget(scope); + } + + if (aScriptLoading != DONT_LOAD_SCRIPTS && !mTriedBrowserInit) { + mTriedBrowserInit = true; + // Initialize the child side of the browser element machinery, + // if appropriate. + if (IsMozBrowserOrApp()) { + RecvLoadRemoteScript(BROWSER_ELEMENT_CHILD_SCRIPT, true); + } + } + + return true; +} + +bool +TabChild::InitRenderingState(const TextureFactoryIdentifier& aTextureFactoryIdentifier, + const uint64_t& aLayersId, + PRenderFrameChild* aRenderFrame) +{ + mPuppetWidget->InitIMEState(); + + RenderFrameChild* remoteFrame = static_cast<RenderFrameChild*>(aRenderFrame); + if (!remoteFrame) { + NS_WARNING("failed to construct RenderFrame"); + return false; + } + + MOZ_ASSERT(aLayersId != 0); + mTextureFactoryIdentifier = aTextureFactoryIdentifier; + + // Pushing layers transactions directly to a separate + // compositor context. + PCompositorBridgeChild* compositorChild = CompositorBridgeChild::Get(); + if (!compositorChild) { + NS_WARNING("failed to get CompositorBridgeChild instance"); + PRenderFrameChild::Send__delete__(remoteFrame); + return false; + } + + ShadowLayerForwarder* lf = + mPuppetWidget->GetLayerManager( + nullptr, mTextureFactoryIdentifier.mParentBackend) + ->AsShadowForwarder(); + // As long as we are creating a ClientLayerManager for the puppet widget, + // lf must be non-null here. + MOZ_ASSERT(lf); + + if (lf) { + nsTArray<LayersBackend> backends; + backends.AppendElement(mTextureFactoryIdentifier.mParentBackend); + bool success; + PLayerTransactionChild* shadowManager = + compositorChild->SendPLayerTransactionConstructor(backends, + aLayersId, &mTextureFactoryIdentifier, &success); + if (!success) { + NS_WARNING("failed to properly allocate layer transaction"); + PRenderFrameChild::Send__delete__(remoteFrame); + return false; + } + + if (!shadowManager) { + NS_WARNING("failed to construct LayersChild"); + // This results in |remoteFrame| being deleted. + PRenderFrameChild::Send__delete__(remoteFrame); + return false; + } + + lf->SetShadowManager(shadowManager); + lf->IdentifyTextureHost(mTextureFactoryIdentifier); + ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier); + gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier); + } + + mRemoteFrame = remoteFrame; + if (aLayersId != 0) { + if (!sTabChildren) { + sTabChildren = new TabChildMap; + } + MOZ_ASSERT(!sTabChildren->Get(aLayersId)); + sTabChildren->Put(aLayersId, this); + mLayersId = aLayersId; + } + + mApzcTreeManager = CompositorBridgeChild::Get()->GetAPZCTreeManager(mLayersId); + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + + if (observerService) { + observerService->AddObserver(this, + BEFORE_FIRST_PAINT, + false); + } + return true; +} + +void +TabChild::GetDPI(float* aDPI) +{ + *aDPI = -1.0; + if (!mRemoteFrame) { + return; + } + + if (mDPI > 0) { + *aDPI = mDPI; + return; + } + + // Fallback to a sync call if needed. + SendGetDPI(aDPI); +} + +void +TabChild::GetDefaultScale(double* aScale) +{ + *aScale = -1.0; + if (!mRemoteFrame) { + return; + } + + if (mDefaultScale > 0) { + *aScale = mDefaultScale; + return; + } + + // Fallback to a sync call if needed. + SendGetDefaultScale(aScale); +} + +void +TabChild::GetWidgetRounding(int32_t* aRounding) +{ + *aRounding = 1; + if (!mRemoteFrame) { + return; + } + if (mRounding > 0) { + *aRounding = mRounding; + return; + } + + // Fallback to a sync call if needed. + SendGetWidgetRounding(aRounding); +} + +void +TabChild::GetMaxTouchPoints(uint32_t* aTouchPoints) +{ + // Fallback to a sync call. + SendGetMaxTouchPoints(aTouchPoints); +} + +void +TabChild::NotifyPainted() +{ + if (!mNotified) { + mRemoteFrame->SendNotifyCompositorTransaction(); + mNotified = true; + } +} + +void +TabChild::MakeVisible() +{ + if (mPuppetWidget && mPuppetWidget->IsVisible()) { + return; + } + + if (mPuppetWidget) { + mPuppetWidget->Show(true); + } +} + +void +TabChild::MakeHidden() +{ + if (mPuppetWidget && !mPuppetWidget->IsVisible()) { + return; + } + + CompositorBridgeChild* compositor = CompositorBridgeChild::Get(); + + // Clear cached resources directly. This avoids one extra IPC + // round-trip from CompositorBridgeChild to CompositorBridgeParent. + compositor->RecvClearCachedResources(mLayersId); + + if (mPuppetWidget) { + mPuppetWidget->Show(false); + } +} + +NS_IMETHODIMP +TabChild::GetMessageManager(nsIContentFrameMessageManager** aResult) +{ + if (mTabChildGlobal) { + NS_ADDREF(*aResult = mTabChildGlobal); + return NS_OK; + } + *aResult = nullptr; + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +TabChild::GetWebBrowserChrome(nsIWebBrowserChrome3** aWebBrowserChrome) +{ + NS_IF_ADDREF(*aWebBrowserChrome = mWebBrowserChrome); + return NS_OK; +} + +NS_IMETHODIMP +TabChild::SetWebBrowserChrome(nsIWebBrowserChrome3* aWebBrowserChrome) +{ + mWebBrowserChrome = aWebBrowserChrome; + return NS_OK; +} + +void +TabChild::SendRequestFocus(bool aCanFocus) +{ + PBrowserChild::SendRequestFocus(aCanFocus); +} + +void +TabChild::SendGetTabCount(uint32_t* tabCount) +{ + PBrowserChild::SendGetTabCount(tabCount); +} + +void +TabChild::EnableDisableCommands(const nsAString& aAction, + nsTArray<nsCString>& aEnabledCommands, + nsTArray<nsCString>& aDisabledCommands) +{ + PBrowserChild::SendEnableDisableCommands(PromiseFlatString(aAction), + aEnabledCommands, aDisabledCommands); +} + +NS_IMETHODIMP +TabChild::GetTabId(uint64_t* aId) +{ + *aId = GetTabId(); + return NS_OK; +} + +void +TabChild::SetTabId(const TabId& aTabId) +{ + MOZ_ASSERT(mUniqueId == 0); + + mUniqueId = aTabId; + NestedTabChildMap()[mUniqueId] = this; +} + +bool +TabChild::DoSendBlockingMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + JS::Handle<JSObject *> aCpows, + nsIPrincipal* aPrincipal, + nsTArray<StructuredCloneData>* aRetVal, + bool aIsSync) +{ + ClonedMessageData data; + if (!BuildClonedMessageDataForChild(Manager(), aData, data)) { + return false; + } + InfallibleTArray<CpowEntry> cpows; + if (aCpows && !Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { + return false; + } + if (aIsSync) { + return SendSyncMessage(PromiseFlatString(aMessage), data, cpows, + Principal(aPrincipal), aRetVal); + } + + return SendRpcMessage(PromiseFlatString(aMessage), data, cpows, + Principal(aPrincipal), aRetVal); +} + +nsresult +TabChild::DoSendAsyncMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + JS::Handle<JSObject *> aCpows, + nsIPrincipal* aPrincipal) +{ + ClonedMessageData data; + if (!BuildClonedMessageDataForChild(Manager(), aData, data)) { + return NS_ERROR_DOM_DATA_CLONE_ERR; + } + InfallibleTArray<CpowEntry> cpows; + if (aCpows && !Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { + return NS_ERROR_UNEXPECTED; + } + if (!SendAsyncMessage(PromiseFlatString(aMessage), cpows, + Principal(aPrincipal), data)) { + return NS_ERROR_UNEXPECTED; + } + return NS_OK; +} + +/* static */ nsTArray<RefPtr<TabChild>> +TabChild::GetAll() +{ + nsTArray<RefPtr<TabChild>> list; + if (!sTabChildren) { + return list; + } + + for (auto iter = sTabChildren->Iter(); !iter.Done(); iter.Next()) { + list.AppendElement(iter.Data()); + } + + return list; +} + +TabChild* +TabChild::GetFrom(nsIPresShell* aPresShell) +{ + nsIDocument* doc = aPresShell->GetDocument(); + if (!doc) { + return nullptr; + } + nsCOMPtr<nsIDocShell> docShell(doc->GetDocShell()); + return GetFrom(docShell); +} + +TabChild* +TabChild::GetFrom(uint64_t aLayersId) +{ + if (!sTabChildren) { + return nullptr; + } + return sTabChildren->Get(aLayersId); +} + +void +TabChild::DidComposite(uint64_t aTransactionId, + const TimeStamp& aCompositeStart, + const TimeStamp& aCompositeEnd) +{ + MOZ_ASSERT(mPuppetWidget); + MOZ_ASSERT(mPuppetWidget->GetLayerManager()); + MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() == + LayersBackend::LAYERS_CLIENT); + + RefPtr<ClientLayerManager> manager = mPuppetWidget->GetLayerManager()->AsClientLayerManager(); + + manager->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd); +} + +void +TabChild::DidRequestComposite(const TimeStamp& aCompositeReqStart, + const TimeStamp& aCompositeReqEnd) +{ + nsCOMPtr<nsIDocShell> docShellComPtr = do_GetInterface(WebNavigation()); + if (!docShellComPtr) { + return; + } + + nsDocShell* docShell = static_cast<nsDocShell*>(docShellComPtr.get()); + RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get(); + + if (timelines && timelines->HasConsumer(docShell)) { + // Since we're assuming that it's impossible for content JS to directly + // trigger a synchronous paint, we can avoid capturing a stack trace here, + // which means we won't run into JS engine reentrancy issues like bug + // 1310014. + timelines->AddMarkerForDocShell(docShell, + "CompositeForwardTransaction", aCompositeReqStart, + MarkerTracingType::START, MarkerStackRequest::NO_STACK); + timelines->AddMarkerForDocShell(docShell, + "CompositeForwardTransaction", aCompositeReqEnd, + MarkerTracingType::END, MarkerStackRequest::NO_STACK); + } +} + +void +TabChild::ClearCachedResources() +{ + MOZ_ASSERT(mPuppetWidget); + MOZ_ASSERT(mPuppetWidget->GetLayerManager()); + MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() == + LayersBackend::LAYERS_CLIENT); + + ClientLayerManager *manager = mPuppetWidget->GetLayerManager()->AsClientLayerManager(); + manager->ClearCachedResources(); +} + +void +TabChild::InvalidateLayers() +{ + MOZ_ASSERT(mPuppetWidget); + MOZ_ASSERT(mPuppetWidget->GetLayerManager()); + MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() == + LayersBackend::LAYERS_CLIENT); + + RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager(); + FrameLayerBuilder::InvalidateAllLayers(lm); +} + +void +TabChild::ReinitRendering() +{ + MOZ_ASSERT(mLayersId); + + // Before we establish a new PLayerTransaction, we must connect our layer tree + // id, CompositorBridge, and the widget compositor all together again. + // Normally this happens in TabParent before TabChild is given rendering + // information. + // + // In this case, we will send a sync message to our TabParent, which in turn + // will send a sync message to the Compositor of the widget owning this tab. + // This guarantees the correct association is in place before our + // PLayerTransaction constructor message arrives on the cross-process + // compositor bridge. + SendEnsureLayersConnected(); + + RefPtr<CompositorBridgeChild> cb = CompositorBridgeChild::Get(); + + bool success; + nsTArray<LayersBackend> ignored; + PLayerTransactionChild* shadowManager = + cb->SendPLayerTransactionConstructor(ignored, LayersId(), &mTextureFactoryIdentifier, &success); + if (!success) { + NS_WARNING("failed to re-allocate layer transaction"); + return; + } + + if (!shadowManager) { + NS_WARNING("failed to re-construct LayersChild"); + return; + } + + RefPtr<LayerManager> lm = mPuppetWidget->RecreateLayerManager(shadowManager); + ShadowLayerForwarder* lf = lm->AsShadowForwarder(); + lf->IdentifyTextureHost(mTextureFactoryIdentifier); + + mApzcTreeManager = CompositorBridgeChild::Get()->GetAPZCTreeManager(mLayersId); + if (mApzcTreeManager) { + APZChild* apz = ContentProcessController::Create(mUniqueId); + CompositorBridgeChild::Get()->SendPAPZConstructor(apz, mLayersId); + } + + nsCOMPtr<nsIDocument> doc(GetDocument()); + doc->NotifyLayerManagerRecreated(); +} + +void +TabChild::CompositorUpdated(const TextureFactoryIdentifier& aNewIdentifier) +{ + gfxPlatform::GetPlatform()->CompositorUpdated(); + + RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager(); + ClientLayerManager* clm = lm->AsClientLayerManager(); + MOZ_ASSERT(clm); + + mTextureFactoryIdentifier = aNewIdentifier; + clm->UpdateTextureFactoryIdentifier(aNewIdentifier); + FrameLayerBuilder::InvalidateAllLayers(clm); +} + +NS_IMETHODIMP +TabChild::OnShowTooltip(int32_t aXCoords, int32_t aYCoords, const char16_t *aTipText, + const char16_t *aTipDir) +{ + nsString str(aTipText); + nsString dir(aTipDir); + SendShowTooltip(aXCoords, aYCoords, str, dir); + return NS_OK; +} + +NS_IMETHODIMP +TabChild::OnHideTooltip() +{ + SendHideTooltip(); + return NS_OK; +} + +bool +TabChild::RecvRequestNotifyAfterRemotePaint() +{ + // Get the CompositorBridgeChild instance for this content thread. + CompositorBridgeChild* compositor = CompositorBridgeChild::Get(); + + // Tell the CompositorBridgeChild that, when it gets a RemotePaintIsReady + // message that it should forward it us so that we can bounce it to our + // RenderFrameParent. + compositor->RequestNotifyAfterRemotePaint(this); + return true; +} + +bool +TabChild::RecvUIResolutionChanged(const float& aDpi, + const int32_t& aRounding, + const double& aScale) +{ + ScreenIntSize oldScreenSize = GetInnerSize(); + mDPI = 0; + mRounding = 0; + mDefaultScale = 0; + static_cast<PuppetWidget*>(mPuppetWidget.get())->UpdateBackingScaleCache(aDpi, aRounding, aScale); + nsCOMPtr<nsIDocument> document(GetDocument()); + nsCOMPtr<nsIPresShell> presShell = document->GetShell(); + if (presShell) { + RefPtr<nsPresContext> presContext = presShell->GetPresContext(); + if (presContext) { + presContext->UIResolutionChangedSync(); + } + } + + ScreenIntSize screenSize = GetInnerSize(); + if (mHasValidInnerSize && oldScreenSize != screenSize) { + ScreenIntRect screenRect = GetOuterRect(); + mPuppetWidget->Resize(screenRect.x + mClientOffset.x + mChromeDisp.x, + screenRect.y + mClientOffset.y + mChromeDisp.y, + screenSize.width, screenSize.height, true); + + nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation()); + baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height, + nsIBaseWindow::eRepaint); + } + + return true; +} + +bool +TabChild::RecvThemeChanged(nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache) +{ + LookAndFeel::SetIntCache(aLookAndFeelIntCache); + nsCOMPtr<nsIDocument> document(GetDocument()); + nsCOMPtr<nsIPresShell> presShell = document->GetShell(); + if (presShell) { + RefPtr<nsPresContext> presContext = presShell->GetPresContext(); + if (presContext) { + presContext->ThemeChanged(); + } + } + return true; +} + +bool +TabChild::RecvSetFreshProcess() +{ + mIsFreshProcess = true; + return true; +} + +mozilla::plugins::PPluginWidgetChild* +TabChild::AllocPPluginWidgetChild() +{ + return new mozilla::plugins::PluginWidgetChild(); +} + +bool +TabChild::DeallocPPluginWidgetChild(mozilla::plugins::PPluginWidgetChild* aActor) +{ + delete aActor; + return true; +} + +nsresult +TabChild::CreatePluginWidget(nsIWidget* aParent, nsIWidget** aOut) +{ + *aOut = nullptr; + mozilla::plugins::PluginWidgetChild* child = + static_cast<mozilla::plugins::PluginWidgetChild*>(SendPPluginWidgetConstructor()); + if (!child) { + NS_ERROR("couldn't create PluginWidgetChild"); + return NS_ERROR_UNEXPECTED; + } + nsCOMPtr<nsIWidget> pluginWidget = nsIWidget::CreatePluginProxyWidget(this, child); + if (!pluginWidget) { + NS_ERROR("couldn't create PluginWidgetProxy"); + return NS_ERROR_UNEXPECTED; + } + + nsWidgetInitData initData; + initData.mWindowType = eWindowType_plugin_ipc_content; + initData.mUnicode = false; + initData.clipChildren = true; + initData.clipSiblings = true; + nsresult rv = pluginWidget->Create(aParent, nullptr, + LayoutDeviceIntRect(0, 0, 0, 0), + &initData); + if (NS_FAILED(rv)) { + NS_WARNING("Creating native plugin widget on the chrome side failed."); + } + pluginWidget.forget(aOut); + return rv; +} + +ScreenIntSize +TabChild::GetInnerSize() +{ + LayoutDeviceIntSize innerSize = + RoundedToInt(mUnscaledInnerSize * mPuppetWidget->GetDefaultScale()); + return ViewAs<ScreenPixel>(innerSize, PixelCastJustification::LayoutDeviceIsScreenForTabDims); +}; + +ScreenIntRect +TabChild::GetOuterRect() +{ + LayoutDeviceIntRect outerRect = + RoundedToInt(mUnscaledOuterRect * mPuppetWidget->GetDefaultScale()); + return ViewAs<ScreenPixel>(outerRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims); +} + +void +TabChild::ForcePaint(uint64_t aLayerObserverEpoch) +{ + if (!IPCOpen()) { + // Don't bother doing anything now. Better to wait until we receive the + // message on the PContent channel. + return; + } + + nsAutoScriptBlocker scriptBlocker; + RecvSetDocShellIsActive(true, false, aLayerObserverEpoch); +} + +/******************************************************************************* + * nsISHistoryListener + ******************************************************************************/ + +NS_IMETHODIMP +TabChildSHistoryListener::OnHistoryNewEntry(nsIURI *aNewURI, int32_t aOldIndex) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChildSHistoryListener::OnHistoryGoBack(nsIURI *aBackURI, bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChildSHistoryListener::OnHistoryGoForward(nsIURI *aForwardURI, bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChildSHistoryListener::OnHistoryReload(nsIURI *aReloadURI, uint32_t aReloadFlags, bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChildSHistoryListener::OnHistoryGotoIndex(int32_t aIndex, nsIURI *aGotoURI, bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChildSHistoryListener::OnHistoryPurge(int32_t aNumEntries, bool *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChildSHistoryListener::OnHistoryReplaceEntry(int32_t aIndex) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +TabChildSHistoryListener::OnLengthChange(int32_t aCount) +{ + RefPtr<TabChild> tabChild(mTabChild); + if (!tabChild) { + return NS_ERROR_FAILURE; + } + + if (aCount < 0) { + return NS_ERROR_FAILURE; + } + + return tabChild->SendNotifySessionHistoryChange(aCount) ? + NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +TabChildSHistoryListener::OnRequestCrossBrowserNavigation(uint32_t aIndex) +{ + RefPtr<TabChild> tabChild(mTabChild); + if (!tabChild) { + return NS_ERROR_FAILURE; + } + + return tabChild->SendRequestCrossBrowserNavigation(aIndex) ? + NS_OK : NS_ERROR_FAILURE; +} + +TabChildGlobal::TabChildGlobal(TabChildBase* aTabChild) +: mTabChild(aTabChild) +{ + SetIsNotDOMBinding(); +} + +TabChildGlobal::~TabChildGlobal() +{ +} + +void +TabChildGlobal::Init() +{ + NS_ASSERTION(!mMessageManager, "Re-initializing?!?"); + mMessageManager = new nsFrameMessageManager(mTabChild, + nullptr, + MM_CHILD); +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(TabChildGlobal) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TabChildGlobal, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mTabChild); + tmp->UnlinkHostObjectURIs(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TabChildGlobal, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTabChild) + tmp->TraverseHostObjectURIs(cb); +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TabChildGlobal) + NS_INTERFACE_MAP_ENTRY(nsIMessageListenerManager) + NS_INTERFACE_MAP_ENTRY(nsIMessageSender) + NS_INTERFACE_MAP_ENTRY(nsISyncMessageSender) + NS_INTERFACE_MAP_ENTRY(nsIContentFrameMessageManager) + NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) + NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ContentFrameMessageManager) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_ADDREF_INHERITED(TabChildGlobal, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(TabChildGlobal, DOMEventTargetHelper) + +// This method isn't automatically forwarded safely because it's notxpcom, so +// the IDL binding doesn't know what value to return. +NS_IMETHODIMP_(bool) +TabChildGlobal::MarkForCC() +{ + if (mTabChild) { + mTabChild->MarkScopesForCC(); + } + EventListenerManager* elm = GetExistingListenerManager(); + if (elm) { + elm->MarkForCC(); + } + return mMessageManager ? mMessageManager->MarkForCC() : false; +} + +NS_IMETHODIMP +TabChildGlobal::GetContent(mozIDOMWindowProxy** aContent) +{ + *aContent = nullptr; + if (!mTabChild) + return NS_ERROR_NULL_POINTER; + nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(mTabChild->WebNavigation()); + window.forget(aContent); + return NS_OK; +} + +NS_IMETHODIMP +TabChildGlobal::GetDocShell(nsIDocShell** aDocShell) +{ + *aDocShell = nullptr; + if (!mTabChild) + return NS_ERROR_NULL_POINTER; + nsCOMPtr<nsIDocShell> docShell = do_GetInterface(mTabChild->WebNavigation()); + docShell.swap(*aDocShell); + return NS_OK; +} + +nsIPrincipal* +TabChildGlobal::GetPrincipal() +{ + if (!mTabChild) + return nullptr; + return mTabChild->GetPrincipal(); +} + +JSObject* +TabChildGlobal::GetGlobalJSObject() +{ + NS_ENSURE_TRUE(mTabChild, nullptr); + nsCOMPtr<nsIXPConnectJSObjectHolder> ref = mTabChild->GetGlobal(); + NS_ENSURE_TRUE(ref, nullptr); + return ref->GetJSObject(); +} diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h new file mode 100644 index 000000000..b23c7c19e --- /dev/null +++ b/dom/ipc/TabChild.h @@ -0,0 +1,818 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_TabChild_h +#define mozilla_dom_TabChild_h + +#include "mozilla/dom/PBrowserChild.h" +#include "nsIWebNavigation.h" +#include "nsCOMPtr.h" +#include "nsAutoPtr.h" +#include "nsIWebBrowserChrome2.h" +#include "nsIEmbeddingSiteWindow.h" +#include "nsIWebBrowserChromeFocus.h" +#include "nsIDOMEventListener.h" +#include "nsIInterfaceRequestor.h" +#include "nsIWindowProvider.h" +#include "nsIDOMWindow.h" +#include "nsIDocShell.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsFrameMessageManager.h" +#include "nsIPresShell.h" +#include "nsIScriptObjectPrincipal.h" +#include "nsWeakReference.h" +#include "nsITabChild.h" +#include "nsITooltipListener.h" +#include "mozilla/Attributes.h" +#include "mozilla/dom/TabContext.h" +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/EventForwards.h" +#include "mozilla/layers/CompositorTypes.h" +#include "mozilla/layers/APZCCallbackHelper.h" +#include "nsIWebBrowserChrome3.h" +#include "mozilla/dom/ipc/IdType.h" +#include "AudioChannelService.h" +#include "PuppetWidget.h" +#include "mozilla/layers/GeckoContentController.h" +#include "nsISHistoryListener.h" +#include "nsIPartialSHistoryListener.h" + +class nsIDOMWindowUtils; +class nsIHttpChannel; + +namespace mozilla { +namespace layout { +class RenderFrameChild; +} // namespace layout + +namespace layers { +class APZChild; +class APZEventState; +class AsyncDragMetrics; +class IAPZCTreeManager; +class ImageCompositeNotification; +} // namespace layers + +namespace widget { +struct AutoCacheNativeKeyCommands; +} // namespace widget + +namespace plugins { +class PluginWidgetChild; +} // namespace plugins + +namespace dom { + +class TabChild; +class ClonedMessageData; +class TabChildBase; + +class TabChildGlobal : public DOMEventTargetHelper, + public nsIContentFrameMessageManager, + public nsIScriptObjectPrincipal, + public nsIGlobalObject, + public nsSupportsWeakReference +{ +public: + explicit TabChildGlobal(TabChildBase* aTabChild); + void Init(); + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TabChildGlobal, DOMEventTargetHelper) + NS_FORWARD_SAFE_NSIMESSAGELISTENERMANAGER(mMessageManager) + NS_FORWARD_SAFE_NSIMESSAGESENDER(mMessageManager) + NS_FORWARD_SAFE_NSIMESSAGEMANAGERGLOBAL(mMessageManager) + NS_IMETHOD SendSyncMessage(const nsAString& aMessageName, + JS::Handle<JS::Value> aObject, + JS::Handle<JS::Value> aRemote, + nsIPrincipal* aPrincipal, + JSContext* aCx, + uint8_t aArgc, + JS::MutableHandle<JS::Value> aRetval) override + { + return mMessageManager + ? mMessageManager->SendSyncMessage(aMessageName, aObject, aRemote, + aPrincipal, aCx, aArgc, aRetval) + : NS_ERROR_NULL_POINTER; + } + NS_IMETHOD SendRpcMessage(const nsAString& aMessageName, + JS::Handle<JS::Value> aObject, + JS::Handle<JS::Value> aRemote, + nsIPrincipal* aPrincipal, + JSContext* aCx, + uint8_t aArgc, + JS::MutableHandle<JS::Value> aRetval) override + { + return mMessageManager + ? mMessageManager->SendRpcMessage(aMessageName, aObject, aRemote, + aPrincipal, aCx, aArgc, aRetval) + : NS_ERROR_NULL_POINTER; + } + NS_IMETHOD GetContent(mozIDOMWindowProxy** aContent) override; + NS_IMETHOD GetDocShell(nsIDocShell** aDocShell) override; + + nsresult AddEventListener(const nsAString& aType, + nsIDOMEventListener* aListener, + bool aUseCapture) + { + // By default add listeners only for trusted events! + return DOMEventTargetHelper::AddEventListener(aType, aListener, + aUseCapture, false, 2); + } + using DOMEventTargetHelper::AddEventListener; + NS_IMETHOD AddEventListener(const nsAString& aType, + nsIDOMEventListener* aListener, + bool aUseCapture, bool aWantsUntrusted, + uint8_t optional_argc) override + { + return DOMEventTargetHelper::AddEventListener(aType, aListener, + aUseCapture, + aWantsUntrusted, + optional_argc); + } + + nsresult + PreHandleEvent(EventChainPreVisitor& aVisitor) override + { + aVisitor.mForceContentDispatch = true; + return NS_OK; + } + + virtual nsIPrincipal* GetPrincipal() override; + virtual JSObject* GetGlobalJSObject() override; + + virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override + { + MOZ_CRASH("TabChildGlobal doesn't use DOM bindings!"); + } + + nsCOMPtr<nsIContentFrameMessageManager> mMessageManager; + RefPtr<TabChildBase> mTabChild; + +protected: + ~TabChildGlobal(); +}; + +class ContentListener final : public nsIDOMEventListener +{ +public: + explicit ContentListener(TabChild* aTabChild) : mTabChild(aTabChild) {} + NS_DECL_ISUPPORTS + NS_DECL_NSIDOMEVENTLISTENER +protected: + ~ContentListener() {} + TabChild* mTabChild; +}; + +/** + * Listens on session history change, and sends NotifySessionHistoryChange to + * parent process. + */ +class TabChildSHistoryListener final : public nsISHistoryListener, + public nsIPartialSHistoryListener, + public nsSupportsWeakReference +{ +public: + explicit TabChildSHistoryListener(TabChild* aTabChild) : mTabChild(aTabChild) {} + void ClearTabChild() { mTabChild = nullptr; } + + NS_DECL_ISUPPORTS + NS_DECL_NSISHISTORYLISTENER + NS_DECL_NSIPARTIALSHISTORYLISTENER + +private: + ~TabChildSHistoryListener() {} + TabChild* mTabChild; +}; + +// This is base clase which helps to share Viewport and touch related +// functionality between b2g/android FF/embedlite clients implementation. +// It make sense to place in this class all helper functions, and functionality +// which could be shared between Cross-process/Cross-thread implmentations. +class TabChildBase : public nsISupports, + public nsMessageManagerScriptExecutor, + public ipc::MessageManagerCallback +{ +protected: + typedef mozilla::widget::PuppetWidget PuppetWidget; + +public: + TabChildBase(); + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TabChildBase) + + virtual nsIWebNavigation* WebNavigation() const = 0; + virtual PuppetWidget* WebWidget() = 0; + nsIPrincipal* GetPrincipal() { return mPrincipal; } + virtual bool DoUpdateZoomConstraints(const uint32_t& aPresShellId, + const mozilla::layers::FrameMetrics::ViewID& aViewId, + const Maybe<mozilla::layers::ZoomConstraints>& aConstraints) = 0; + + virtual ScreenIntSize GetInnerSize() = 0; + + // Get the Document for the top-level window in this tab. + already_AddRefed<nsIDocument> GetDocument() const; + + // Get the pres-shell of the document for the top-level window in this tab. + already_AddRefed<nsIPresShell> GetPresShell() const; + +protected: + virtual ~TabChildBase(); + + // Wraps up a JSON object as a structured clone and sends it to the browser + // chrome script. + // + // XXX/bug 780335: Do the work the browser chrome script does in C++ instead + // so we don't need things like this. + void DispatchMessageManagerMessage(const nsAString& aMessageName, + const nsAString& aJSONData); + + void ProcessUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics); + + bool UpdateFrameHandler(const mozilla::layers::FrameMetrics& aFrameMetrics); + +protected: + RefPtr<TabChildGlobal> mTabChildGlobal; + nsCOMPtr<nsIWebBrowserChrome3> mWebBrowserChrome; +}; + +class TabChild final : public TabChildBase, + public PBrowserChild, + public nsIWebBrowserChrome2, + public nsIEmbeddingSiteWindow, + public nsIWebBrowserChromeFocus, + public nsIInterfaceRequestor, + public nsIWindowProvider, + public nsSupportsWeakReference, + public nsITabChild, + public nsIObserver, + public TabContext, + public nsITooltipListener, + public mozilla::ipc::IShmemAllocator +{ + typedef mozilla::dom::ClonedMessageData ClonedMessageData; + typedef mozilla::layout::RenderFrameChild RenderFrameChild; + typedef mozilla::layers::APZEventState APZEventState; + typedef mozilla::layers::SetAllowedTouchBehaviorCallback SetAllowedTouchBehaviorCallback; + +public: + /** + * Find TabChild of aTabId in the same content process of the + * caller. + */ + static already_AddRefed<TabChild> FindTabChild(const TabId& aTabId); + + // Return a list of all active TabChildren. + static nsTArray<RefPtr<TabChild>> GetAll(); + +public: + /** + * Create a new TabChild object. + */ + TabChild(nsIContentChild* aManager, + const TabId& aTabId, + const TabContext& aContext, + uint32_t aChromeFlags); + + nsresult Init(); + + /** + * This is expected to be called off the critical path to content + * startup. This is an opportunity to load things that are slow + * on the critical path. + */ + static void PreloadSlowThings(); + + /** Return a TabChild with the given attributes. */ + static already_AddRefed<TabChild> + Create(nsIContentChild* aManager, const TabId& aTabId, + const TabContext& aContext, uint32_t aChromeFlags); + + // Let managees query if it is safe to send messages. + bool IsDestroyed() const{ return mDestroyed; } + + const TabId GetTabId() const + { + MOZ_ASSERT(mUniqueId != 0); + return mUniqueId; + } + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIWEBBROWSERCHROME + NS_DECL_NSIWEBBROWSERCHROME2 + NS_DECL_NSIEMBEDDINGSITEWINDOW + NS_DECL_NSIWEBBROWSERCHROMEFOCUS + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIWINDOWPROVIDER + NS_DECL_NSITABCHILD + NS_DECL_NSIOBSERVER + NS_DECL_NSITOOLTIPLISTENER + + FORWARD_SHMEM_ALLOCATOR_TO(PBrowserChild) + + /** + * MessageManagerCallback methods that we override. + */ + virtual bool DoSendBlockingMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + JS::Handle<JSObject *> aCpows, + nsIPrincipal* aPrincipal, + nsTArray<StructuredCloneData>* aRetVal, + bool aIsSync) override; + + virtual nsresult DoSendAsyncMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + JS::Handle<JSObject *> aCpows, + nsIPrincipal* aPrincipal) override; + + virtual bool + DoUpdateZoomConstraints(const uint32_t& aPresShellId, + const ViewID& aViewId, + const Maybe<ZoomConstraints>& aConstraints) override; + + virtual bool RecvLoadURL(const nsCString& aURI, + const ShowInfo& aInfo) override; + virtual bool + RecvShow(const ScreenIntSize& aSize, + const ShowInfo& aInfo, + const TextureFactoryIdentifier& aTextureFactoryIdentifier, + const uint64_t& aLayersId, + PRenderFrameChild* aRenderFrame, + const bool& aParentIsActive, + const nsSizeMode& aSizeMode) override; + + virtual bool + RecvUpdateDimensions(const CSSRect& aRect, + const CSSSize& aSize, + const ScreenOrientationInternal& aOrientation, + const LayoutDeviceIntPoint& aClientOffset, + const LayoutDeviceIntPoint& aChromeDisp) override; + virtual bool + RecvSizeModeChanged(const nsSizeMode& aSizeMode) override; + + virtual bool RecvActivate() override; + + virtual bool RecvDeactivate() override; + + virtual bool RecvMouseEvent(const nsString& aType, + const float& aX, + const float& aY, + const int32_t& aButton, + const int32_t& aClickCount, + const int32_t& aModifiers, + const bool& aIgnoreRootScrollFrame) override; + + virtual bool RecvRealMouseMoveEvent(const mozilla::WidgetMouseEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) override; + + virtual bool RecvSynthMouseMoveEvent(const mozilla::WidgetMouseEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) override; + + virtual bool RecvRealMouseButtonEvent(const mozilla::WidgetMouseEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) override; + + virtual bool RecvRealDragEvent(const WidgetDragEvent& aEvent, + const uint32_t& aDragAction, + const uint32_t& aDropEffect) override; + + virtual bool + RecvRealKeyEvent(const mozilla::WidgetKeyboardEvent& aEvent, + const MaybeNativeKeyBinding& aBindings) override; + + virtual bool RecvMouseWheelEvent(const mozilla::WidgetWheelEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) override; + + virtual bool RecvRealTouchEvent(const WidgetTouchEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId, + const nsEventStatus& aApzResponse) override; + + virtual bool + RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId, + const nsEventStatus& aApzResponse) override; + + virtual bool RecvKeyEvent(const nsString& aType, + const int32_t& aKeyCode, + const int32_t& aCharCode, + const int32_t& aModifiers, + const bool& aPreventDefault) override; + + virtual bool RecvNativeSynthesisResponse(const uint64_t& aObserverId, + const nsCString& aResponse) override; + + virtual bool RecvPluginEvent(const WidgetPluginEvent& aEvent) override; + + virtual bool + RecvCompositionEvent(const mozilla::WidgetCompositionEvent& aEvent) override; + + virtual bool + RecvSelectionEvent(const mozilla::WidgetSelectionEvent& aEvent) override; + + virtual bool + RecvPasteTransferable(const IPCDataTransfer& aDataTransfer, + const bool& aIsPrivateData, + const IPC::Principal& aRequestingPrincipal) override; + + virtual bool + RecvActivateFrameEvent(const nsString& aType, const bool& aCapture) override; + + virtual bool RecvLoadRemoteScript(const nsString& aURL, + const bool& aRunInGlobalScope) override; + + virtual bool RecvAsyncMessage(const nsString& aMessage, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData) override; + + virtual bool + RecvSwappedWithOtherRemoteLoader(const IPCTabContext& aContext) override; + + virtual PDocAccessibleChild* + AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&, + const uint32_t&, const IAccessibleHolder&) override; + + virtual bool DeallocPDocAccessibleChild(PDocAccessibleChild*) override; + + virtual PDocumentRendererChild* + AllocPDocumentRendererChild(const nsRect& aDocumentRect, + const gfx::Matrix& aTransform, + const nsString& aBggcolor, + const uint32_t& aRenderFlags, + const bool& aFlushLayout, + const nsIntSize& arenderSize) override; + + virtual bool + DeallocPDocumentRendererChild(PDocumentRendererChild* aCctor) override; + + virtual bool + RecvPDocumentRendererConstructor(PDocumentRendererChild* aActor, + const nsRect& aDocumentRect, + const gfx::Matrix& aTransform, + const nsString& aBgcolor, + const uint32_t& aRenderFlags, + const bool& aFlushLayout, + const nsIntSize& aRenderSize) override; + + + virtual PColorPickerChild* + AllocPColorPickerChild(const nsString& aTitle, + const nsString& aInitialColor) override; + + virtual bool DeallocPColorPickerChild(PColorPickerChild* aActor) override; + + virtual PDatePickerChild* + AllocPDatePickerChild(const nsString& title, const nsString& initialDate) override; + virtual bool DeallocPDatePickerChild(PDatePickerChild* actor) override; + + virtual PFilePickerChild* + AllocPFilePickerChild(const nsString& aTitle, const int16_t& aMode) override; + + virtual bool + DeallocPFilePickerChild(PFilePickerChild* aActor) override; + + virtual PIndexedDBPermissionRequestChild* + AllocPIndexedDBPermissionRequestChild(const Principal& aPrincipal) override; + + virtual bool + DeallocPIndexedDBPermissionRequestChild(PIndexedDBPermissionRequestChild* aActor) override; + + virtual nsIWebNavigation* WebNavigation() const override + { + return mWebNav; + } + + virtual PuppetWidget* WebWidget() override { return mPuppetWidget; } + + /** Return the DPI of the widget this TabChild draws to. */ + void GetDPI(float* aDPI); + + void GetDefaultScale(double *aScale); + + void GetWidgetRounding(int32_t* aRounding); + + bool IsTransparent() const { return mIsTransparent; } + + void GetMaxTouchPoints(uint32_t* aTouchPoints); + + ScreenOrientationInternal GetOrientation() const { return mOrientation; } + + void SetBackgroundColor(const nscolor& aColor); + + void NotifyPainted(); + + void RequestNativeKeyBindings(mozilla::widget::AutoCacheNativeKeyCommands* aAutoCache, + const WidgetKeyboardEvent* aEvent); + + /** + * Signal to this TabChild that it should be made visible: + * activated widget, retained layer tree, etc. (Respectively, + * made not visible.) + */ + void MakeVisible(); + void MakeHidden(); + + nsIContentChild* Manager() const { return mManager; } + + static inline TabChild* + GetFrom(nsIDocShell* aDocShell) + { + if (!aDocShell) { + return nullptr; + } + + nsCOMPtr<nsITabChild> tc = aDocShell->GetTabChild(); + return static_cast<TabChild*>(tc.get()); + } + + static inline TabChild* + GetFrom(mozIDOMWindow* aWindow) + { + nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow); + nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav); + return GetFrom(docShell); + } + + static inline TabChild* + GetFrom(mozIDOMWindowProxy* aWindow) + { + nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow); + nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav); + return GetFrom(docShell); + } + + static TabChild* GetFrom(nsIPresShell* aPresShell); + static TabChild* GetFrom(uint64_t aLayersId); + + uint64_t LayersId() { return mLayersId; } + + void DidComposite(uint64_t aTransactionId, + const TimeStamp& aCompositeStart, + const TimeStamp& aCompositeEnd); + + void DidRequestComposite(const TimeStamp& aCompositeReqStart, + const TimeStamp& aCompositeReqEnd); + + void ClearCachedResources(); + void InvalidateLayers(); + void ReinitRendering(); + void CompositorUpdated(const TextureFactoryIdentifier& aNewIdentifier); + + static inline TabChild* GetFrom(nsIDOMWindow* aWindow) + { + nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow); + nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav); + return GetFrom(docShell); + } + + virtual bool RecvUIResolutionChanged(const float& aDpi, + const int32_t& aRounding, + const double& aScale) override; + + virtual bool + RecvThemeChanged(nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache) override; + + virtual bool RecvHandleAccessKey(const WidgetKeyboardEvent& aEvent, + nsTArray<uint32_t>&& aCharCodes, + const int32_t& aModifierMask) override; + + virtual bool RecvAudioChannelChangeNotification(const uint32_t& aAudioChannel, + const float& aVolume, + const bool& aMuted) override; + + virtual bool RecvSetUseGlobalHistory(const bool& aUse) override; + + virtual bool RecvHandledWindowedPluginKeyEvent( + const mozilla::NativeEventData& aKeyEventData, + const bool& aIsConsumed) override; + + virtual bool RecvPrint(const uint64_t& aOuterWindowID, + const PrintData& aPrintData) override; + + virtual bool RecvUpdateNativeWindowHandle(const uintptr_t& aNewHandle) override; + + /** + * Native widget remoting protocol for use with windowed plugins with e10s. + */ + PPluginWidgetChild* AllocPPluginWidgetChild() override; + + bool DeallocPPluginWidgetChild(PPluginWidgetChild* aActor) override; + + nsresult CreatePluginWidget(nsIWidget* aParent, nsIWidget** aOut); + + LayoutDeviceIntPoint GetClientOffset() const { return mClientOffset; } + LayoutDeviceIntPoint GetChromeDisplacement() const { return mChromeDisp; }; + + bool IPCOpen() const { return mIPCOpen; } + + bool ParentIsActive() const + { + return mParentIsActive; + } + + bool AsyncPanZoomEnabled() const { return mAsyncPanZoomEnabled; } + + virtual ScreenIntSize GetInnerSize() override; + + // Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow(). + void DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier, + const uint64_t& aLayersId, + PRenderFrameChild* aRenderFrame, + const ShowInfo& aShowInfo); + + void ContentReceivedInputBlock(const ScrollableLayerGuid& aGuid, + uint64_t aInputBlockId, + bool aPreventDefault) const; + void SetTargetAPZC(uint64_t aInputBlockId, + const nsTArray<ScrollableLayerGuid>& aTargets) const; + bool RecvHandleTap(const layers::GeckoContentController::TapType& aType, + const LayoutDevicePoint& aPoint, + const Modifiers& aModifiers, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) override; + void SetAllowedTouchBehavior(uint64_t aInputBlockId, + const nsTArray<TouchBehaviorFlags>& aFlags) const; + + bool UpdateFrame(const FrameMetrics& aFrameMetrics); + bool NotifyAPZStateChange(const ViewID& aViewId, + const layers::GeckoContentController::APZStateChange& aChange, + const int& aArg); + void StartScrollbarDrag(const layers::AsyncDragMetrics& aDragMetrics); + void ZoomToRect(const uint32_t& aPresShellId, + const FrameMetrics::ViewID& aViewId, + const CSSRect& aRect, + const uint32_t& aFlags); + + // Request that the docshell be marked as active. + void ForcePaint(uint64_t aLayerObserverEpoch); + +#if defined(XP_WIN) && defined(ACCESSIBILITY) + uintptr_t GetNativeWindowHandle() const { return mNativeWindowHandle; } +#endif + + bool TakeIsFreshProcess() + { + bool wasFreshProcess = mIsFreshProcess; + mIsFreshProcess = false; + return wasFreshProcess; + } + +protected: + virtual ~TabChild(); + + virtual PRenderFrameChild* AllocPRenderFrameChild() override; + + virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override; + + virtual bool RecvDestroy() override; + + virtual bool RecvSetDocShellIsActive(const bool& aIsActive, + const bool& aIsHidden, + const uint64_t& aLayerObserverEpoch) override; + + virtual bool RecvNavigateByKey(const bool& aForward, + const bool& aForDocumentNavigation) override; + + virtual bool RecvRequestNotifyAfterRemotePaint() override; + + virtual bool RecvSuppressDisplayport(const bool& aEnabled) override; + + virtual bool RecvParentActivated(const bool& aActivated) override; + + virtual bool RecvSetKeyboardIndicators(const UIStateChangeType& aShowAccelerators, + const UIStateChangeType& aShowFocusRings) override; + + virtual bool RecvStopIMEStateManagement() override; + + virtual bool RecvMenuKeyboardListenerInstalled( + const bool& aInstalled) override; + + virtual bool RecvNotifyAttachGroupedSessionHistory(const uint32_t& aOffset) override; + + virtual bool RecvNotifyPartialSessionHistoryActive(const uint32_t& aGlobalLength, + const uint32_t& aTargetLocalIndex) override; + + virtual bool RecvNotifyPartialSessionHistoryDeactive() override; + + virtual bool RecvSetFreshProcess() override; + +private: + void HandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, + const ScrollableLayerGuid& aGuid); + + // Notify others that our TabContext has been updated. + // + // You should call this after calling TabContext::SetTabContext(). We also + // call this during Init(). + // + // @param aIsPreallocated true if this is called for Preallocated Tab. + void NotifyTabContextUpdated(bool aIsPreallocated); + + // Update the frameType on our docshell. + void UpdateFrameType(); + + void ActorDestroy(ActorDestroyReason why) override; + + enum FrameScriptLoading { DONT_LOAD_SCRIPTS, DEFAULT_LOAD_SCRIPTS }; + + bool InitTabChildGlobal(FrameScriptLoading aScriptLoading = DEFAULT_LOAD_SCRIPTS); + + bool InitRenderingState(const TextureFactoryIdentifier& aTextureFactoryIdentifier, + const uint64_t& aLayersId, + PRenderFrameChild* aRenderFrame); + + void DestroyWindow(); + + void SetProcessNameToAppName(); + + void ApplyShowInfo(const ShowInfo& aInfo); + + bool HasValidInnerSize(); + + void SetTabId(const TabId& aTabId); + + ScreenIntRect GetOuterRect(); + + void SetUnscaledInnerSize(const CSSSize& aSize) + { + mUnscaledInnerSize = aSize; + } + + class DelayedDeleteRunnable; + + TextureFactoryIdentifier mTextureFactoryIdentifier; + nsCOMPtr<nsIWebNavigation> mWebNav; + RefPtr<PuppetWidget> mPuppetWidget; + nsCOMPtr<nsIURI> mLastURI; + RenderFrameChild* mRemoteFrame; + RefPtr<nsIContentChild> mManager; + RefPtr<TabChildSHistoryListener> mHistoryListener; + uint32_t mChromeFlags; + int32_t mActiveSuppressDisplayport; + uint64_t mLayersId; + CSSRect mUnscaledOuterRect; + nscolor mLastBackgroundColor; + bool mDidFakeShow; + bool mNotified; + bool mTriedBrowserInit; + ScreenOrientationInternal mOrientation; + + bool mIgnoreKeyPressEvent; + RefPtr<APZEventState> mAPZEventState; + SetAllowedTouchBehaviorCallback mSetAllowedTouchBehaviorCallback; + bool mHasValidInnerSize; + bool mDestroyed; + // Position of client area relative to the outer window + LayoutDeviceIntPoint mClientOffset; + // Position of tab, relative to parent widget (typically the window) + LayoutDeviceIntPoint mChromeDisp; + TabId mUniqueId; + + friend class ContentChild; + float mDPI; + int32_t mRounding; + double mDefaultScale; + + bool mIsTransparent; + + bool mIPCOpen; + bool mParentIsActive; + bool mAsyncPanZoomEnabled; + CSSSize mUnscaledInnerSize; + bool mDidSetRealShowInfo; + bool mDidLoadURLInit; + bool mIsFreshProcess; + + AutoTArray<bool, NUMBER_OF_AUDIO_CHANNELS> mAudioChannelsActive; + + RefPtr<layers::IAPZCTreeManager> mApzcTreeManager; + // APZChild clears this pointer from its destructor, so it shouldn't be a + // dangling pointer. + layers::APZChild* mAPZChild; + + // The most recently seen layer observer epoch in RecvSetDocShellIsActive. + uint64_t mLayerObserverEpoch; + +#if defined(XP_WIN) && defined(ACCESSIBILITY) + // The handle associated with the native window that contains this tab + uintptr_t mNativeWindowHandle; +#endif // defined(XP_WIN) + + DISALLOW_EVIL_CONSTRUCTORS(TabChild); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_TabChild_h diff --git a/dom/ipc/TabContext.cpp b/dom/ipc/TabContext.cpp new file mode 100644 index 000000000..b36dbc5eb --- /dev/null +++ b/dom/ipc/TabContext.cpp @@ -0,0 +1,434 @@ +/* -*- 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/dom/TabContext.h" +#include "mozilla/dom/PTabContext.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/dom/TabChild.h" +#include "nsIAppsService.h" +#include "nsIScriptSecurityManager.h" +#include "nsServiceManagerUtils.h" + +#define NO_APP_ID (nsIScriptSecurityManager::NO_APP_ID) + +using namespace mozilla::dom::ipc; +using namespace mozilla::layout; + +namespace mozilla { +namespace dom { + +TabContext::TabContext() + : mIsPrerendered(false) + , mInitialized(false) + , mIsMozBrowserElement(false) + , mContainingAppId(NO_APP_ID) + , mShowAccelerators(UIStateChangeType_NoChange) + , mShowFocusRings(UIStateChangeType_NoChange) +{ +} + +bool +TabContext::IsMozBrowserElement() const +{ + return mIsMozBrowserElement; +} + +bool +TabContext::IsIsolatedMozBrowserElement() const +{ + return mOriginAttributes.mInIsolatedMozBrowser; +} + +bool +TabContext::IsMozBrowserOrApp() const +{ + return HasOwnApp() || IsMozBrowserElement(); +} + +uint32_t +TabContext::OwnAppId() const +{ + return mOriginAttributes.mAppId; +} + +already_AddRefed<mozIApplication> +TabContext::GetOwnApp() const +{ + nsCOMPtr<mozIApplication> ownApp = mOwnApp; + return ownApp.forget(); +} + +bool +TabContext::HasOwnApp() const +{ + nsCOMPtr<mozIApplication> ownApp = GetOwnApp(); + return !!ownApp; +} + +uint32_t +TabContext::BrowserOwnerAppId() const +{ + if (IsMozBrowserElement()) { + return mContainingAppId; + } + return NO_APP_ID; +} + +already_AddRefed<mozIApplication> +TabContext::GetBrowserOwnerApp() const +{ + nsCOMPtr<mozIApplication> ownerApp; + if (IsMozBrowserElement()) { + ownerApp = mContainingApp; + } + return ownerApp.forget(); +} + +bool +TabContext::HasBrowserOwnerApp() const +{ + nsCOMPtr<mozIApplication> ownerApp = GetBrowserOwnerApp(); + return !!ownerApp; +} + +uint32_t +TabContext::AppOwnerAppId() const +{ + if (HasOwnApp()) { + return mContainingAppId; + } + return NO_APP_ID; +} + +already_AddRefed<mozIApplication> +TabContext::GetAppOwnerApp() const +{ + nsCOMPtr<mozIApplication> ownerApp; + if (HasOwnApp()) { + ownerApp = mContainingApp; + } + return ownerApp.forget(); +} + +bool +TabContext::HasAppOwnerApp() const +{ + nsCOMPtr<mozIApplication> ownerApp = GetAppOwnerApp(); + return !!ownerApp; +} + +uint32_t +TabContext::OwnOrContainingAppId() const +{ + if (HasOwnApp()) { + return mOriginAttributes.mAppId; + } + + return mContainingAppId; +} + +already_AddRefed<mozIApplication> +TabContext::GetOwnOrContainingApp() const +{ + nsCOMPtr<mozIApplication> ownOrContainingApp; + if (HasOwnApp()) { + ownOrContainingApp = mOwnApp; + } else { + ownOrContainingApp = mContainingApp; + } + + return ownOrContainingApp.forget(); +} + +bool +TabContext::HasOwnOrContainingApp() const +{ + nsCOMPtr<mozIApplication> ownOrContainingApp = GetOwnOrContainingApp(); + return !!ownOrContainingApp; +} + +bool +TabContext::SetTabContext(const TabContext& aContext) +{ + NS_ENSURE_FALSE(mInitialized, false); + + *this = aContext; + mInitialized = true; + + return true; +} + +void +TabContext::SetPrivateBrowsingAttributes(bool aIsPrivateBrowsing) +{ + mOriginAttributes.SyncAttributesWithPrivateBrowsing(aIsPrivateBrowsing); +} + +bool +TabContext::UpdateTabContextAfterSwap(const TabContext& aContext) +{ + // This is only used after already initialized. + MOZ_ASSERT(mInitialized); + + // The only permissable change is to `mIsMozBrowserElement`. All other fields + // must match for the change to be accepted. + if (aContext.OwnAppId() != OwnAppId() || + aContext.mContainingAppId != mContainingAppId || + aContext.mOriginAttributes != mOriginAttributes) { + return false; + } + + mIsMozBrowserElement = aContext.mIsMozBrowserElement; + return true; +} + +const DocShellOriginAttributes& +TabContext::OriginAttributesRef() const +{ + return mOriginAttributes; +} + +const nsAString& +TabContext::PresentationURL() const +{ + return mPresentationURL; +} + +UIStateChangeType +TabContext::ShowAccelerators() const +{ + return mShowAccelerators; +} + +UIStateChangeType +TabContext::ShowFocusRings() const +{ + return mShowFocusRings; +} + +bool +TabContext::SetTabContext(bool aIsMozBrowserElement, + bool aIsPrerendered, + mozIApplication* aOwnApp, + mozIApplication* aAppFrameOwnerApp, + UIStateChangeType aShowAccelerators, + UIStateChangeType aShowFocusRings, + const DocShellOriginAttributes& aOriginAttributes, + const nsAString& aPresentationURL) +{ + NS_ENSURE_FALSE(mInitialized, false); + + // Get ids for both apps and only write to our member variables after we've + // verified that this worked. + uint32_t ownAppId = NO_APP_ID; + if (aOwnApp) { + nsresult rv = aOwnApp->GetLocalId(&ownAppId); + NS_ENSURE_SUCCESS(rv, false); + NS_ENSURE_TRUE(ownAppId != NO_APP_ID, false); + } + + uint32_t containingAppId = NO_APP_ID; + if (aAppFrameOwnerApp) { + nsresult rv = aAppFrameOwnerApp->GetLocalId(&containingAppId); + NS_ENSURE_SUCCESS(rv, false); + NS_ENSURE_TRUE(containingAppId != NO_APP_ID, false); + } + + // Veryify that app id matches mAppId passed in originAttributes + MOZ_RELEASE_ASSERT((aOwnApp && aOriginAttributes.mAppId == ownAppId) || + (aAppFrameOwnerApp && aOriginAttributes.mAppId == containingAppId) || + aOriginAttributes.mAppId == NO_APP_ID); + + mInitialized = true; + mIsMozBrowserElement = aIsMozBrowserElement; + mIsPrerendered = aIsPrerendered; + mOriginAttributes = aOriginAttributes; + mContainingAppId = containingAppId; + mOwnApp = aOwnApp; + mContainingApp = aAppFrameOwnerApp; + mPresentationURL = aPresentationURL; + mShowAccelerators = aShowAccelerators; + mShowFocusRings = aShowFocusRings; + return true; +} + +IPCTabContext +TabContext::AsIPCTabContext() const +{ + return IPCTabContext(FrameIPCTabContext(mOriginAttributes, + mContainingAppId, + mIsMozBrowserElement, + mIsPrerendered, + mPresentationURL, + mShowAccelerators, + mShowFocusRings)); +} + +static already_AddRefed<mozIApplication> +GetAppForId(uint32_t aAppId) +{ + nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID); + NS_ENSURE_TRUE(appsService, nullptr); + + nsCOMPtr<mozIApplication> app; + appsService->GetAppByLocalId(aAppId, getter_AddRefs(app)); + + return app.forget(); +} + +MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams) + : mInvalidReason(nullptr) +{ + bool isMozBrowserElement = false; + bool isPrerendered = false; + uint32_t containingAppId = NO_APP_ID; + DocShellOriginAttributes originAttributes; + nsAutoString presentationURL; + UIStateChangeType showAccelerators = UIStateChangeType_NoChange; + UIStateChangeType showFocusRings = UIStateChangeType_NoChange; + + switch(aParams.type()) { + case IPCTabContext::TPopupIPCTabContext: { + const PopupIPCTabContext &ipcContext = aParams.get_PopupIPCTabContext(); + + TabContext *context; + if (ipcContext.opener().type() == PBrowserOrId::TPBrowserParent) { + context = TabParent::GetFrom(ipcContext.opener().get_PBrowserParent()); + if (!context) { + mInvalidReason = "Child is-browser process tried to " + "open a null tab."; + return; + } + if (context->IsMozBrowserElement() && + !ipcContext.isMozBrowserElement()) { + // If the TabParent corresponds to a browser element, then it can only + // open other browser elements, for security reasons. We should have + // checked this before calling the TabContext constructor, so this is + // a fatal error. + mInvalidReason = "Child is-browser process tried to " + "open a non-browser tab."; + return; + } + } else if (ipcContext.opener().type() == PBrowserOrId::TPBrowserChild) { + context = static_cast<TabChild*>(ipcContext.opener().get_PBrowserChild()); + } else if (ipcContext.opener().type() == PBrowserOrId::TTabId) { + // We should never get here because this PopupIPCTabContext is only + // used for allocating a new tab id, not for allocating a PBrowser. + mInvalidReason = "Child process tried to open an tab without the opener information."; + return; + } else { + // This should be unreachable because PopupIPCTabContext::opener is not a + // nullable field. + mInvalidReason = "PopupIPCTabContext::opener was null (?!)."; + return; + } + + // Browser elements can't nest other browser elements. So if + // our opener is browser element, we must be a new DOM window + // opened by it. In that case we inherit our containing app ID + // (if any). + // + // Otherwise, we're a new app window and we inherit from our + // opener app. + isMozBrowserElement = ipcContext.isMozBrowserElement(); + originAttributes = context->mOriginAttributes; + if (isMozBrowserElement) { + containingAppId = context->OwnOrContainingAppId(); + } else { + containingAppId = context->mContainingAppId; + } + break; + } + case IPCTabContext::TFrameIPCTabContext: { + const FrameIPCTabContext &ipcContext = + aParams.get_FrameIPCTabContext(); + + isMozBrowserElement = ipcContext.isMozBrowserElement(); + isPrerendered = ipcContext.isPrerendered(); + containingAppId = ipcContext.frameOwnerAppId(); + presentationURL = ipcContext.presentationURL(); + showAccelerators = ipcContext.showAccelerators(); + showFocusRings = ipcContext.showFocusRings(); + originAttributes = ipcContext.originAttributes(); + break; + } + case IPCTabContext::TUnsafeIPCTabContext: { + // XXXcatalinb: This used *only* by ServiceWorkerClients::OpenWindow. + // It is meant as a temporary solution until service workers can + // provide a TabChild equivalent. Don't allow this on b2g since + // it might be used to escalate privileges. +#ifdef MOZ_B2G + mInvalidReason = "ServiceWorkerClients::OpenWindow is not supported."; + return; +#endif + if (!Preferences::GetBool("dom.serviceWorkers.enabled", false)) { + mInvalidReason = "ServiceWorkers should be enabled."; + return; + } + + containingAppId = NO_APP_ID; + break; + } + default: { + MOZ_CRASH(); + } + } + + nsCOMPtr<mozIApplication> ownApp; + if (!isMozBrowserElement) { + // mAppId corresponds to OwnOrContainingAppId; if isMozBrowserElement is + // false then it's ownApp otherwise it's containingApp + ownApp = GetAppForId(originAttributes.mAppId); + if ((ownApp == nullptr) != (originAttributes.mAppId == NO_APP_ID)) { + mInvalidReason = "Got an ownAppId that didn't correspond to an app."; + return; + } + } + + nsCOMPtr<mozIApplication> containingApp = GetAppForId(containingAppId); + if ((containingApp == nullptr) != (containingAppId == NO_APP_ID)) { + mInvalidReason = "Got a containingAppId that didn't correspond to an app."; + return; + } + + bool rv; + rv = mTabContext.SetTabContext(isMozBrowserElement, + isPrerendered, + ownApp, + containingApp, + showAccelerators, + showFocusRings, + originAttributes, + presentationURL); + if (!rv) { + mInvalidReason = "Couldn't initialize TabContext."; + } +} + +bool +MaybeInvalidTabContext::IsValid() +{ + return mInvalidReason == nullptr; +} + +const char* +MaybeInvalidTabContext::GetInvalidReason() +{ + return mInvalidReason; +} + +const TabContext& +MaybeInvalidTabContext::GetTabContext() +{ + if (!IsValid()) { + MOZ_CRASH("Can't GetTabContext() if !IsValid()."); + } + + return mTabContext; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/TabContext.h b/dom/ipc/TabContext.h new file mode 100644 index 000000000..48974cad1 --- /dev/null +++ b/dom/ipc/TabContext.h @@ -0,0 +1,337 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_TabContext_h +#define mozilla_dom_TabContext_h + +#include "mozIApplication.h" +#include "nsCOMPtr.h" +#include "mozilla/BasePrincipal.h" +#include "nsPIDOMWindow.h" +#include "nsPIWindowRoot.h" + +namespace mozilla { +namespace dom { + +class IPCTabContext; + +/** + * TabContext encapsulates information about an iframe that may be a mozbrowser + * or mozapp. You can ask whether a TabContext corresponds to a mozbrowser or + * mozapp, get the app that contains the browser, and so on. + * + * TabParent and TabChild both inherit from TabContext, and you can also have + * standalone TabContext objects. + * + * This class is immutable except by calling one of the protected + * SetTabContext*() methods (and those methods can only be called once). See + * also MutableTabContext. + */ +class TabContext +{ +public: + TabContext(); + + /* (The implicit copy-constructor and operator= are fine.) */ + + /** + * Generates IPCTabContext of type BrowserFrameIPCTabContext or + * AppFrameIPCTabContext from this TabContext's information. + */ + IPCTabContext AsIPCTabContext() const; + + /** + * Does this TabContext correspond to a mozbrowser? + * + * <iframe mozbrowser mozapp> and <xul:browser> are not considered to be + * mozbrowser elements. + * + * If IsMozBrowserElement() is true, HasOwnApp() and HasAppOwnerApp() are + * guaranteed to be false. + * + * If IsMozBrowserElement() is false, HasBrowserOwnerApp() is guaranteed to be + * false. + */ + bool IsMozBrowserElement() const; + + /** + * Does this TabContext correspond to an isolated mozbrowser? + * + * <iframe mozbrowser mozapp> and <xul:browser> are not considered to be + * mozbrowser elements. <iframe mozbrowser noisolation> does not count as + * isolated since isolation is disabled. Isolation can only be disabled by + * chrome pages. + */ + bool IsIsolatedMozBrowserElement() const; + + /** + * Does this TabContext correspond to a mozbrowser or mozapp? This is + * equivalent to IsMozBrowserElement() || HasOwnApp(). Returns false for + * <xul:browser>, which is neither a mozbrowser nor a mozapp. + */ + bool IsMozBrowserOrApp() const; + + /** + * OwnAppId() returns the id of the app which directly corresponds to this + * context's frame. GetOwnApp() returns the corresponding app object, and + * HasOwnApp() returns true iff GetOwnApp() would return a non-null value. + * + * If HasOwnApp() is true, IsMozBrowserElement() is guaranteed to be + * false. + */ + uint32_t OwnAppId() const; + already_AddRefed<mozIApplication> GetOwnApp() const; + bool HasOwnApp() const; + + /** + * BrowserOwnerAppId() gets the ID of the app which contains this browser + * frame. If this is not a mozbrowser frame (if !IsMozBrowserElement()), then + * BrowserOwnerAppId() is guaranteed to return NO_APP_ID. + * + * Even if we are a browser frame, BrowserOwnerAppId() may still return + * NO_APP_ID, if this browser frame is not contained inside an app. + */ + uint32_t BrowserOwnerAppId() const; + already_AddRefed<mozIApplication> GetBrowserOwnerApp() const; + bool HasBrowserOwnerApp() const; + + /** + * AppOwnerAppId() gets the ID of the app which contains this app frame. If + * this is not an app frame (i.e., if !HasOwnApp()), then AppOwnerAppId() is + * guaranteed to return NO_APP_ID. + * + * Even if we are an app frame, AppOwnerAppId() may still return NO_APP_ID, if + * this app frame is not contained inside an app. + */ + uint32_t AppOwnerAppId() const; + already_AddRefed<mozIApplication> GetAppOwnerApp() const; + bool HasAppOwnerApp() const; + + /** + * OwnOrContainingAppId() gets the ID of this frame, if HasOwnApp(). If this + * frame does not have its own app, it gets the ID of the app which contains + * this frame (i.e., the result of {Browser,App}OwnerAppId(), as applicable). + */ + uint32_t OwnOrContainingAppId() const; + already_AddRefed<mozIApplication> GetOwnOrContainingApp() const; + bool HasOwnOrContainingApp() const; + + /** + * OriginAttributesRef() returns the DocShellOriginAttributes of this frame to + * the caller. This is used to store any attribute associated with the frame's + * docshell, such as the AppId. + */ + const DocShellOriginAttributes& OriginAttributesRef() const; + + /** + * Returns the presentation URL associated with the tab if this tab is + * created for presented content + */ + const nsAString& PresentationURL() const; + + UIStateChangeType ShowAccelerators() const; + UIStateChangeType ShowFocusRings() const; + +protected: + friend class MaybeInvalidTabContext; + + /** + * These protected mutator methods let you modify a TabContext once. Further + * attempts to modify a given TabContext will fail (the method will return + * false). + * + * These mutators will also fail if the TabContext was created with anything + * other than the no-args constructor. + */ + + /** + * Set this TabContext to match the given TabContext. + */ + bool SetTabContext(const TabContext& aContext); + + /** + * Set the tab context's origin attributes to a private browsing value. + */ + void SetPrivateBrowsingAttributes(bool aIsPrivateBrowsing); + + /** + * Set the TabContext for this frame. This can either be: + * - an app frame (with the given own app) inside the given owner app. Either + * apps can be null. + * - a browser frame inside the given owner app (which may be null). + * - a non-browser, non-app frame. Both own app and owner app should be null. + */ + bool SetTabContext(bool aIsMozBrowserElement, + bool aIsPrerendered, + mozIApplication* aOwnApp, + mozIApplication* aAppFrameOwnerApp, + UIStateChangeType aShowAccelerators, + UIStateChangeType aShowFocusRings, + const DocShellOriginAttributes& aOriginAttributes, + const nsAString& aPresentationURL); + + /** + * Modify this TabContext to match the given TabContext. This is a special + * case triggered by nsFrameLoader::SwapWithOtherRemoteLoader which may have + * caused the owner content to change. + * + * This special case only allows the field `mIsMozBrowserElement` to be + * changed. If any other fields have changed, the update is ignored and + * returns false. + */ + bool UpdateTabContextAfterSwap(const TabContext& aContext); + + /** + * Whether this TabContext is in prerender mode. + */ + bool mIsPrerendered; + +private: + /** + * Has this TabContext been initialized? If so, mutator methods will fail. + */ + bool mInitialized; + + /** + * Whether this TabContext corresponds to a mozbrowser. + * + * <iframe mozbrowser mozapp> and <xul:browser> are not considered to be + * mozbrowser elements. + */ + bool mIsMozBrowserElement; + + /** + * This TabContext's own app. If this is non-null, then this + * TabContext corresponds to an app, and mIsMozBrowserElement must be false. + */ + nsCOMPtr<mozIApplication> mOwnApp; + + /** + * This TabContext's containing app. If mIsMozBrowserElement, this + * corresponds to the app which contains the browser frame; otherwise, this + * corresponds to the app which contains the app frame. + */ + nsCOMPtr<mozIApplication> mContainingApp; + + /* + * Cache of mContainingApp->GetLocalId(). + */ + uint32_t mContainingAppId; + + /** + * DocShellOriginAttributes of the top level tab docShell + */ + DocShellOriginAttributes mOriginAttributes; + + /** + * The requested presentation URL. + */ + nsString mPresentationURL; + + /** + * Keyboard indicator state (focus rings, accelerators). + */ + UIStateChangeType mShowAccelerators; + UIStateChangeType mShowFocusRings; +}; + +/** + * MutableTabContext is the same as MaybeInvalidTabContext, except the mutation + * methods are public instead of protected. You can still only call these + * mutation methods once on a given object. + */ +class MutableTabContext : public TabContext +{ +public: + bool SetTabContext(const TabContext& aContext) + { + return TabContext::SetTabContext(aContext); + } + + bool + SetTabContext(bool aIsMozBrowserElement, + bool aIsPrerendered, + mozIApplication* aOwnApp, + mozIApplication* aAppFrameOwnerApp, + UIStateChangeType aShowAccelerators, + UIStateChangeType aShowFocusRings, + const DocShellOriginAttributes& aOriginAttributes, + const nsAString& aPresentationURL = EmptyString()) + { + return TabContext::SetTabContext(aIsMozBrowserElement, + aIsPrerendered, + aOwnApp, + aAppFrameOwnerApp, + aShowAccelerators, + aShowFocusRings, + aOriginAttributes, + aPresentationURL); + } +}; + +/** + * MaybeInvalidTabContext is a simple class that lets you transform an + * IPCTabContext into a TabContext. + * + * The issue is that an IPCTabContext is not necessarily valid; for example, it + * might specify an app-id which doesn't exist. So to convert an IPCTabContext + * into a TabContext, you construct a MaybeInvalidTabContext, check whether it's + * valid, and, if so, read out your TabContext. + * + * Example usage: + * + * void UseTabContext(const TabContext& aTabContext); + * + * void CreateTab(const IPCTabContext& aContext) { + * MaybeInvalidTabContext tc(aContext); + * if (!tc.IsValid()) { + * NS_ERROR(nsPrintfCString("Got an invalid IPCTabContext: %s", + * tc.GetInvalidReason())); + * return; + * } + * UseTabContext(tc.GetTabContext()); + * } + */ +class MaybeInvalidTabContext +{ +public: + /** + * This constructor copies the information in aContext and sets IsValid() as + * appropriate. + */ + explicit MaybeInvalidTabContext(const IPCTabContext& aContext); + + /** + * Was the IPCTabContext we received in our constructor valid? + */ + bool IsValid(); + + /** + * If IsValid(), this function returns null. Otherwise, it returns a + * human-readable string indicating why the IPCTabContext passed to our + * constructor was not valid. + */ + const char* GetInvalidReason(); + + /** + * If IsValid(), this function returns a reference to a TabContext + * corresponding to the IPCTabContext passed to our constructor. If + * !IsValid(), this function crashes. + */ + const TabContext& GetTabContext(); + +private: + MaybeInvalidTabContext(const MaybeInvalidTabContext&) = delete; + MaybeInvalidTabContext& operator=(const MaybeInvalidTabContext&) = delete; + + const char* mInvalidReason; + MutableTabContext mTabContext; +}; + +} // namespace dom +} // namespace mozilla + +#endif diff --git a/dom/ipc/TabMessageUtils.cpp b/dom/ipc/TabMessageUtils.cpp new file mode 100644 index 000000000..5085a7e95 --- /dev/null +++ b/dom/ipc/TabMessageUtils.cpp @@ -0,0 +1,30 @@ +/* -*- 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/EventDispatcher.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/TabMessageUtils.h" +#include "nsCOMPtr.h" + +namespace mozilla { +namespace dom { + +bool +ReadRemoteEvent(const IPC::Message* aMsg, PickleIterator* aIter, + RemoteDOMEvent* aResult) +{ + aResult->mEvent = nullptr; + nsString type; + NS_ENSURE_TRUE(ReadParam(aMsg, aIter, &type), false); + + aResult->mEvent = EventDispatcher::CreateEvent(nullptr, nullptr, nullptr, + type); + + return aResult->mEvent->Deserialize(aMsg, aIter); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/TabMessageUtils.h b/dom/ipc/TabMessageUtils.h new file mode 100644 index 000000000..cdb76099d --- /dev/null +++ b/dom/ipc/TabMessageUtils.h @@ -0,0 +1,117 @@ +/* -*- 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/. */ + +#ifndef TABMESSAGE_UTILS_H +#define TABMESSAGE_UTILS_H + +#include "ipc/IPCMessageUtils.h" +#include "mozilla/dom/AudioChannelBinding.h" +#include "nsIDOMEvent.h" +#include "nsPIDOMWindow.h" +#include "nsCOMPtr.h" + +#ifdef MOZ_CRASHREPORTER +#include "nsExceptionHandler.h" +#endif + +namespace mozilla { +namespace dom { +struct RemoteDOMEvent +{ + // Make sure to set the owner after deserializing. + nsCOMPtr<nsIDOMEvent> mEvent; +}; + +bool ReadRemoteEvent(const IPC::Message* aMsg, PickleIterator* aIter, + mozilla::dom::RemoteDOMEvent* aResult); + +#ifdef MOZ_CRASHREPORTER +typedef CrashReporter::ThreadId NativeThreadId; +#else +// unused in this case +typedef int32_t NativeThreadId; +#endif + +} // namespace dom +} // namespace mozilla + +namespace IPC { + +template<> +struct ParamTraits<mozilla::dom::RemoteDOMEvent> +{ + typedef mozilla::dom::RemoteDOMEvent paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + aParam.mEvent->Serialize(aMsg, true); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + return mozilla::dom::ReadRemoteEvent(aMsg, aIter, aResult); + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + } +}; + +template<> +struct ParamTraits<mozilla::dom::AudioChannel> +{ + typedef mozilla::dom::AudioChannel paramType; + + static bool IsLegalValue(const paramType &aValue) + { + return aValue <= mozilla::dom::AudioChannel::Publicnotification; + } + + static void Write(Message* aMsg, const paramType& aValue) + { + MOZ_ASSERT(IsLegalValue(aValue)); + WriteParam(aMsg, (uint32_t)aValue); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + uint32_t value; + if(!ReadParam(aMsg, aIter, &value) || + !IsLegalValue(paramType(value))) { + return false; + } + *aResult = paramType(value); + return true; + } + + static void Log(const paramType& aParam, std::wstring* aLog) + {} +}; + +template <> +struct ParamTraits<nsEventStatus> + : public ContiguousEnumSerializer<nsEventStatus, + nsEventStatus_eIgnore, + nsEventStatus_eSentinel> +{}; + +template<> +struct ParamTraits<nsSizeMode> + : public ContiguousEnumSerializer<nsSizeMode, + nsSizeMode_Normal, + nsSizeMode_Invalid> +{}; + +template<> +struct ParamTraits<UIStateChangeType> + : public ContiguousEnumSerializer<UIStateChangeType, + UIStateChangeType_NoChange, + UIStateChangeType_Invalid> +{ }; + +} // namespace IPC + +#endif // TABMESSAGE_UTILS_H diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp new file mode 100644 index 000000000..0df4c1253 --- /dev/null +++ b/dom/ipc/TabParent.cpp @@ -0,0 +1,3306 @@ +/* -*- 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 "base/basictypes.h" + +#include "TabParent.h" + +#include "AudioChannelService.h" +#include "AppProcessChecker.h" +#include "mozIApplication.h" +#ifdef ACCESSIBILITY +#include "mozilla/a11y/DocAccessibleParent.h" +#include "nsAccessibilityService.h" +#endif +#include "mozilla/BrowserElementParent.h" +#include "mozilla/dom/ContentBridgeParent.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/DataTransfer.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/indexedDB/ActorsParent.h" +#include "mozilla/plugins/PluginWidgetParent.h" +#include "mozilla/EventStateManager.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/DataSurfaceHelpers.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/Hal.h" +#include "mozilla/IMEStateManager.h" +#include "mozilla/ipc/DocumentRendererParent.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" +#include "mozilla/layers/AsyncDragMetrics.h" +#include "mozilla/layers/InputAPZContext.h" +#include "mozilla/layout/RenderFrameParent.h" +#include "mozilla/LookAndFeel.h" +#include "mozilla/MouseEvents.h" +#include "mozilla/net/NeckoChild.h" +#include "mozilla/Preferences.h" +#include "mozilla/TextEvents.h" +#include "mozilla/TouchEvents.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Unused.h" +#include "BlobParent.h" +#include "nsCOMPtr.h" +#include "nsContentAreaDragDrop.h" +#include "nsContentUtils.h" +#include "nsDebug.h" +#include "nsFocusManager.h" +#include "nsFrameLoader.h" +#include "nsIBaseWindow.h" +#include "nsIBrowser.h" +#include "nsIContent.h" +#include "nsIDocShell.h" +#include "nsIDocShellTreeOwner.h" +#include "nsIDOMElement.h" +#include "nsIDOMEvent.h" +#include "nsIDOMWindow.h" +#include "nsIDOMWindowUtils.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsILoadInfo.h" +#include "nsPrincipal.h" +#include "nsIPromptFactory.h" +#include "nsIURI.h" +#include "nsIWindowWatcher.h" +#include "nsIWebBrowserChrome.h" +#include "nsIXULBrowserWindow.h" +#include "nsIXULWindow.h" +#include "nsIRemoteBrowser.h" +#include "nsViewManager.h" +#include "nsVariant.h" +#include "nsIWidget.h" +#ifndef XP_WIN +#include "nsJARProtocolHandler.h" +#endif +#include "nsPIDOMWindow.h" +#include "nsPresShell.h" +#include "nsPrintfCString.h" +#include "nsServiceManagerUtils.h" +#include "nsThreadUtils.h" +#include "PermissionMessageUtils.h" +#include "StructuredCloneData.h" +#include "ColorPickerParent.h" +#include "DatePickerParent.h" +#include "FilePickerParent.h" +#include "TabChild.h" +#include "LoadContext.h" +#include "nsNetCID.h" +#include "nsIAuthInformation.h" +#include "nsIAuthPromptCallback.h" +#include "nsAuthInformationHolder.h" +#include "nsICancelable.h" +#include "gfxPrefs.h" +#include "nsILoginManagerPrompter.h" +#include "nsPIWindowRoot.h" +#include "nsIAuthPrompt2.h" +#include "gfxDrawable.h" +#include "ImageOps.h" +#include "UnitTransforms.h" +#include <algorithm> +#include "mozilla/WebBrowserPersistDocumentParent.h" +#include "nsIGroupedSHistory.h" +#include "PartialSHistory.h" + +#if defined(XP_WIN) && defined(ACCESSIBILITY) +#include "mozilla/a11y/AccessibleWrap.h" +#endif + +using namespace mozilla::dom; +using namespace mozilla::ipc; +using namespace mozilla::layers; +using namespace mozilla::layout; +using namespace mozilla::services; +using namespace mozilla::widget; +using namespace mozilla::jsipc; +using namespace mozilla::gfx; + +using mozilla::Unused; + +// The flags passed by the webProgress notifications are 16 bits shifted +// from the ones registered by webProgressListeners. +#define NOTIFY_FLAG_SHIFT 16 + +namespace mozilla { +namespace dom { + +TabParent::LayerToTabParentTable* TabParent::sLayerToTabParentTable = nullptr; + +NS_IMPL_ISUPPORTS(TabParent, + nsITabParent, + nsIAuthPromptProvider, + nsISecureBrowserUI, + nsISupportsWeakReference, + nsIWebBrowserPersistable) + +TabParent::TabParent(nsIContentParent* aManager, + const TabId& aTabId, + const TabContext& aContext, + uint32_t aChromeFlags) + : TabContext(aContext) + , mFrameElement(nullptr) + , mRect(0, 0, 0, 0) + , mDimensions(0, 0) + , mOrientation(0) + , mDPI(0) + , mRounding(0) + , mDefaultScale(0) + , mUpdatedDimensions(false) + , mSizeMode(nsSizeMode_Normal) + , mManager(aManager) + , mDocShellIsActive(false) + , mMarkedDestroying(false) + , mIsDestroyed(false) + , mChromeFlags(aChromeFlags) + , mDragValid(false) + , mInitedByParent(false) + , mTabId(aTabId) + , mCreatingWindow(false) + , mCursor(nsCursor(-1)) + , mTabSetsCursor(false) + , mHasContentOpener(false) +#ifdef DEBUG + , mActiveSupressDisplayportCount(0) +#endif + , mLayerTreeEpoch(0) + , mPreserveLayers(false) +{ + MOZ_ASSERT(aManager); +} + +TabParent::~TabParent() +{ +} + +TabParent* +TabParent::GetTabParentFromLayersId(uint64_t aLayersId) +{ + if (!sLayerToTabParentTable) { + return nullptr; + } + return sLayerToTabParentTable->Get(aLayersId); +} + +void +TabParent::AddTabParentToTable(uint64_t aLayersId, TabParent* aTabParent) +{ + if (!sLayerToTabParentTable) { + sLayerToTabParentTable = new LayerToTabParentTable(); + } + sLayerToTabParentTable->Put(aLayersId, aTabParent); +} + +void +TabParent::RemoveTabParentFromTable(uint64_t aLayersId) +{ + if (!sLayerToTabParentTable) { + return; + } + sLayerToTabParentTable->Remove(aLayersId); + if (sLayerToTabParentTable->Count() == 0) { + delete sLayerToTabParentTable; + sLayerToTabParentTable = nullptr; + } +} + +void +TabParent::CacheFrameLoader(nsFrameLoader* aFrameLoader) +{ + mFrameLoader = aFrameLoader; +} + +/** + * Will return nullptr if there is no outer window available for the + * document hosting the owner element of this TabParent. Also will return + * nullptr if that outer window is in the process of closing. + */ +already_AddRefed<nsPIDOMWindowOuter> +TabParent::GetParentWindowOuter() +{ + nsCOMPtr<nsIContent> frame = do_QueryInterface(GetOwnerElement()); + if (!frame) { + return nullptr; + } + + nsCOMPtr<nsPIDOMWindowOuter> parent = frame->OwnerDoc()->GetWindow(); + if (!parent || parent->Closed()) { + return nullptr; + } + + return parent.forget(); +} + +void +TabParent::SetOwnerElement(Element* aElement) +{ + // If we held previous content then unregister for its events. + RemoveWindowListeners(); + + // If we change top-level documents then we need to change our + // registration with them. + RefPtr<nsPIWindowRoot> curTopLevelWin, newTopLevelWin; + if (mFrameElement) { + curTopLevelWin = nsContentUtils::GetWindowRoot(mFrameElement->OwnerDoc()); + } + if (aElement) { + newTopLevelWin = nsContentUtils::GetWindowRoot(aElement->OwnerDoc()); + } + bool isSameTopLevelWin = curTopLevelWin == newTopLevelWin; + if (curTopLevelWin && !isSameTopLevelWin) { + curTopLevelWin->RemoveBrowser(this); + } + + // Update to the new content, and register to listen for events from it. + mFrameElement = aElement; + + if (newTopLevelWin && !isSameTopLevelWin) { + newTopLevelWin->AddBrowser(this); + } + + if (mFrameElement) { + bool useGlobalHistory = + !mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::disableglobalhistory); + Unused << SendSetUseGlobalHistory(useGlobalHistory); + } + +#if defined(XP_WIN) && defined(ACCESSIBILITY) + if (!mIsDestroyed) { + uintptr_t newWindowHandle = 0; + if (nsCOMPtr<nsIWidget> widget = GetWidget()) { + newWindowHandle = + reinterpret_cast<uintptr_t>(widget->GetNativeData(NS_NATIVE_WINDOW)); + } + Unused << SendUpdateNativeWindowHandle(newWindowHandle); + } +#endif + + AddWindowListeners(); + TryCacheDPIAndScale(); +} + +void +TabParent::AddWindowListeners() +{ + if (mFrameElement && mFrameElement->OwnerDoc()) { + if (nsCOMPtr<nsPIDOMWindowOuter> window = mFrameElement->OwnerDoc()->GetWindow()) { + nsCOMPtr<EventTarget> eventTarget = window->GetTopWindowRoot(); + if (eventTarget) { + eventTarget->AddEventListener(NS_LITERAL_STRING("MozUpdateWindowPos"), + this, false, false); + } + } + if (nsIPresShell* shell = mFrameElement->OwnerDoc()->GetShell()) { + mPresShellWithRefreshListener = shell; + shell->AddPostRefreshObserver(this); + } + + RefPtr<AudioChannelService> acs = AudioChannelService::GetOrCreate(); + if (acs) { + acs->RegisterTabParent(this); + } + } +} + +void +TabParent::RemoveWindowListeners() +{ + if (mFrameElement && mFrameElement->OwnerDoc()->GetWindow()) { + nsCOMPtr<nsPIDOMWindowOuter> window = mFrameElement->OwnerDoc()->GetWindow(); + nsCOMPtr<EventTarget> eventTarget = window->GetTopWindowRoot(); + if (eventTarget) { + eventTarget->RemoveEventListener(NS_LITERAL_STRING("MozUpdateWindowPos"), + this, false); + } + } + if (mPresShellWithRefreshListener) { + mPresShellWithRefreshListener->RemovePostRefreshObserver(this); + mPresShellWithRefreshListener = nullptr; + } + + RefPtr<AudioChannelService> acs = AudioChannelService::GetOrCreate(); + if (acs) { + acs->UnregisterTabParent(this); + } +} + +void +TabParent::DidRefresh() +{ + if (mChromeOffset != -GetChildProcessOffset()) { + UpdatePosition(); + } +} + +void +TabParent::GetAppType(nsAString& aOut) +{ + aOut.Truncate(); + nsCOMPtr<Element> elem = do_QueryInterface(mFrameElement); + if (!elem) { + return; + } + + elem->GetAttr(kNameSpaceID_None, nsGkAtoms::mozapptype, aOut); +} + +bool +TabParent::IsVisible() const +{ + RefPtr<nsFrameLoader> frameLoader = GetFrameLoader(); + if (!frameLoader) { + return false; + } + + return frameLoader->GetVisible(); +} + +void +TabParent::DestroyInternal() +{ + IMEStateManager::OnTabParentDestroying(this); + + RemoveWindowListeners(); + + // If this fails, it's most likely due to a content-process crash, + // and auto-cleanup will kick in. Otherwise, the child side will + // destroy itself and send back __delete__(). + Unused << SendDestroy(); + + if (RenderFrameParent* frame = GetRenderFrame()) { + RemoveTabParentFromTable(frame->GetLayersId()); + frame->Destroy(); + } + + // Let all PluginWidgets know we are tearing down. Prevents + // these objects from sending async events after the child side + // is shut down. + const ManagedContainer<PPluginWidgetParent>& kids = + ManagedPPluginWidgetParent(); + for (auto iter = kids.ConstIter(); !iter.Done(); iter.Next()) { + static_cast<mozilla::plugins::PluginWidgetParent*>( + iter.Get()->GetKey())->ParentDestroy(); + } +} + +void +TabParent::Destroy() +{ + // Aggressively release the window to avoid leaking the world in shutdown + // corner cases. + mBrowserDOMWindow = nullptr; + + if (mIsDestroyed) { + return; + } + + DestroyInternal(); + + mIsDestroyed = true; + + if (XRE_IsParentProcess()) { + ContentParent::NotifyTabDestroying(this->GetTabId(), Manager()->AsContentParent()->ChildID()); + } else { + ContentParent::NotifyTabDestroying(this->GetTabId(), Manager()->ChildID()); + } + + mMarkedDestroying = true; +} + +bool +TabParent::RecvEnsureLayersConnected() +{ + if (RenderFrameParent* frame = GetRenderFrame()) { + frame->EnsureLayersConnected(); + } + return true; +} + +bool +TabParent::Recv__delete__() +{ + if (XRE_IsParentProcess()) { + ContentParent::DeallocateTabId(mTabId, + Manager()->AsContentParent()->ChildID(), + mMarkedDestroying); + } + else { + Manager()->AsContentBridgeParent()->NotifyTabDestroyed(); + ContentParent::DeallocateTabId(mTabId, + Manager()->ChildID(), + mMarkedDestroying); + } + + return true; +} + +void +TabParent::ActorDestroy(ActorDestroyReason why) +{ + // Even though TabParent::Destroy calls this, we need to do it here too in + // case of a crash. + IMEStateManager::OnTabParentDestroying(this); + + // Prevent executing ContentParent::NotifyTabDestroying in + // TabParent::Destroy() called by frameLoader->DestroyComplete() below + // when tab crashes in contentprocess because ContentParent::ActorDestroy() + // in main process will be triggered before this function + // and remove the process information that + // ContentParent::NotifyTabDestroying need from mContentParentMap. + + // When tab crashes in content process, + // there is no need to call ContentParent::NotifyTabDestroying + // because the jobs in ContentParent::NotifyTabDestroying + // will be done by ContentParent::ActorDestroy. + if (XRE_IsContentProcess() && why == AbnormalShutdown && !mIsDestroyed) { + DestroyInternal(); + mIsDestroyed = true; + } + + RefPtr<nsFrameLoader> frameLoader = GetFrameLoader(true); + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); + if (frameLoader) { + nsCOMPtr<Element> frameElement(mFrameElement); + ReceiveMessage(CHILD_PROCESS_SHUTDOWN_MESSAGE, false, nullptr, nullptr, + nullptr); + frameLoader->DestroyComplete(); + + if (why == AbnormalShutdown && os) { + os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, frameLoader), + "oop-frameloader-crashed", nullptr); + nsCOMPtr<nsIFrameLoaderOwner> owner = do_QueryInterface(frameElement); + if (owner) { + RefPtr<nsFrameLoader> currentFrameLoader = owner->GetFrameLoader(); + // It's possible that the frameloader owner has already moved on + // and created a new frameloader. If so, we don't fire the event, + // since the frameloader owner has clearly moved on. + if (currentFrameLoader == frameLoader) { + nsContentUtils::DispatchTrustedEvent(frameElement->OwnerDoc(), frameElement, + NS_LITERAL_STRING("oop-browser-crashed"), + true, true); + + } + } + } + + mFrameLoader = nullptr; + } + + if (os) { + os->NotifyObservers(NS_ISUPPORTS_CAST(nsITabParent*, this), "ipc:browser-destroyed", nullptr); + } +} + +bool +TabParent::RecvMoveFocus(const bool& aForward, const bool& aForDocumentNavigation) +{ + nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID); + if (fm) { + nsCOMPtr<nsIDOMElement> dummy; + + uint32_t type = aForward ? + (aForDocumentNavigation ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FORWARDDOC) : + static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FORWARD)) : + (aForDocumentNavigation ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_BACKWARDDOC) : + static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_BACKWARD)); + nsCOMPtr<nsIDOMElement> frame = do_QueryInterface(mFrameElement); + fm->MoveFocus(nullptr, frame, type, nsIFocusManager::FLAG_BYKEY, + getter_AddRefs(dummy)); + } + return true; +} + +bool +TabParent::RecvSizeShellTo(const uint32_t& aFlags, const int32_t& aWidth, const int32_t& aHeight, + const int32_t& aShellItemWidth, const int32_t& aShellItemHeight) +{ + NS_ENSURE_TRUE(mFrameElement, true); + + nsCOMPtr<nsIDocShell> docShell = mFrameElement->OwnerDoc()->GetDocShell(); + NS_ENSURE_TRUE(docShell, true); + + nsCOMPtr<nsIDocShellTreeOwner> treeOwner; + nsresult rv = docShell->GetTreeOwner(getter_AddRefs(treeOwner)); + NS_ENSURE_SUCCESS(rv, true); + + int32_t width = aWidth; + int32_t height = aHeight; + + if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CX) { + width = mDimensions.width; + } + + if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CY) { + height = mDimensions.height; + } + + nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(treeOwner)); + NS_ENSURE_TRUE(xulWin, true); + xulWin->SizeShellToWithLimit(width, height, aShellItemWidth, aShellItemHeight); + + return true; +} + +bool +TabParent::RecvDropLinks(nsTArray<nsString>&& aLinks) +{ + nsCOMPtr<nsIBrowser> browser = do_QueryInterface(mFrameElement); + if (browser) { + UniquePtr<const char16_t*[]> links; + links = MakeUnique<const char16_t*[]>(aLinks.Length()); + for (uint32_t i = 0; i < aLinks.Length(); i++) { + links[i] = aLinks[i].get(); + } + browser->DropLinks(aLinks.Length(), links.get()); + } + return true; +} + +bool +TabParent::RecvEvent(const RemoteDOMEvent& aEvent) +{ + nsCOMPtr<nsIDOMEvent> event = do_QueryInterface(aEvent.mEvent); + NS_ENSURE_TRUE(event, true); + + nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(mFrameElement); + NS_ENSURE_TRUE(target, true); + + event->SetOwner(target); + + bool dummy; + target->DispatchEvent(event, &dummy); + return true; +} + +TabParent* TabParent::sNextTabParent; + +/* static */ TabParent* +TabParent::GetNextTabParent() +{ + TabParent* result = sNextTabParent; + sNextTabParent = nullptr; + return result; +} + +bool +TabParent::SendLoadRemoteScript(const nsString& aURL, + const bool& aRunInGlobalScope) +{ + if (mCreatingWindow) { + mDelayedFrameScripts.AppendElement(FrameScriptInfo(aURL, aRunInGlobalScope)); + return true; + } + + MOZ_ASSERT(mDelayedFrameScripts.IsEmpty()); + return PBrowserParent::SendLoadRemoteScript(aURL, aRunInGlobalScope); +} + +void +TabParent::LoadURL(nsIURI* aURI) +{ + MOZ_ASSERT(aURI); + + if (mIsDestroyed) { + return; + } + + nsCString spec; + aURI->GetSpec(spec); + + if (mCreatingWindow) { + // Don't send the message if the child wants to load its own URL. + MOZ_ASSERT(mDelayedURL.IsEmpty()); + mDelayedURL = spec; + return; + } + + Unused << SendLoadURL(spec, GetShowInfo()); +} + +void +TabParent::Show(const ScreenIntSize& size, bool aParentIsActive) +{ + mDimensions = size; + if (mIsDestroyed) { + return; + } + + TextureFactoryIdentifier textureFactoryIdentifier; + uint64_t layersId = 0; + bool success = false; + RenderFrameParent* renderFrame = nullptr; + if (IsInitedByParent()) { + // If TabParent is initialized by parent side then the RenderFrame must also + // be created here. If TabParent is initialized by child side, + // child side will create RenderFrame. + MOZ_ASSERT(!GetRenderFrame()); + RefPtr<nsFrameLoader> frameLoader = GetFrameLoader(); + if (frameLoader) { + renderFrame = new RenderFrameParent(frameLoader, &success); + MOZ_ASSERT(success); + layersId = renderFrame->GetLayersId(); + renderFrame->GetTextureFactoryIdentifier(&textureFactoryIdentifier); + AddTabParentToTable(layersId, this); + Unused << SendPRenderFrameConstructor(renderFrame); + } + } else { + // Otherwise, the child should have constructed the RenderFrame, + // and we should already know about it. + MOZ_ASSERT(GetRenderFrame()); + } + + nsCOMPtr<nsISupports> container = mFrameElement->OwnerDoc()->GetContainer(); + nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container); + nsCOMPtr<nsIWidget> mainWidget; + baseWindow->GetMainWidget(getter_AddRefs(mainWidget)); + mSizeMode = mainWidget ? mainWidget->SizeMode() : nsSizeMode_Normal; + + Unused << SendShow(size, GetShowInfo(), textureFactoryIdentifier, + layersId, renderFrame, aParentIsActive, mSizeMode); +} + +bool +TabParent::RecvSetDimensions(const uint32_t& aFlags, + const int32_t& aX, const int32_t& aY, + const int32_t& aCx, const int32_t& aCy) +{ + MOZ_ASSERT(!(aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_INNER), + "We should never see DIM_FLAGS_SIZE_INNER here!"); + + NS_ENSURE_TRUE(mFrameElement, true); + nsCOMPtr<nsIDocShell> docShell = mFrameElement->OwnerDoc()->GetDocShell(); + NS_ENSURE_TRUE(docShell, true); + nsCOMPtr<nsIDocShellTreeOwner> treeOwner; + docShell->GetTreeOwner(getter_AddRefs(treeOwner)); + nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = do_QueryInterface(treeOwner); + NS_ENSURE_TRUE(treeOwnerAsWin, true); + + // We only care about the parameters that actually changed, see more + // details in TabChild::SetDimensions. + int32_t unused; + int32_t x = aX; + if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_X) { + treeOwnerAsWin->GetPosition(&x, &unused); + } + + int32_t y = aY; + if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_Y) { + treeOwnerAsWin->GetPosition(&unused, &y); + } + + int32_t cx = aCx; + if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CX) { + treeOwnerAsWin->GetSize(&cx, &unused); + } + + int32_t cy = aCy; + if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CY) { + treeOwnerAsWin->GetSize(&unused, &cy); + } + + if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION && + aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER) { + treeOwnerAsWin->SetPositionAndSize(x, y, cx, cy, + nsIBaseWindow::eRepaint); + return true; + } + + if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION) { + treeOwnerAsWin->SetPosition(x, y); + mUpdatedDimensions = false; + UpdatePosition(); + return true; + } + + if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER) { + treeOwnerAsWin->SetSize(cx, cy, true); + return true; + } + + MOZ_ASSERT(false, "Unknown flags!"); + return false; +} + +nsresult +TabParent::UpdatePosition() +{ + RefPtr<nsFrameLoader> frameLoader = GetFrameLoader(); + if (!frameLoader) { + return NS_OK; + } + nsIntRect windowDims; + NS_ENSURE_SUCCESS(frameLoader->GetWindowDimensions(windowDims), NS_ERROR_FAILURE); + UpdateDimensions(windowDims, mDimensions); + return NS_OK; +} + +void +TabParent::UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size) +{ + if (mIsDestroyed) { + return; + } + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + NS_WARNING("No widget found in TabParent::UpdateDimensions"); + return; + } + + hal::ScreenConfiguration config; + hal::GetCurrentScreenConfiguration(&config); + ScreenOrientationInternal orientation = config.orientation(); + LayoutDeviceIntPoint clientOffset = widget->GetClientOffset(); + LayoutDeviceIntPoint chromeOffset = -GetChildProcessOffset(); + + if (!mUpdatedDimensions || mOrientation != orientation || + mDimensions != size || !mRect.IsEqualEdges(rect) || + clientOffset != mClientOffset || + chromeOffset != mChromeOffset) { + + mUpdatedDimensions = true; + mRect = rect; + mDimensions = size; + mOrientation = orientation; + mClientOffset = clientOffset; + mChromeOffset = chromeOffset; + + CSSToLayoutDeviceScale widgetScale = widget->GetDefaultScale(); + + LayoutDeviceIntRect devicePixelRect = + ViewAs<LayoutDevicePixel>(mRect, + PixelCastJustification::LayoutDeviceIsScreenForTabDims); + LayoutDeviceIntSize devicePixelSize = + ViewAs<LayoutDevicePixel>(mDimensions, + PixelCastJustification::LayoutDeviceIsScreenForTabDims); + + CSSRect unscaledRect = devicePixelRect / widgetScale; + CSSSize unscaledSize = devicePixelSize / widgetScale; + Unused << SendUpdateDimensions(unscaledRect, unscaledSize, + orientation, clientOffset, chromeOffset); + } +} + +void +TabParent::SizeModeChanged(const nsSizeMode& aSizeMode) +{ + if (!mIsDestroyed && aSizeMode != mSizeMode) { + mSizeMode = aSizeMode; + Unused << SendSizeModeChanged(aSizeMode); + } +} + +void +TabParent::UIResolutionChanged() +{ + if (!mIsDestroyed) { + // TryCacheDPIAndScale()'s cache is keyed off of + // mDPI being greater than 0, so this invalidates it. + mDPI = -1; + TryCacheDPIAndScale(); + // If mDPI was set to -1 to invalidate it and then TryCacheDPIAndScale + // fails to cache the values, then mDefaultScale.scale might be invalid. + // We don't want to send that value to content. Just send -1 for it too in + // that case. + Unused << SendUIResolutionChanged(mDPI, mRounding, + mDPI < 0 ? -1.0 : mDefaultScale.scale); + } +} + +void +TabParent::ThemeChanged() +{ + if (!mIsDestroyed) { + // The theme has changed, and any cached values we had sent down + // to the child have been invalidated. When this method is called, + // LookAndFeel should have the up-to-date values, which we now + // send down to the child. We do this for every remote tab for now, + // but bug 1156934 has been filed to do it once per content process. + Unused << SendThemeChanged(LookAndFeel::GetIntCache()); + } +} + +void +TabParent::HandleAccessKey(const WidgetKeyboardEvent& aEvent, + nsTArray<uint32_t>& aCharCodes, + const int32_t& aModifierMask) +{ + if (!mIsDestroyed) { + Unused << SendHandleAccessKey(aEvent, aCharCodes, aModifierMask); + } +} + +void +TabParent::Activate() +{ + if (!mIsDestroyed) { + Unused << SendActivate(); + } +} + +void +TabParent::Deactivate() +{ + if (!mIsDestroyed) { + Unused << SendDeactivate(); + } +} + +NS_IMETHODIMP +TabParent::Init(mozIDOMWindowProxy *window) +{ + return NS_OK; +} + +NS_IMETHODIMP +TabParent::GetState(uint32_t *aState) +{ + NS_ENSURE_ARG(aState); + NS_WARNING("SecurityState not valid here"); + *aState = 0; + return NS_OK; +} + +NS_IMETHODIMP +TabParent::SetDocShell(nsIDocShell *aDocShell) +{ + NS_ENSURE_ARG(aDocShell); + NS_WARNING("No mDocShell member in TabParent so there is no docShell to set"); + return NS_OK; +} + + a11y::PDocAccessibleParent* +TabParent::AllocPDocAccessibleParent(PDocAccessibleParent* aParent, + const uint64_t&, const uint32_t&, + const IAccessibleHolder&) +{ +#ifdef ACCESSIBILITY + return new a11y::DocAccessibleParent(); +#else + return nullptr; +#endif +} + +bool +TabParent::DeallocPDocAccessibleParent(PDocAccessibleParent* aParent) +{ +#ifdef ACCESSIBILITY + delete static_cast<a11y::DocAccessibleParent*>(aParent); +#endif + return true; +} + +bool +TabParent::RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc, + PDocAccessibleParent* aParentDoc, + const uint64_t& aParentID, + const uint32_t& aMsaaID, + const IAccessibleHolder& aDocCOMProxy) +{ +#ifdef ACCESSIBILITY + auto doc = static_cast<a11y::DocAccessibleParent*>(aDoc); + if (aParentDoc) { + // A document should never directly be the parent of another document. + // There should always be an outer doc accessible child of the outer + // document containing the child. + MOZ_ASSERT(aParentID); + if (!aParentID) { + return false; + } + + auto parentDoc = static_cast<a11y::DocAccessibleParent*>(aParentDoc); + bool added = parentDoc->AddChildDoc(doc, aParentID); +#ifdef XP_WIN + MOZ_ASSERT(aDocCOMProxy.IsNull()); + if (added) { + a11y::WrapperFor(doc)->SetID(aMsaaID); + } +#endif + return added; + } else { + // null aParentDoc means this document is at the top level in the child + // process. That means it makes no sense to get an id for an accessible + // that is its parent. + MOZ_ASSERT(!aParentID); + if (aParentID) { + return false; + } + + doc->SetTopLevel(); + a11y::DocManager::RemoteDocAdded(doc); +#ifdef XP_WIN + a11y::WrapperFor(doc)->SetID(aMsaaID); + MOZ_ASSERT(!aDocCOMProxy.IsNull()); + RefPtr<IAccessible> proxy(aDocCOMProxy.Get()); + doc->SetCOMProxy(proxy); +#endif + } +#endif + return true; +} + +a11y::DocAccessibleParent* +TabParent::GetTopLevelDocAccessible() const +{ +#ifdef ACCESSIBILITY + // XXX Consider managing non top level PDocAccessibles with their parent + // document accessible. + const ManagedContainer<PDocAccessibleParent>& docs = ManagedPDocAccessibleParent(); + for (auto iter = docs.ConstIter(); !iter.Done(); iter.Next()) { + auto doc = static_cast<a11y::DocAccessibleParent*>(iter.Get()->GetKey()); + if (!doc->ParentDoc()) { + return doc; + } + } + + MOZ_ASSERT(docs.Count() == 0, "If there isn't a top level accessible doc " + "there shouldn't be an accessible doc at all!"); +#endif + return nullptr; +} + +PDocumentRendererParent* +TabParent::AllocPDocumentRendererParent(const nsRect& documentRect, + const gfx::Matrix& transform, + const nsString& bgcolor, + const uint32_t& renderFlags, + const bool& flushLayout, + const nsIntSize& renderSize) +{ + return new DocumentRendererParent(); +} + +bool +TabParent::DeallocPDocumentRendererParent(PDocumentRendererParent* actor) +{ + delete actor; + return true; +} + +PFilePickerParent* +TabParent::AllocPFilePickerParent(const nsString& aTitle, const int16_t& aMode) +{ + return new FilePickerParent(aTitle, aMode); +} + +bool +TabParent::DeallocPFilePickerParent(PFilePickerParent* actor) +{ + delete actor; + return true; +} + +auto +TabParent::AllocPIndexedDBPermissionRequestParent(const Principal& aPrincipal) + -> PIndexedDBPermissionRequestParent* +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr<nsIPrincipal> principal(aPrincipal); + if (!principal) { + return nullptr; + } + + nsCOMPtr<nsIContentParent> manager = Manager(); + if (manager->IsContentParent()) { + if (NS_WARN_IF(!AssertAppPrincipal(manager->AsContentParent(), + principal))) { + return nullptr; + } + } else { + MOZ_CRASH("Figure out security checks for bridged content!"); + } + + if (NS_WARN_IF(!mFrameElement)) { + return nullptr; + } + + return + mozilla::dom::indexedDB::AllocPIndexedDBPermissionRequestParent(mFrameElement, + principal); +} + +bool +TabParent::RecvPIndexedDBPermissionRequestConstructor( + PIndexedDBPermissionRequestParent* aActor, + const Principal& aPrincipal) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aActor); + + return + mozilla::dom::indexedDB::RecvPIndexedDBPermissionRequestConstructor(aActor); +} + +bool +TabParent::DeallocPIndexedDBPermissionRequestParent( + PIndexedDBPermissionRequestParent* aActor) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aActor); + + return + mozilla::dom::indexedDB::DeallocPIndexedDBPermissionRequestParent(aActor); +} + +void +TabParent::SendMouseEvent(const nsAString& aType, float aX, float aY, + int32_t aButton, int32_t aClickCount, + int32_t aModifiers, bool aIgnoreRootScrollFrame) +{ + if (!mIsDestroyed) { + Unused << PBrowserParent::SendMouseEvent(nsString(aType), aX, aY, + aButton, aClickCount, + aModifiers, aIgnoreRootScrollFrame); + } +} + +void +TabParent::SendKeyEvent(const nsAString& aType, + int32_t aKeyCode, + int32_t aCharCode, + int32_t aModifiers, + bool aPreventDefault) +{ + if (!mIsDestroyed) { + Unused << PBrowserParent::SendKeyEvent(nsString(aType), aKeyCode, aCharCode, + aModifiers, aPreventDefault); + } +} + +bool TabParent::SendRealMouseEvent(WidgetMouseEvent& event) +{ + if (mIsDestroyed) { + return false; + } + event.mRefPoint += GetChildProcessOffset(); + + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (widget) { + // When we mouseenter the tab, the tab's cursor should + // become the current cursor. When we mouseexit, we stop. + if (eMouseEnterIntoWidget == event.mMessage) { + mTabSetsCursor = true; + if (mCustomCursor) { + widget->SetCursor(mCustomCursor, mCustomCursorHotspotX, mCustomCursorHotspotY); + } else if (mCursor != nsCursor(-1)) { + widget->SetCursor(mCursor); + } + } else if (eMouseExitFromWidget == event.mMessage) { + mTabSetsCursor = false; + } + } + + ScrollableLayerGuid guid; + uint64_t blockId; + ApzAwareEventRoutingToChild(&guid, &blockId, nullptr); + + if (eMouseMove == event.mMessage) { + if (event.mReason == WidgetMouseEvent::eSynthesized) { + return SendSynthMouseMoveEvent(event, guid, blockId); + } else { + return SendRealMouseMoveEvent(event, guid, blockId); + } + } + + return SendRealMouseButtonEvent(event, guid, blockId); +} + +LayoutDeviceToCSSScale +TabParent::GetLayoutDeviceToCSSScale() +{ + nsCOMPtr<nsIContent> content = do_QueryInterface(mFrameElement); + nsIDocument* doc = (content ? content->OwnerDoc() : nullptr); + nsIPresShell* shell = (doc ? doc->GetShell() : nullptr); + nsPresContext* ctx = (shell ? shell->GetPresContext() : nullptr); + return LayoutDeviceToCSSScale(ctx + ? (float)ctx->AppUnitsPerDevPixel() / nsPresContext::AppUnitsPerCSSPixel() + : 0.0f); +} + +bool +TabParent::SendRealDragEvent(WidgetDragEvent& event, uint32_t aDragAction, + uint32_t aDropEffect) +{ + if (mIsDestroyed) { + return false; + } + event.mRefPoint += GetChildProcessOffset(); + return PBrowserParent::SendRealDragEvent(event, aDragAction, aDropEffect); +} + +LayoutDevicePoint TabParent::AdjustTapToChildWidget(const LayoutDevicePoint& aPoint) +{ + return aPoint + LayoutDevicePoint(GetChildProcessOffset()); +} + +bool TabParent::SendMouseWheelEvent(WidgetWheelEvent& event) +{ + if (mIsDestroyed) { + return false; + } + + ScrollableLayerGuid guid; + uint64_t blockId; + ApzAwareEventRoutingToChild(&guid, &blockId, nullptr); + event.mRefPoint += GetChildProcessOffset(); + return PBrowserParent::SendMouseWheelEvent(event, guid, blockId); +} + +bool TabParent::RecvDispatchWheelEvent(const mozilla::WidgetWheelEvent& aEvent) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + return true; + } + + WidgetWheelEvent localEvent(aEvent); + localEvent.mWidget = widget; + localEvent.mRefPoint -= GetChildProcessOffset(); + + widget->DispatchInputEvent(&localEvent); + return true; +} + +bool +TabParent::RecvDispatchMouseEvent(const mozilla::WidgetMouseEvent& aEvent) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + return true; + } + + WidgetMouseEvent localEvent(aEvent); + localEvent.mWidget = widget; + localEvent.mRefPoint -= GetChildProcessOffset(); + + widget->DispatchInputEvent(&localEvent); + return true; +} + +bool +TabParent::RecvDispatchKeyboardEvent(const mozilla::WidgetKeyboardEvent& aEvent) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + return true; + } + + WidgetKeyboardEvent localEvent(aEvent); + localEvent.mWidget = widget; + localEvent.mRefPoint -= GetChildProcessOffset(); + + widget->DispatchInputEvent(&localEvent); + return true; +} + +static void +DoCommandCallback(mozilla::Command aCommand, void* aData) +{ + static_cast<InfallibleTArray<mozilla::CommandInt>*>(aData)->AppendElement(aCommand); +} + +bool +TabParent::RecvRequestNativeKeyBindings(const WidgetKeyboardEvent& aEvent, + MaybeNativeKeyBinding* aBindings) +{ + AutoTArray<mozilla::CommandInt, 4> singleLine; + AutoTArray<mozilla::CommandInt, 4> multiLine; + AutoTArray<mozilla::CommandInt, 4> richText; + + *aBindings = mozilla::void_t(); + + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + return true; + } + + WidgetKeyboardEvent localEvent(aEvent); + + if (NS_FAILED(widget->AttachNativeKeyEvent(localEvent))) { + return true; + } + + widget->ExecuteNativeKeyBinding(nsIWidget::NativeKeyBindingsForSingleLineEditor, + localEvent, DoCommandCallback, &singleLine); + widget->ExecuteNativeKeyBinding(nsIWidget::NativeKeyBindingsForMultiLineEditor, + localEvent, DoCommandCallback, &multiLine); + widget->ExecuteNativeKeyBinding(nsIWidget::NativeKeyBindingsForRichTextEditor, + localEvent, DoCommandCallback, &richText); + + if (!singleLine.IsEmpty() || !multiLine.IsEmpty() || !richText.IsEmpty()) { + *aBindings = NativeKeyBinding(singleLine, multiLine, richText); + } + + return true; +} + +class SynthesizedEventObserver : public nsIObserver +{ + NS_DECL_ISUPPORTS + +public: + SynthesizedEventObserver(TabParent* aTabParent, const uint64_t& aObserverId) + : mTabParent(aTabParent) + , mObserverId(aObserverId) + { + MOZ_ASSERT(mTabParent); + } + + NS_IMETHOD Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) override + { + if (!mTabParent || !mObserverId) { + // We already sent the notification, or we don't actually need to + // send any notification at all. + return NS_OK; + } + + if (!mTabParent->SendNativeSynthesisResponse(mObserverId, nsCString(aTopic))) { + NS_WARNING("Unable to send native event synthesization response!"); + } + // Null out tabparent to indicate we already sent the response + mTabParent = nullptr; + return NS_OK; + } + +private: + virtual ~SynthesizedEventObserver() { } + + RefPtr<TabParent> mTabParent; + uint64_t mObserverId; +}; + +NS_IMPL_ISUPPORTS(SynthesizedEventObserver, nsIObserver) + +class MOZ_STACK_CLASS AutoSynthesizedEventResponder +{ +public: + AutoSynthesizedEventResponder(TabParent* aTabParent, + const uint64_t& aObserverId, + const char* aTopic) + : mObserver(new SynthesizedEventObserver(aTabParent, aObserverId)) + , mTopic(aTopic) + { } + + ~AutoSynthesizedEventResponder() + { + // This may be a no-op if the observer already sent a response. + mObserver->Observe(nullptr, mTopic, nullptr); + } + + nsIObserver* GetObserver() + { + return mObserver; + } + +private: + nsCOMPtr<nsIObserver> mObserver; + const char* mTopic; +}; + +bool +TabParent::RecvSynthesizeNativeKeyEvent(const int32_t& aNativeKeyboardLayout, + const int32_t& aNativeKeyCode, + const uint32_t& aModifierFlags, + const nsString& aCharacters, + const nsString& aUnmodifiedCharacters, + const uint64_t& aObserverId) +{ + AutoSynthesizedEventResponder responder(this, aObserverId, "keyevent"); + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (widget) { + widget->SynthesizeNativeKeyEvent(aNativeKeyboardLayout, aNativeKeyCode, + aModifierFlags, aCharacters, aUnmodifiedCharacters, + responder.GetObserver()); + } + return true; +} + +bool +TabParent::RecvSynthesizeNativeMouseEvent(const LayoutDeviceIntPoint& aPoint, + const uint32_t& aNativeMessage, + const uint32_t& aModifierFlags, + const uint64_t& aObserverId) +{ + AutoSynthesizedEventResponder responder(this, aObserverId, "mouseevent"); + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (widget) { + widget->SynthesizeNativeMouseEvent(aPoint, aNativeMessage, aModifierFlags, + responder.GetObserver()); + } + return true; +} + +bool +TabParent::RecvSynthesizeNativeMouseMove(const LayoutDeviceIntPoint& aPoint, + const uint64_t& aObserverId) +{ + AutoSynthesizedEventResponder responder(this, aObserverId, "mousemove"); + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (widget) { + widget->SynthesizeNativeMouseMove(aPoint, responder.GetObserver()); + } + return true; +} + +bool +TabParent::RecvSynthesizeNativeMouseScrollEvent(const LayoutDeviceIntPoint& aPoint, + const uint32_t& aNativeMessage, + const double& aDeltaX, + const double& aDeltaY, + const double& aDeltaZ, + const uint32_t& aModifierFlags, + const uint32_t& aAdditionalFlags, + const uint64_t& aObserverId) +{ + AutoSynthesizedEventResponder responder(this, aObserverId, "mousescrollevent"); + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (widget) { + widget->SynthesizeNativeMouseScrollEvent(aPoint, aNativeMessage, + aDeltaX, aDeltaY, aDeltaZ, aModifierFlags, aAdditionalFlags, + responder.GetObserver()); + } + return true; +} + +bool +TabParent::RecvSynthesizeNativeTouchPoint(const uint32_t& aPointerId, + const TouchPointerState& aPointerState, + const LayoutDeviceIntPoint& aPoint, + const double& aPointerPressure, + const uint32_t& aPointerOrientation, + const uint64_t& aObserverId) +{ + AutoSynthesizedEventResponder responder(this, aObserverId, "touchpoint"); + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (widget) { + widget->SynthesizeNativeTouchPoint(aPointerId, aPointerState, aPoint, + aPointerPressure, aPointerOrientation, responder.GetObserver()); + } + return true; +} + +bool +TabParent::RecvSynthesizeNativeTouchTap(const LayoutDeviceIntPoint& aPoint, + const bool& aLongTap, + const uint64_t& aObserverId) +{ + AutoSynthesizedEventResponder responder(this, aObserverId, "touchtap"); + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (widget) { + widget->SynthesizeNativeTouchTap(aPoint, aLongTap, responder.GetObserver()); + } + return true; +} + +bool +TabParent::RecvClearNativeTouchSequence(const uint64_t& aObserverId) +{ + AutoSynthesizedEventResponder responder(this, aObserverId, "cleartouch"); + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (widget) { + widget->ClearNativeTouchSequence(responder.GetObserver()); + } + return true; +} + +bool TabParent::SendRealKeyEvent(WidgetKeyboardEvent& event) +{ + if (mIsDestroyed) { + return false; + } + event.mRefPoint += GetChildProcessOffset(); + + MaybeNativeKeyBinding bindings; + bindings = void_t(); + if (event.mMessage == eKeyPress) { + nsCOMPtr<nsIWidget> widget = GetWidget(); + + AutoTArray<mozilla::CommandInt, 4> singleLine; + AutoTArray<mozilla::CommandInt, 4> multiLine; + AutoTArray<mozilla::CommandInt, 4> richText; + + widget->ExecuteNativeKeyBinding(nsIWidget::NativeKeyBindingsForSingleLineEditor, + event, DoCommandCallback, &singleLine); + widget->ExecuteNativeKeyBinding(nsIWidget::NativeKeyBindingsForMultiLineEditor, + event, DoCommandCallback, &multiLine); + widget->ExecuteNativeKeyBinding(nsIWidget::NativeKeyBindingsForRichTextEditor, + event, DoCommandCallback, &richText); + + if (!singleLine.IsEmpty() || !multiLine.IsEmpty() || !richText.IsEmpty()) { + bindings = NativeKeyBinding(singleLine, multiLine, richText); + } + } + + return PBrowserParent::SendRealKeyEvent(event, bindings); +} + +bool TabParent::SendRealTouchEvent(WidgetTouchEvent& event) +{ + if (mIsDestroyed) { + return false; + } + + // PresShell::HandleEventInternal adds touches on touch end/cancel. This + // confuses remote content and the panning and zooming logic into thinking + // that the added touches are part of the touchend/cancel, when actually + // they're not. + if (event.mMessage == eTouchEnd || event.mMessage == eTouchCancel) { + for (int i = event.mTouches.Length() - 1; i >= 0; i--) { + if (!event.mTouches[i]->mChanged) { + event.mTouches.RemoveElementAt(i); + } + } + } + + ScrollableLayerGuid guid; + uint64_t blockId; + nsEventStatus apzResponse; + ApzAwareEventRoutingToChild(&guid, &blockId, &apzResponse); + + if (mIsDestroyed) { + return false; + } + + LayoutDeviceIntPoint offset = GetChildProcessOffset(); + for (uint32_t i = 0; i < event.mTouches.Length(); i++) { + event.mTouches[i]->mRefPoint += offset; + } + + return (event.mMessage == eTouchMove) ? + PBrowserParent::SendRealTouchMoveEvent(event, guid, blockId, apzResponse) : + PBrowserParent::SendRealTouchEvent(event, guid, blockId, apzResponse); +} + +bool +TabParent::SendHandleTap(TapType aType, + const LayoutDevicePoint& aPoint, + Modifiers aModifiers, + const ScrollableLayerGuid& aGuid, + uint64_t aInputBlockId) +{ + if (mIsDestroyed) { + return false; + } + if ((aType == TapType::eSingleTap || aType == TapType::eSecondTap) && + GetRenderFrame()) { + GetRenderFrame()->TakeFocusForClickFromTap(); + } + LayoutDeviceIntPoint offset = GetChildProcessOffset(); + return PBrowserParent::SendHandleTap(aType, aPoint + offset, aModifiers, aGuid, + aInputBlockId); +} + +bool +TabParent::RecvSyncMessage(const nsString& aMessage, + const ClonedMessageData& aData, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + nsTArray<StructuredCloneData>* aRetVal) +{ + // FIXME Permission check for TabParent in Content process + nsIPrincipal* principal = aPrincipal; + if (Manager()->IsContentParent()) { + ContentParent* parent = Manager()->AsContentParent(); + if (!ContentParent::IgnoreIPCPrincipal() && + parent && principal && !AssertAppPrincipal(parent, principal)) { + return false; + } + } + + StructuredCloneData data; + ipc::UnpackClonedMessageDataForParent(aData, data); + + CrossProcessCpowHolder cpows(Manager(), aCpows); + return ReceiveMessage(aMessage, true, &data, &cpows, aPrincipal, aRetVal); +} + +bool +TabParent::RecvRpcMessage(const nsString& aMessage, + const ClonedMessageData& aData, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + nsTArray<StructuredCloneData>* aRetVal) +{ + // FIXME Permission check for TabParent in Content process + nsIPrincipal* principal = aPrincipal; + if (Manager()->IsContentParent()) { + ContentParent* parent = Manager()->AsContentParent(); + if (!ContentParent::IgnoreIPCPrincipal() && + parent && principal && !AssertAppPrincipal(parent, principal)) { + return false; + } + } + + StructuredCloneData data; + ipc::UnpackClonedMessageDataForParent(aData, data); + + CrossProcessCpowHolder cpows(Manager(), aCpows); + return ReceiveMessage(aMessage, true, &data, &cpows, aPrincipal, aRetVal); +} + +bool +TabParent::RecvAsyncMessage(const nsString& aMessage, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData) +{ + // FIXME Permission check for TabParent in Content process + nsIPrincipal* principal = aPrincipal; + if (Manager()->IsContentParent()) { + ContentParent* parent = Manager()->AsContentParent(); + if (!ContentParent::IgnoreIPCPrincipal() && + parent && principal && !AssertAppPrincipal(parent, principal)) { + return false; + } + } + + StructuredCloneData data; + ipc::UnpackClonedMessageDataForParent(aData, data); + + CrossProcessCpowHolder cpows(Manager(), aCpows); + return ReceiveMessage(aMessage, false, &data, &cpows, aPrincipal, nullptr); +} + +bool +TabParent::RecvSetCursor(const uint32_t& aCursor, const bool& aForce) +{ + mCursor = static_cast<nsCursor>(aCursor); + mCustomCursor = nullptr; + + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (widget) { + if (aForce) { + widget->ClearCachedCursor(); + } + if (mTabSetsCursor) { + widget->SetCursor(mCursor); + } + } + return true; +} + +bool +TabParent::RecvSetCustomCursor(const nsCString& aCursorData, + const uint32_t& aWidth, + const uint32_t& aHeight, + const uint32_t& aStride, + const uint8_t& aFormat, + const uint32_t& aHotspotX, + const uint32_t& aHotspotY, + const bool& aForce) +{ + mCursor = nsCursor(-1); + + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (widget) { + if (aForce) { + widget->ClearCachedCursor(); + } + + if (mTabSetsCursor) { + const gfx::IntSize size(aWidth, aHeight); + + RefPtr<gfx::DataSourceSurface> customCursor = + gfx::CreateDataSourceSurfaceFromData(size, + static_cast<gfx::SurfaceFormat>(aFormat), + reinterpret_cast<const uint8_t*>(aCursorData.BeginReading()), + aStride); + + RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(customCursor, size); + nsCOMPtr<imgIContainer> cursorImage(image::ImageOps::CreateFromDrawable(drawable)); + widget->SetCursor(cursorImage, aHotspotX, aHotspotY); + mCustomCursor = cursorImage; + mCustomCursorHotspotX = aHotspotX; + mCustomCursorHotspotY = aHotspotY; + } + } + + return true; +} + +nsIXULBrowserWindow* +TabParent::GetXULBrowserWindow() +{ + if (!mFrameElement) { + return nullptr; + } + + nsCOMPtr<nsIDocShell> docShell = mFrameElement->OwnerDoc()->GetDocShell(); + if (!docShell) { + return nullptr; + } + + nsCOMPtr<nsIDocShellTreeOwner> treeOwner; + docShell->GetTreeOwner(getter_AddRefs(treeOwner)); + if (!treeOwner) { + return nullptr; + } + + nsCOMPtr<nsIXULWindow> window = do_GetInterface(treeOwner); + if (!window) { + return nullptr; + } + + nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow; + window->GetXULBrowserWindow(getter_AddRefs(xulBrowserWindow)); + return xulBrowserWindow; +} + +bool +TabParent::RecvSetStatus(const uint32_t& aType, const nsString& aStatus) +{ + nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow(); + if (!xulBrowserWindow) { + return true; + } + + switch (aType) { + case nsIWebBrowserChrome::STATUS_SCRIPT: + xulBrowserWindow->SetJSStatus(aStatus); + break; + case nsIWebBrowserChrome::STATUS_LINK: + xulBrowserWindow->SetOverLink(aStatus, nullptr); + break; + } + return true; +} + +bool +TabParent::RecvShowTooltip(const uint32_t& aX, const uint32_t& aY, const nsString& aTooltip, + const nsString& aDirection) +{ + nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow(); + if (!xulBrowserWindow) { + return true; + } + + xulBrowserWindow->ShowTooltip(aX, aY, aTooltip, aDirection); + return true; +} + +bool +TabParent::RecvHideTooltip() +{ + nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow(); + if (!xulBrowserWindow) { + return true; + } + + xulBrowserWindow->HideTooltip(); + return true; +} + +bool +TabParent::RecvNotifyIMEFocus(const ContentCache& aContentCache, + const IMENotification& aIMENotification, + nsIMEUpdatePreference* aPreference) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + *aPreference = nsIMEUpdatePreference(); + return true; + } + + mContentCache.AssignContent(aContentCache, widget, &aIMENotification); + IMEStateManager::NotifyIME(aIMENotification, widget, true); + + if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) { + *aPreference = widget->GetIMEUpdatePreference(); + } + return true; +} + +bool +TabParent::RecvNotifyIMETextChange(const ContentCache& aContentCache, + const IMENotification& aIMENotification) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) + return true; + +#ifdef DEBUG + nsIMEUpdatePreference updatePreference = widget->GetIMEUpdatePreference(); + NS_ASSERTION(updatePreference.WantTextChange(), + "Don't call Send/RecvNotifyIMETextChange without NOTIFY_TEXT_CHANGE"); +#endif + + mContentCache.AssignContent(aContentCache, widget, &aIMENotification); + mContentCache.MaybeNotifyIME(widget, aIMENotification); + return true; +} + +bool +TabParent::RecvNotifyIMECompositionUpdate( + const ContentCache& aContentCache, + const IMENotification& aIMENotification) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + return true; + } + + mContentCache.AssignContent(aContentCache, widget, &aIMENotification); + mContentCache.MaybeNotifyIME(widget, aIMENotification); + return true; +} + +bool +TabParent::RecvNotifyIMESelection(const ContentCache& aContentCache, + const IMENotification& aIMENotification) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) + return true; + + mContentCache.AssignContent(aContentCache, widget, &aIMENotification); + mContentCache.MaybeNotifyIME(widget, aIMENotification); + return true; +} + +bool +TabParent::RecvUpdateContentCache(const ContentCache& aContentCache) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + return true; + } + + mContentCache.AssignContent(aContentCache, widget); + return true; +} + +bool +TabParent::RecvNotifyIMEMouseButtonEvent( + const IMENotification& aIMENotification, + bool* aConsumedByIME) +{ + + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + *aConsumedByIME = false; + return true; + } + nsresult rv = IMEStateManager::NotifyIME(aIMENotification, widget, true); + *aConsumedByIME = rv == NS_SUCCESS_EVENT_CONSUMED; + return true; +} + +bool +TabParent::RecvNotifyIMEPositionChange(const ContentCache& aContentCache, + const IMENotification& aIMENotification) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + return true; + } + + mContentCache.AssignContent(aContentCache, widget, &aIMENotification); + mContentCache.MaybeNotifyIME(widget, aIMENotification); + return true; +} + +bool +TabParent::RecvOnEventNeedingAckHandled(const EventMessage& aMessage) +{ + // This is called when the child process receives WidgetCompositionEvent or + // WidgetSelectionEvent. + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + return true; + } + + // While calling OnEventNeedingAckHandled(), TabParent *might* be destroyed + // since it may send notifications to IME. + RefPtr<TabParent> kungFuDeathGrip(this); + mContentCache.OnEventNeedingAckHandled(widget, aMessage); + return true; +} + +void +TabParent::HandledWindowedPluginKeyEvent(const NativeEventData& aKeyEventData, + bool aIsConsumed) +{ + DebugOnly<bool> ok = + SendHandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed); + NS_WARNING_ASSERTION(ok, "SendHandledWindowedPluginKeyEvent failed"); +} + +bool +TabParent::RecvOnWindowedPluginKeyEvent(const NativeEventData& aKeyEventData) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (NS_WARN_IF(!widget)) { + // Notifies the plugin process of the key event being not consumed by us. + HandledWindowedPluginKeyEvent(aKeyEventData, false); + return true; + } + nsresult rv = widget->OnWindowedPluginKeyEvent(aKeyEventData, this); + if (NS_WARN_IF(NS_FAILED(rv))) { + // Notifies the plugin process of the key event being not consumed by us. + HandledWindowedPluginKeyEvent(aKeyEventData, false); + return true; + } + + // If the key event is posted to another process, we need to wait a call + // of HandledWindowedPluginKeyEvent(). So, nothing to do here in this case. + if (rv == NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY) { + return true; + } + + // Otherwise, the key event is handled synchronously. Let's notify the + // plugin process of the key event's result. + bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED); + HandledWindowedPluginKeyEvent(aKeyEventData, consumed); + + return true; +} + +bool +TabParent::RecvRequestFocus(const bool& aCanRaise) +{ + nsCOMPtr<nsIFocusManager> fm = nsFocusManager::GetFocusManager(); + if (!fm) { + return true; + } + + nsCOMPtr<nsIContent> content = do_QueryInterface(mFrameElement); + if (!content || !content->OwnerDoc()) { + return true; + } + + uint32_t flags = nsIFocusManager::FLAG_NOSCROLL; + if (aCanRaise) + flags |= nsIFocusManager::FLAG_RAISE; + + nsCOMPtr<nsIDOMElement> node = do_QueryInterface(mFrameElement); + fm->SetFocus(node, flags); + return true; +} + +bool +TabParent::RecvEnableDisableCommands(const nsString& aAction, + nsTArray<nsCString>&& aEnabledCommands, + nsTArray<nsCString>&& aDisabledCommands) +{ + nsCOMPtr<nsIRemoteBrowser> remoteBrowser = do_QueryInterface(mFrameElement); + if (remoteBrowser) { + UniquePtr<const char*[]> enabledCommands, disabledCommands; + + if (aEnabledCommands.Length()) { + enabledCommands = MakeUnique<const char*[]>(aEnabledCommands.Length()); + for (uint32_t c = 0; c < aEnabledCommands.Length(); c++) { + enabledCommands[c] = aEnabledCommands[c].get(); + } + } + + if (aDisabledCommands.Length()) { + disabledCommands = MakeUnique<const char*[]>(aDisabledCommands.Length()); + for (uint32_t c = 0; c < aDisabledCommands.Length(); c++) { + disabledCommands[c] = aDisabledCommands[c].get(); + } + } + + remoteBrowser->EnableDisableCommands(aAction, + aEnabledCommands.Length(), enabledCommands.get(), + aDisabledCommands.Length(), disabledCommands.get()); + } + + return true; +} + +NS_IMETHODIMP +TabParent::GetChildProcessOffset(int32_t* aOutCssX, int32_t* aOutCssY) +{ + NS_ENSURE_ARG(aOutCssX); + NS_ENSURE_ARG(aOutCssY); + CSSPoint offset = LayoutDevicePoint(GetChildProcessOffset()) + * GetLayoutDeviceToCSSScale(); + *aOutCssX = offset.x; + *aOutCssY = offset.y; + return NS_OK; +} + +LayoutDeviceIntPoint +TabParent::GetChildProcessOffset() +{ + // The "toplevel widget" in child processes is always at position + // 0,0. Map the event coordinates to match that. + + LayoutDeviceIntPoint offset(0, 0); + RefPtr<nsFrameLoader> frameLoader = GetFrameLoader(); + if (!frameLoader) { + return offset; + } + nsIFrame* targetFrame = frameLoader->GetPrimaryFrameOfOwningContent(); + if (!targetFrame) { + return offset; + } + + // Find out how far we're offset from the nearest widget. + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + return offset; + } + nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(widget, + LayoutDeviceIntPoint(0, 0), + targetFrame); + + return LayoutDeviceIntPoint::FromAppUnitsToNearest( + pt, targetFrame->PresContext()->AppUnitsPerDevPixel()); +} + +bool +TabParent::RecvReplyKeyEvent(const WidgetKeyboardEvent& event) +{ + NS_ENSURE_TRUE(mFrameElement, true); + + WidgetKeyboardEvent localEvent(event); + // Mark the event as not to be dispatched to remote process again. + localEvent.StopCrossProcessForwarding(); + + // Here we convert the WidgetEvent that we received to an nsIDOMEvent + // to be able to dispatch it to the <browser> element as the target element. + nsIDocument* doc = mFrameElement->OwnerDoc(); + nsIPresShell* presShell = doc->GetShell(); + NS_ENSURE_TRUE(presShell, true); + nsPresContext* presContext = presShell->GetPresContext(); + NS_ENSURE_TRUE(presContext, true); + + AutoHandlingUserInputStatePusher userInpStatePusher(localEvent.IsTrusted(), + &localEvent, doc); + + EventDispatcher::Dispatch(mFrameElement, presContext, &localEvent); + return true; +} + +bool +TabParent::RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& aEvent) +{ + NS_ENSURE_TRUE(mFrameElement, true); + + WidgetKeyboardEvent localEvent(aEvent); + localEvent.mWidget = GetWidget(); + + nsIDocument* doc = mFrameElement->OwnerDoc(); + nsCOMPtr<nsIPresShell> presShell = doc->GetShell(); + NS_ENSURE_TRUE(presShell, true); + + if (mFrameElement && + PresShell::BeforeAfterKeyboardEventEnabled() && + localEvent.mMessage != eKeyPress) { + presShell->DispatchAfterKeyboardEvent(mFrameElement, localEvent, + aEvent.DefaultPrevented()); + } + + return true; +} + +bool +TabParent::RecvAccessKeyNotHandled(const WidgetKeyboardEvent& aEvent) +{ + NS_ENSURE_TRUE(mFrameElement, true); + + WidgetKeyboardEvent localEvent(aEvent); + localEvent.mMessage = eAccessKeyNotFound; + localEvent.mAccessKeyForwardedToChild = false; + + // Here we convert the WidgetEvent that we received to an nsIDOMEvent + // to be able to dispatch it to the <browser> element as the target element. + nsIDocument* doc = mFrameElement->OwnerDoc(); + nsIPresShell* presShell = doc->GetShell(); + NS_ENSURE_TRUE(presShell, true); + + if (presShell->CanDispatchEvent()) { + nsPresContext* presContext = presShell->GetPresContext(); + NS_ENSURE_TRUE(presContext, true); + + EventDispatcher::Dispatch(mFrameElement, presContext, &localEvent); + } + + return true; +} + +bool +TabParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + return true; + } + if (NS_WARN_IF(!mContentCache.HandleQueryContentEvent(aEvent, widget)) || + NS_WARN_IF(!aEvent.mSucceeded)) { + return true; + } + switch (aEvent.mMessage) { + case eQueryTextRect: + case eQueryCaretRect: + case eQueryEditorRect: + aEvent.mReply.mRect -= GetChildProcessOffset(); + break; + default: + break; + } + return true; +} + +bool +TabParent::SendCompositionEvent(WidgetCompositionEvent& event) +{ + if (mIsDestroyed) { + return false; + } + + if (!mContentCache.OnCompositionEvent(event)) { + return true; + } + return PBrowserParent::SendCompositionEvent(event); +} + +bool +TabParent::SendSelectionEvent(WidgetSelectionEvent& event) +{ + if (mIsDestroyed) { + return false; + } + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + return true; + } + mContentCache.OnSelectionEvent(event); + if (NS_WARN_IF(!PBrowserParent::SendSelectionEvent(event))) { + return false; + } + event.mSucceeded = true; + return true; +} + +bool +TabParent::SendPasteTransferable(const IPCDataTransfer& aDataTransfer, + const bool& aIsPrivateData, + const IPC::Principal& aRequestingPrincipal) +{ + return PBrowserParent::SendPasteTransferable(aDataTransfer, + aIsPrivateData, + aRequestingPrincipal); +} + +/*static*/ TabParent* +TabParent::GetFrom(nsFrameLoader* aFrameLoader) +{ + if (!aFrameLoader) { + return nullptr; + } + PBrowserParent* remoteBrowser = aFrameLoader->GetRemoteBrowser(); + return static_cast<TabParent*>(remoteBrowser); +} + +/*static*/ TabParent* +TabParent::GetFrom(nsIFrameLoader* aFrameLoader) +{ + if (!aFrameLoader) + return nullptr; + return GetFrom(static_cast<nsFrameLoader*>(aFrameLoader)); +} + +/*static*/ TabParent* +TabParent::GetFrom(nsITabParent* aTabParent) +{ + return static_cast<TabParent*>(aTabParent); +} + +/*static*/ TabParent* +TabParent::GetFrom(PBrowserParent* aTabParent) +{ + return static_cast<TabParent*>(aTabParent); +} + +/*static*/ TabParent* +TabParent::GetFrom(nsIContent* aContent) +{ + nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(aContent); + if (!loaderOwner) { + return nullptr; + } + RefPtr<nsFrameLoader> frameLoader = loaderOwner->GetFrameLoader(); + return GetFrom(frameLoader); +} + +/*static*/ TabId +TabParent::GetTabIdFrom(nsIDocShell *docShell) +{ + nsCOMPtr<nsITabChild> tabChild(TabChild::GetFrom(docShell)); + if (tabChild) { + return static_cast<TabChild*>(tabChild.get())->GetTabId(); + } + return TabId(0); +} + +RenderFrameParent* +TabParent::GetRenderFrame() +{ + PRenderFrameParent* p = LoneManagedOrNullAsserts(ManagedPRenderFrameParent()); + RenderFrameParent* frame = static_cast<RenderFrameParent*>(p); + + return frame; +} + +bool +TabParent::RecvRequestIMEToCommitComposition(const bool& aCancel, + bool* aIsCommitted, + nsString* aCommittedString) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + *aIsCommitted = false; + return true; + } + + *aIsCommitted = + mContentCache.RequestIMEToCommitComposition(widget, aCancel, + *aCommittedString); + return true; +} + +bool +TabParent::RecvStartPluginIME(const WidgetKeyboardEvent& aKeyboardEvent, + const int32_t& aPanelX, const int32_t& aPanelY, + nsString* aCommitted) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + return true; + } + widget->StartPluginIME(aKeyboardEvent, + (int32_t&)aPanelX, + (int32_t&)aPanelY, + *aCommitted); + return true; +} + +bool +TabParent::RecvSetPluginFocused(const bool& aFocused) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + return true; + } + widget->SetPluginFocused((bool&)aFocused); + return true; +} + + bool +TabParent::RecvSetCandidateWindowForPlugin( + const CandidateWindowPosition& aPosition) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + return true; + } + + widget->SetCandidateWindowForPlugin(aPosition); + return true; +} + +bool +TabParent::RecvDefaultProcOfPluginEvent(const WidgetPluginEvent& aEvent) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + return true; + } + + widget->DefaultProcOfPluginEvent(aEvent); + return true; +} + +bool +TabParent::RecvGetInputContext(int32_t* aIMEEnabled, + int32_t* aIMEOpen) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + *aIMEEnabled = IMEState::DISABLED; + *aIMEOpen = IMEState::OPEN_STATE_NOT_SUPPORTED; + return true; + } + + InputContext context = widget->GetInputContext(); + *aIMEEnabled = static_cast<int32_t>(context.mIMEState.mEnabled); + *aIMEOpen = static_cast<int32_t>(context.mIMEState.mOpen); + return true; +} + +bool +TabParent::RecvSetInputContext(const int32_t& aIMEEnabled, + const int32_t& aIMEOpen, + const nsString& aType, + const nsString& aInputmode, + const nsString& aActionHint, + const int32_t& aCause, + const int32_t& aFocusChange) +{ + InputContext context; + context.mIMEState.mEnabled = static_cast<IMEState::Enabled>(aIMEEnabled); + context.mIMEState.mOpen = static_cast<IMEState::Open>(aIMEOpen); + context.mHTMLInputType.Assign(aType); + context.mHTMLInputInputmode.Assign(aInputmode); + context.mActionHint.Assign(aActionHint); + context.mOrigin = InputContext::ORIGIN_CONTENT; + + InputContextAction action( + static_cast<InputContextAction::Cause>(aCause), + static_cast<InputContextAction::FocusChange>(aFocusChange)); + + IMEStateManager::SetInputContextForChildProcess(this, context, action); + + return true; +} + +bool +TabParent::RecvIsParentWindowMainWidgetVisible(bool* aIsVisible) +{ + nsCOMPtr<nsIContent> frame = do_QueryInterface(mFrameElement); + if (!frame) + return true; + nsCOMPtr<nsIDOMWindowUtils> windowUtils = + do_QueryInterface(frame->OwnerDoc()->GetWindow()); + nsresult rv = windowUtils->GetIsParentWindowMainWidgetVisible(aIsVisible); + return NS_SUCCEEDED(rv); +} + +bool +TabParent::RecvGetDPI(float* aValue) +{ + TryCacheDPIAndScale(); + + MOZ_ASSERT(mDPI > 0, + "Must not ask for DPI before OwnerElement is received!"); + *aValue = mDPI; + return true; +} + +bool +TabParent::RecvGetDefaultScale(double* aValue) +{ + TryCacheDPIAndScale(); + + MOZ_ASSERT(mDefaultScale.scale > 0, + "Must not ask for scale before OwnerElement is received!"); + *aValue = mDefaultScale.scale; + return true; +} + +bool +TabParent::RecvGetWidgetRounding(int32_t* aValue) +{ + TryCacheDPIAndScale(); + + MOZ_ASSERT(mRounding > 0, + "Must not ask for rounding before OwnerElement is received!"); + *aValue = mRounding; + return true; +} + +bool +TabParent::RecvGetMaxTouchPoints(uint32_t* aTouchPoints) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (widget) { + *aTouchPoints = widget->GetMaxTouchPoints(); + } else { + *aTouchPoints = 0; + } + return true; +} + +already_AddRefed<nsIWidget> +TabParent::GetTopLevelWidget() +{ + nsCOMPtr<nsIContent> content = do_QueryInterface(mFrameElement); + if (content) { + nsIPresShell* shell = content->OwnerDoc()->GetShell(); + if (shell) { + nsViewManager* vm = shell->GetViewManager(); + nsCOMPtr<nsIWidget> widget; + vm->GetRootWidget(getter_AddRefs(widget)); + return widget.forget(); + } + } + return nullptr; +} + +bool +TabParent::RecvGetWidgetNativeData(WindowsHandle* aValue) +{ + *aValue = 0; + nsCOMPtr<nsIWidget> widget = GetTopLevelWidget(); + if (widget) { + *aValue = reinterpret_cast<WindowsHandle>( + widget->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW)); + } + return true; +} + +bool +TabParent::RecvSetNativeChildOfShareableWindow(const uintptr_t& aChildWindow) +{ +#if defined(XP_WIN) + nsCOMPtr<nsIWidget> widget = GetTopLevelWidget(); + if (widget) { + // Note that this call will probably cause a sync native message to the + // process that owns the child window. + widget->SetNativeData(NS_NATIVE_CHILD_OF_SHAREABLE_WINDOW, aChildWindow); + } + return true; +#else + NS_NOTREACHED( + "TabParent::RecvSetNativeChildOfShareableWindow not implemented!"); + return false; +#endif +} + +bool +TabParent::RecvDispatchFocusToTopLevelWindow() +{ + nsCOMPtr<nsIWidget> widget = GetTopLevelWidget(); + if (widget) { + widget->SetFocus(false); + } + return true; +} + +bool +TabParent::ReceiveMessage(const nsString& aMessage, + bool aSync, + StructuredCloneData* aData, + CpowHolder* aCpows, + nsIPrincipal* aPrincipal, + nsTArray<StructuredCloneData>* aRetVal) +{ + RefPtr<nsFrameLoader> frameLoader = GetFrameLoader(true); + if (frameLoader && frameLoader->GetFrameMessageManager()) { + RefPtr<nsFrameMessageManager> manager = + frameLoader->GetFrameMessageManager(); + + manager->ReceiveMessage(mFrameElement, + frameLoader, + aMessage, + aSync, + aData, + aCpows, + aPrincipal, + aRetVal); + } + return true; +} + +// nsIAuthPromptProvider + +// This method is largely copied from nsDocShell::GetAuthPrompt +NS_IMETHODIMP +TabParent::GetAuthPrompt(uint32_t aPromptReason, const nsIID& iid, + void** aResult) +{ + // we're either allowing auth, or it's a proxy request + nsresult rv; + nsCOMPtr<nsIPromptFactory> wwatch = + do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsPIDOMWindowOuter> window; + nsCOMPtr<nsIContent> frame = do_QueryInterface(mFrameElement); + if (frame) + window = frame->OwnerDoc()->GetWindow(); + + // Get an auth prompter for our window so that the parenting + // of the dialogs works as it should when using tabs. + nsCOMPtr<nsISupports> prompt; + rv = wwatch->GetPrompt(window, iid, getter_AddRefs(prompt)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsILoginManagerPrompter> prompter = do_QueryInterface(prompt); + if (prompter) { + nsCOMPtr<nsIDOMElement> browser = do_QueryInterface(mFrameElement); + prompter->SetBrowser(browser); + } + + *aResult = prompt.forget().take(); + return NS_OK; +} + +PColorPickerParent* +TabParent::AllocPColorPickerParent(const nsString& aTitle, + const nsString& aInitialColor) +{ + return new ColorPickerParent(aTitle, aInitialColor); +} + +bool +TabParent::DeallocPColorPickerParent(PColorPickerParent* actor) +{ + delete actor; + return true; +} + +PDatePickerParent* +TabParent::AllocPDatePickerParent(const nsString& aTitle, + const nsString& aInitialDate) +{ + return new DatePickerParent(aTitle, aInitialDate); +} + +bool +TabParent::DeallocPDatePickerParent(PDatePickerParent* actor) +{ + delete actor; + return true; +} + +PRenderFrameParent* +TabParent::AllocPRenderFrameParent() +{ + MOZ_ASSERT(ManagedPRenderFrameParent().IsEmpty()); + RefPtr<nsFrameLoader> frameLoader = GetFrameLoader(); + uint64_t layersId = 0; + bool success = false; + + PRenderFrameParent* renderFrame = + new RenderFrameParent(frameLoader, &success); + if (success) { + RenderFrameParent* rfp = static_cast<RenderFrameParent*>(renderFrame); + layersId = rfp->GetLayersId(); + AddTabParentToTable(layersId, this); + } + return renderFrame; +} + +bool +TabParent::DeallocPRenderFrameParent(PRenderFrameParent* aFrame) +{ + delete aFrame; + return true; +} + +bool +TabParent::SetRenderFrame(PRenderFrameParent* aRFParent) +{ + if (IsInitedByParent()) { + return false; + } + + RefPtr<nsFrameLoader> frameLoader = GetFrameLoader(); + + if (!frameLoader) { + return false; + } + + RenderFrameParent* renderFrame = static_cast<RenderFrameParent*>(aRFParent); + bool success = renderFrame->Init(frameLoader); + if (!success) { + return false; + } + + uint64_t layersId = renderFrame->GetLayersId(); + AddTabParentToTable(layersId, this); + + return true; +} + +bool +TabParent::GetRenderFrameInfo(TextureFactoryIdentifier* aTextureFactoryIdentifier, + uint64_t* aLayersId) +{ + RenderFrameParent* rfp = GetRenderFrame(); + if (!rfp) { + return false; + } + + *aLayersId = rfp->GetLayersId(); + rfp->GetTextureFactoryIdentifier(aTextureFactoryIdentifier); + return true; +} + +bool +TabParent::RecvAudioChannelActivityNotification(const uint32_t& aAudioChannel, + const bool& aActive) +{ + if (aAudioChannel >= NUMBER_OF_AUDIO_CHANNELS) { + return false; + } + + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); + if (os) { + nsAutoCString topic; + topic.Assign("audiochannel-activity-"); + topic.Append(AudioChannelService::GetAudioChannelTable()[aAudioChannel].tag); + + os->NotifyObservers(NS_ISUPPORTS_CAST(nsITabParent*, this), + topic.get(), + aActive ? u"active" : u"inactive"); + } + + return true; +} + +already_AddRefed<nsFrameLoader> +TabParent::GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy) const +{ + if (mIsDestroyed && !aUseCachedFrameLoaderAfterDestroy) { + return nullptr; + } + + if (mFrameLoader) { + RefPtr<nsFrameLoader> fl = mFrameLoader; + return fl.forget(); + } + nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner = do_QueryInterface(mFrameElement); + return frameLoaderOwner ? frameLoaderOwner->GetFrameLoader() : nullptr; +} + +void +TabParent::TryCacheDPIAndScale() +{ + if (mDPI > 0) { + return; + } + + nsCOMPtr<nsIWidget> widget = GetWidget(); + + if (widget) { + mDPI = widget->GetDPI(); + mRounding = widget->RoundsWidgetCoordinatesTo(); + mDefaultScale = widget->GetDefaultScale(); + } +} + +already_AddRefed<nsIWidget> +TabParent::GetWidget() const +{ + if (!mFrameElement) { + return nullptr; + } + nsCOMPtr<nsIWidget> widget = nsContentUtils::WidgetForDocument(mFrameElement->OwnerDoc()); + return widget.forget(); +} + +void +TabParent::ApzAwareEventRoutingToChild(ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId, + nsEventStatus* aOutApzResponse) +{ + // Let the widget know that the event will be sent to the child process, + // which will (hopefully) send a confirmation notice back to APZ. + // Do this even if APZ is off since we need it for swipe gesture support on + // OS X without APZ. + InputAPZContext::SetRoutedToChildProcess(); + + if (AsyncPanZoomEnabled()) { + if (aOutTargetGuid) { + *aOutTargetGuid = InputAPZContext::GetTargetLayerGuid(); + + // There may be cases where the APZ hit-testing code came to a different + // conclusion than the main-thread hit-testing code as to where the event + // is destined. In such cases the layersId of the APZ result may not match + // the layersId of this renderframe. In such cases the main-thread hit- + // testing code "wins" so we need to update the guid to reflect this. + if (RenderFrameParent* rfp = GetRenderFrame()) { + if (aOutTargetGuid->mLayersId != rfp->GetLayersId()) { + *aOutTargetGuid = ScrollableLayerGuid(rfp->GetLayersId(), 0, FrameMetrics::NULL_SCROLL_ID); + } + } + } + if (aOutInputBlockId) { + *aOutInputBlockId = InputAPZContext::GetInputBlockId(); + } + if (aOutApzResponse) { + *aOutApzResponse = InputAPZContext::GetApzResponse(); + } + } else { + if (aOutInputBlockId) { + *aOutInputBlockId = 0; + } + if (aOutApzResponse) { + *aOutApzResponse = nsEventStatus_eIgnore; + } + } +} + +bool +TabParent::RecvBrowserFrameOpenWindow(PBrowserParent* aOpener, + PRenderFrameParent* aRenderFrame, + const nsString& aURL, + const nsString& aName, + const nsString& aFeatures, + bool* aOutWindowOpened, + TextureFactoryIdentifier* aTextureFactoryIdentifier, + uint64_t* aLayersId) +{ + BrowserElementParent::OpenWindowResult opened = + BrowserElementParent::OpenWindowOOP(TabParent::GetFrom(aOpener), + this, aRenderFrame, aURL, aName, aFeatures, + aTextureFactoryIdentifier, aLayersId); + *aOutWindowOpened = (opened == BrowserElementParent::OPEN_WINDOW_ADDED); + if (!*aOutWindowOpened) { + Destroy(); + } + return true; +} + +bool +TabParent::RecvRespondStartSwipeEvent(const uint64_t& aInputBlockId, + const bool& aStartSwipe) +{ + if (nsCOMPtr<nsIWidget> widget = GetWidget()) { + widget->ReportSwipeStarted(aInputBlockId, aStartSwipe); + } + return true; +} + +already_AddRefed<nsILoadContext> +TabParent::GetLoadContext() +{ + nsCOMPtr<nsILoadContext> loadContext; + if (mLoadContext) { + loadContext = mLoadContext; + } else { + bool isPrivate = mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW; + SetPrivateBrowsingAttributes(isPrivate); + loadContext = new LoadContext(GetOwnerElement(), + true /* aIsContent */, + isPrivate, + mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW, + OriginAttributesRef()); + mLoadContext = loadContext; + } + return loadContext.forget(); +} + +NS_IMETHODIMP +TabParent::GetUseAsyncPanZoom(bool* useAsyncPanZoom) +{ + *useAsyncPanZoom = AsyncPanZoomEnabled(); + return NS_OK; +} + +// defined in nsITabParent +NS_IMETHODIMP +TabParent::SetDocShellIsActive(bool isActive) +{ + // Increment the epoch so that layer tree updates from previous + // SetDocShellIsActive requests are ignored. + mLayerTreeEpoch++; + + // docshell is consider prerendered only if not active yet + mIsPrerendered &= !isActive; + mDocShellIsActive = isActive; + Unused << SendSetDocShellIsActive(isActive, mPreserveLayers, mLayerTreeEpoch); + + // Ask the child to repaint using the PHangMonitor channel/thread (which may + // be less congested). + if (isActive) { + ContentParent* cp = Manager()->AsContentParent(); + cp->ForceTabPaint(this, mLayerTreeEpoch); + } + + return NS_OK; +} + +NS_IMETHODIMP +TabParent::GetDocShellIsActive(bool* aIsActive) +{ + *aIsActive = mDocShellIsActive; + return NS_OK; +} + +NS_IMETHODIMP +TabParent::GetIsPrerendered(bool* aIsPrerendered) +{ + *aIsPrerendered = mIsPrerendered; + return NS_OK; +} + +NS_IMETHODIMP +TabParent::PreserveLayers(bool aPreserveLayers) +{ + mPreserveLayers = aPreserveLayers; + return NS_OK; +} + +NS_IMETHODIMP +TabParent::SuppressDisplayport(bool aEnabled) +{ + if (IsDestroyed()) { + return NS_OK; + } + +#ifdef DEBUG + if (aEnabled) { + mActiveSupressDisplayportCount++; + } else { + mActiveSupressDisplayportCount--; + } + MOZ_ASSERT(mActiveSupressDisplayportCount >= 0); +#endif + + Unused << SendSuppressDisplayport(aEnabled); + return NS_OK; +} + +NS_IMETHODIMP +TabParent::GetTabId(uint64_t* aId) +{ + *aId = GetTabId(); + return NS_OK; +} + +NS_IMETHODIMP +TabParent::GetOsPid(int32_t* aId) +{ + *aId = Manager()->Pid(); + return NS_OK; +} + +NS_IMETHODIMP +TabParent::GetHasContentOpener(bool* aResult) +{ + *aResult = mHasContentOpener; + return NS_OK; +} + +void +TabParent::SetHasContentOpener(bool aHasContentOpener) +{ + mHasContentOpener = aHasContentOpener; +} + +NS_IMETHODIMP +TabParent::NavigateByKey(bool aForward, bool aForDocumentNavigation) +{ + Unused << SendNavigateByKey(aForward, aForDocumentNavigation); + return NS_OK; +} + +class LayerTreeUpdateRunnable final + : public mozilla::Runnable +{ + uint64_t mLayersId; + uint64_t mEpoch; + bool mActive; + +public: + explicit LayerTreeUpdateRunnable(uint64_t aLayersId, uint64_t aEpoch, bool aActive) + : mLayersId(aLayersId), mEpoch(aEpoch), mActive(aActive) + { + MOZ_ASSERT(!NS_IsMainThread()); + } + +private: + NS_IMETHOD Run() override { + MOZ_ASSERT(NS_IsMainThread()); + if (RefPtr<TabParent> tabParent = TabParent::GetTabParentFromLayersId(mLayersId)) { + tabParent->LayerTreeUpdate(mEpoch, mActive); + } + return NS_OK; + } +}; + +void +TabParent::LayerTreeUpdate(uint64_t aEpoch, bool aActive) +{ + // Ignore updates from old epochs. They might tell us that layers are + // available when we've already sent a message to clear them. We can't trust + // the update in that case since layers could disappear anytime after that. + if (aEpoch != mLayerTreeEpoch || mIsDestroyed) { + return; + } + + nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(mFrameElement); + if (!target) { + NS_WARNING("Could not locate target for layer tree message."); + return; + } + + RefPtr<Event> event = NS_NewDOMEvent(mFrameElement, nullptr, nullptr); + if (aActive) { + event->InitEvent(NS_LITERAL_STRING("MozLayerTreeReady"), true, false); + } else { + event->InitEvent(NS_LITERAL_STRING("MozLayerTreeCleared"), true, false); + } + event->SetTrusted(true); + event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true; + bool dummy; + mFrameElement->DispatchEvent(event, &dummy); +} + +bool +TabParent::RecvForcePaintNoOp(const uint64_t& aLayerObserverEpoch) +{ + // We sent a ForcePaint message when layers were already visible. In this + // case, we should act as if an update occurred even though we already have + // the layers. + LayerTreeUpdate(aLayerObserverEpoch, true); + return true; +} + +bool +TabParent::RecvRemotePaintIsReady() +{ + nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(mFrameElement); + if (!target) { + NS_WARNING("Could not locate target for MozAfterRemotePaint message."); + return true; + } + + RefPtr<Event> event = NS_NewDOMEvent(mFrameElement, nullptr, nullptr); + event->InitEvent(NS_LITERAL_STRING("MozAfterRemotePaint"), false, false); + event->SetTrusted(true); + event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true; + bool dummy; + mFrameElement->DispatchEvent(event, &dummy); + return true; +} + +mozilla::plugins::PPluginWidgetParent* +TabParent::AllocPPluginWidgetParent() +{ + return new mozilla::plugins::PluginWidgetParent(); +} + +bool +TabParent::DeallocPPluginWidgetParent(mozilla::plugins::PPluginWidgetParent* aActor) +{ + delete aActor; + return true; +} + +nsresult +TabParent::HandleEvent(nsIDOMEvent* aEvent) +{ + nsAutoString eventType; + aEvent->GetType(eventType); + + if (eventType.EqualsLiteral("MozUpdateWindowPos") && !mIsDestroyed) { + // This event is sent when the widget moved. Therefore we only update + // the position. + return UpdatePosition(); + } + return NS_OK; +} + +class FakeChannel final : public nsIChannel, + public nsIAuthPromptCallback, + public nsIInterfaceRequestor, + public nsILoadContext +{ +public: + FakeChannel(const nsCString& aUri, uint64_t aCallbackId, Element* aElement) + : mCallbackId(aCallbackId) + , mElement(aElement) + { + NS_NewURI(getter_AddRefs(mUri), aUri); + } + + NS_DECL_ISUPPORTS +#define NO_IMPL override { return NS_ERROR_NOT_IMPLEMENTED; } + NS_IMETHOD GetName(nsACString&) NO_IMPL + NS_IMETHOD IsPending(bool*) NO_IMPL + NS_IMETHOD GetStatus(nsresult*) NO_IMPL + NS_IMETHOD Cancel(nsresult) NO_IMPL + NS_IMETHOD Suspend() NO_IMPL + NS_IMETHOD Resume() NO_IMPL + NS_IMETHOD GetLoadGroup(nsILoadGroup**) NO_IMPL + NS_IMETHOD SetLoadGroup(nsILoadGroup*) NO_IMPL + NS_IMETHOD SetLoadFlags(nsLoadFlags) NO_IMPL + NS_IMETHOD GetLoadFlags(nsLoadFlags*) NO_IMPL + NS_IMETHOD GetOriginalURI(nsIURI**) NO_IMPL + NS_IMETHOD SetOriginalURI(nsIURI*) NO_IMPL + NS_IMETHOD GetURI(nsIURI** aUri) override + { + nsCOMPtr<nsIURI> copy = mUri; + copy.forget(aUri); + return NS_OK; + } + NS_IMETHOD GetOwner(nsISupports**) NO_IMPL + NS_IMETHOD SetOwner(nsISupports*) NO_IMPL + NS_IMETHOD GetLoadInfo(nsILoadInfo** aLoadInfo) override + { + nsCOMPtr<nsILoadInfo> copy = mLoadInfo; + copy.forget(aLoadInfo); + return NS_OK; + } + NS_IMETHOD SetLoadInfo(nsILoadInfo* aLoadInfo) override + { + mLoadInfo = aLoadInfo; + return NS_OK; + } + NS_IMETHOD GetNotificationCallbacks(nsIInterfaceRequestor** aRequestor) override + { + NS_ADDREF(*aRequestor = this); + return NS_OK; + } + NS_IMETHOD SetNotificationCallbacks(nsIInterfaceRequestor*) NO_IMPL + NS_IMETHOD GetSecurityInfo(nsISupports**) NO_IMPL + NS_IMETHOD GetContentType(nsACString&) NO_IMPL + NS_IMETHOD SetContentType(const nsACString&) NO_IMPL + NS_IMETHOD GetContentCharset(nsACString&) NO_IMPL + NS_IMETHOD SetContentCharset(const nsACString&) NO_IMPL + NS_IMETHOD GetContentLength(int64_t*) NO_IMPL + NS_IMETHOD SetContentLength(int64_t) NO_IMPL + NS_IMETHOD Open(nsIInputStream**) NO_IMPL + NS_IMETHOD Open2(nsIInputStream**) NO_IMPL + NS_IMETHOD AsyncOpen(nsIStreamListener*, nsISupports*) NO_IMPL + NS_IMETHOD AsyncOpen2(nsIStreamListener*) NO_IMPL + NS_IMETHOD GetContentDisposition(uint32_t*) NO_IMPL + NS_IMETHOD SetContentDisposition(uint32_t) NO_IMPL + NS_IMETHOD GetContentDispositionFilename(nsAString&) NO_IMPL + NS_IMETHOD SetContentDispositionFilename(const nsAString&) NO_IMPL + NS_IMETHOD GetContentDispositionHeader(nsACString&) NO_IMPL + NS_IMETHOD OnAuthAvailable(nsISupports *aContext, nsIAuthInformation *aAuthInfo) override; + NS_IMETHOD OnAuthCancelled(nsISupports *aContext, bool userCancel) override; + NS_IMETHOD GetInterface(const nsIID & uuid, void **result) override + { + return QueryInterface(uuid, result); + } + NS_IMETHOD GetAssociatedWindow(mozIDOMWindowProxy**) NO_IMPL + NS_IMETHOD GetTopWindow(mozIDOMWindowProxy**) NO_IMPL + NS_IMETHOD GetTopFrameElement(nsIDOMElement** aElement) override + { + nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(mElement); + elem.forget(aElement); + return NS_OK; + } + NS_IMETHOD GetNestedFrameId(uint64_t*) NO_IMPL + NS_IMETHOD GetIsContent(bool*) NO_IMPL + NS_IMETHOD GetUsePrivateBrowsing(bool*) NO_IMPL + NS_IMETHOD SetUsePrivateBrowsing(bool) NO_IMPL + NS_IMETHOD SetPrivateBrowsing(bool) NO_IMPL + NS_IMETHOD GetIsInIsolatedMozBrowserElement(bool*) NO_IMPL + NS_IMETHOD GetAppId(uint32_t*) NO_IMPL + NS_IMETHOD GetOriginAttributes(JS::MutableHandleValue) NO_IMPL + NS_IMETHOD GetUseRemoteTabs(bool*) NO_IMPL + NS_IMETHOD SetRemoteTabs(bool) NO_IMPL + NS_IMETHOD IsTrackingProtectionOn(bool*) NO_IMPL +#undef NO_IMPL + +protected: + ~FakeChannel() {} + + nsCOMPtr<nsIURI> mUri; + uint64_t mCallbackId; + nsCOMPtr<Element> mElement; + nsCOMPtr<nsILoadInfo> mLoadInfo; +}; + +NS_IMPL_ISUPPORTS(FakeChannel, nsIChannel, nsIAuthPromptCallback, + nsIRequest, nsIInterfaceRequestor, nsILoadContext); + +bool +TabParent::RecvAsyncAuthPrompt(const nsCString& aUri, + const nsString& aRealm, + const uint64_t& aCallbackId) +{ + nsCOMPtr<nsIAuthPrompt2> authPrompt; + GetAuthPrompt(nsIAuthPromptProvider::PROMPT_NORMAL, + NS_GET_IID(nsIAuthPrompt2), + getter_AddRefs(authPrompt)); + RefPtr<FakeChannel> channel = new FakeChannel(aUri, aCallbackId, mFrameElement); + uint32_t promptFlags = nsIAuthInformation::AUTH_HOST; + + RefPtr<nsAuthInformationHolder> holder = + new nsAuthInformationHolder(promptFlags, aRealm, + EmptyCString()); + + uint32_t level = nsIAuthPrompt2::LEVEL_NONE; + nsCOMPtr<nsICancelable> dummy; + nsresult rv = + authPrompt->AsyncPromptAuth(channel, channel, nullptr, + level, holder, getter_AddRefs(dummy)); + + return rv == NS_OK; +} + +bool +TabParent::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers, + const uint32_t& aAction, + const OptionalShmem& aVisualDnDData, + const uint32_t& aStride, const uint8_t& aFormat, + const LayoutDeviceIntRect& aDragRect) +{ + mInitialDataTransferItems.Clear(); + nsIPresShell* shell = mFrameElement->OwnerDoc()->GetShell(); + if (!shell) { + if (Manager()->IsContentParent()) { + Unused << Manager()->AsContentParent()->SendEndDragSession(true, true, + LayoutDeviceIntPoint()); + } + return true; + } + + EventStateManager* esm = shell->GetPresContext()->EventStateManager(); + for (uint32_t i = 0; i < aTransfers.Length(); ++i) { + mInitialDataTransferItems.AppendElement(mozilla::Move(aTransfers[i].items())); + } + if (Manager()->IsContentParent()) { + nsCOMPtr<nsIDragService> dragService = + do_GetService("@mozilla.org/widget/dragservice;1"); + if (dragService) { + dragService->MaybeAddChildProcess(Manager()->AsContentParent()); + } + } + + if (aVisualDnDData.type() == OptionalShmem::Tvoid_t || + !aVisualDnDData.get_Shmem().IsReadable() || + aVisualDnDData.get_Shmem().Size<char>() < aDragRect.height * aStride) { + mDnDVisualization = nullptr; + } else { + mDnDVisualization = + gfx::CreateDataSourceSurfaceFromData(gfx::IntSize(aDragRect.width, aDragRect.height), + static_cast<gfx::SurfaceFormat>(aFormat), + aVisualDnDData.get_Shmem().get<uint8_t>(), + aStride); + } + + mDragValid = true; + mDragRect = aDragRect; + + esm->BeginTrackingRemoteDragGesture(mFrameElement); + + if (aVisualDnDData.type() == OptionalShmem::TShmem) { + Unused << DeallocShmem(aVisualDnDData); + } + + return true; +} + +void +TabParent::AddInitialDnDDataTo(DataTransfer* aDataTransfer) +{ + for (uint32_t i = 0; i < mInitialDataTransferItems.Length(); ++i) { + nsTArray<IPCDataTransferItem>& itemArray = mInitialDataTransferItems[i]; + for (auto& item : itemArray) { + RefPtr<nsVariantCC> variant = new nsVariantCC(); + // Special case kFilePromiseMime so that we get the right + // nsIFlavorDataProvider for it. + if (item.flavor().EqualsLiteral(kFilePromiseMime)) { + RefPtr<nsISupports> flavorDataProvider = + new nsContentAreaDragDropDataProvider(); + variant->SetAsISupports(flavorDataProvider); + } else if (item.data().type() == IPCDataTransferData::TnsString) { + variant->SetAsAString(item.data().get_nsString()); + } else if (item.data().type() == IPCDataTransferData::TPBlobParent) { + auto* parent = static_cast<BlobParent*>(item.data().get_PBlobParent()); + RefPtr<BlobImpl> impl = parent->GetBlobImpl(); + variant->SetAsISupports(impl); + } else if (item.data().type() == IPCDataTransferData::TShmem) { + if (nsContentUtils::IsFlavorImage(item.flavor())) { + // An image! Get the imgIContainer for it and set it in the variant. + nsCOMPtr<imgIContainer> imageContainer; + nsresult rv = + nsContentUtils::DataTransferItemToImage(item, + getter_AddRefs(imageContainer)); + if (NS_FAILED(rv)) { + continue; + } + variant->SetAsISupports(imageContainer); + } else { + Shmem data = item.data().get_Shmem(); + variant->SetAsACString(nsDependentCString(data.get<char>(), data.Size<char>())); + } + + mozilla::Unused << DeallocShmem(item.data().get_Shmem()); + } + + // Using system principal here, since once the data is on parent process + // side, it can be handled as being from browser chrome or OS. + + // We set aHidden to false, as we don't need to worry about hiding data + // from content in the parent process where there is no content. + // XXX: Nested Content Processes may change this + aDataTransfer->SetDataWithPrincipalFromOtherProcess(NS_ConvertUTF8toUTF16(item.flavor()), + variant, i, + nsContentUtils::GetSystemPrincipal(), + /* aHidden = */ false); + } + } + mInitialDataTransferItems.Clear(); +} + +bool +TabParent::TakeDragVisualization(RefPtr<mozilla::gfx::SourceSurface>& aSurface, + LayoutDeviceIntRect* aDragRect) +{ + if (!mDragValid) + return false; + + aSurface = mDnDVisualization.forget(); + *aDragRect = mDragRect; + mDragValid = false; + return true; +} + +bool +TabParent::AsyncPanZoomEnabled() const +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + return widget && widget->AsyncPanZoomEnabled(); +} + +NS_IMETHODIMP +TabParent::StartPersistence(uint64_t aOuterWindowID, + nsIWebBrowserPersistDocumentReceiver* aRecv) +{ + nsCOMPtr<nsIContentParent> manager = Manager(); + if (!manager->IsContentParent()) { + return NS_ERROR_UNEXPECTED; + } + auto* actor = new WebBrowserPersistDocumentParent(); + actor->SetOnReady(aRecv); + return manager->AsContentParent() + ->SendPWebBrowserPersistDocumentConstructor(actor, this, aOuterWindowID) + ? NS_OK : NS_ERROR_FAILURE; + // (The actor will be destroyed on constructor failure.) +} + +ShowInfo +TabParent::GetShowInfo() +{ + TryCacheDPIAndScale(); + if (mFrameElement) { + nsAutoString name; + mFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name); + bool allowFullscreen = + mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) || + mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen); + bool isPrivate = mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing); + bool isTransparent = + nsContentUtils::IsChromeDoc(mFrameElement->OwnerDoc()) && + mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::transparent); + return ShowInfo(name, allowFullscreen, isPrivate, false, + isTransparent, mDPI, mRounding, mDefaultScale.scale); + } + + return ShowInfo(EmptyString(), false, false, false, + false, mDPI, mRounding, mDefaultScale.scale); +} + +void +TabParent::AudioChannelChangeNotification(nsPIDOMWindowOuter* aWindow, + AudioChannel aAudioChannel, + float aVolume, + bool aMuted) +{ + if (!mFrameElement || !mFrameElement->OwnerDoc()) { + return; + } + + nsCOMPtr<nsPIDOMWindowOuter> window = mFrameElement->OwnerDoc()->GetWindow(); + while (window) { + if (window == aWindow) { + Unused << SendAudioChannelChangeNotification(static_cast<uint32_t>(aAudioChannel), + aVolume, aMuted); + break; + } + + nsCOMPtr<nsPIDOMWindowOuter> win = window->GetScriptableParentOrNull(); + if (!win) { + break; + } + + window = win; + } +} + +bool +TabParent::RecvGetTabCount(uint32_t* aValue) +{ + *aValue = 0; + + nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow(); + NS_ENSURE_TRUE(xulBrowserWindow, true); + + uint32_t tabCount; + nsresult rv = xulBrowserWindow->GetTabCount(&tabCount); + NS_ENSURE_SUCCESS(rv, true); + + *aValue = tabCount; + return true; +} + +bool +TabParent::RecvLookUpDictionary(const nsString& aText, + nsTArray<FontRange>&& aFontRangeArray, + const bool& aIsVertical, + const LayoutDeviceIntPoint& aPoint) +{ + nsCOMPtr<nsIWidget> widget = GetWidget(); + if (!widget) { + return true; + } + + widget->LookUpDictionary(aText, aFontRangeArray, aIsVertical, + aPoint - GetChildProcessOffset()); + return true; +} + +bool +TabParent::RecvNotifySessionHistoryChange(const uint32_t& aCount) +{ + RefPtr<nsFrameLoader> frameLoader(GetFrameLoader()); + if (!frameLoader) { + // FrameLoader can be nullptr if the it is destroying. + // In this case session history change can simply be ignored. + return true; + } + + nsCOMPtr<nsIPartialSHistory> partialHistory; + frameLoader->GetPartialSessionHistory(getter_AddRefs(partialHistory)); + if (!partialHistory) { + // PartialSHistory is not enabled + return true; + } + + partialHistory->OnSessionHistoryChange(aCount); + return true; +} + +bool +TabParent::RecvRequestCrossBrowserNavigation(const uint32_t& aGlobalIndex) +{ + RefPtr<nsFrameLoader> frameLoader(GetFrameLoader()); + if (!frameLoader) { + // FrameLoader can be nullptr if the it is destroying. + // In this case we can ignore the request. + return true; + } + + return NS_SUCCEEDED(frameLoader->RequestGroupedHistoryNavigation(aGlobalIndex)); +} + +NS_IMETHODIMP +FakeChannel::OnAuthAvailable(nsISupports *aContext, nsIAuthInformation *aAuthInfo) +{ + nsAuthInformationHolder* holder = + static_cast<nsAuthInformationHolder*>(aAuthInfo); + + if (!net::gNeckoChild->SendOnAuthAvailable(mCallbackId, + holder->User(), + holder->Password(), + holder->Domain())) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +FakeChannel::OnAuthCancelled(nsISupports *aContext, bool userCancel) +{ + if (!net::gNeckoChild->SendOnAuthCancelled(mCallbackId, userCancel)) { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h new file mode 100644 index 000000000..43afb0538 --- /dev/null +++ b/dom/ipc/TabParent.h @@ -0,0 +1,813 @@ +/* -*- 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/. */ + +#ifndef mozilla_tabs_TabParent_h +#define mozilla_tabs_TabParent_h + +#include "js/TypeDecls.h" +#include "mozilla/ContentCache.h" +#include "mozilla/dom/AudioChannelBinding.h" +#include "mozilla/dom/ipc/IdType.h" +#include "mozilla/dom/PBrowserParent.h" +#include "mozilla/dom/PContent.h" +#include "mozilla/dom/PFilePickerParent.h" +#include "mozilla/dom/TabContext.h" +#include "mozilla/EventForwards.h" +#include "mozilla/dom/File.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/RefPtr.h" +#include "mozilla/Move.h" +#include "nsCOMPtr.h" +#include "nsIAuthPromptProvider.h" +#include "nsIBrowserDOMWindow.h" +#include "nsIDOMEventListener.h" +#include "nsIKeyEventInPluginCallback.h" +#include "nsISecureBrowserUI.h" +#include "nsITabParent.h" +#include "nsIWebBrowserPersistable.h" +#include "nsIXULBrowserWindow.h" +#include "nsRefreshDriver.h" +#include "nsWeakReference.h" +#include "Units.h" +#include "nsIWidget.h" +#include "nsIPartialSHistory.h" + +class nsFrameLoader; +class nsIFrameLoader; +class nsIContent; +class nsIPrincipal; +class nsIURI; +class nsILoadContext; +class nsIDocShell; + +namespace mozilla { + +namespace a11y { +class DocAccessibleParent; +} + +namespace jsipc { +class CpowHolder; +} // namespace jsipc + +namespace layers { +struct TextureFactoryIdentifier; +} // namespace layers + +namespace layout { +class RenderFrameParent; +} // namespace layout + +namespace widget { +struct IMENotification; +} // namespace widget + +namespace gfx { +class SourceSurface; +class DataSourceSurface; +} // namespace gfx + +namespace dom { + +class ClonedMessageData; +class nsIContentParent; +class Element; +class DataTransfer; + +namespace ipc { +class StructuredCloneData; +} // ipc namespace + +class TabParent final : public PBrowserParent + , public nsIDOMEventListener + , public nsITabParent + , public nsIAuthPromptProvider + , public nsISecureBrowserUI + , public nsIKeyEventInPluginCallback + , public nsSupportsWeakReference + , public TabContext + , public nsAPostRefreshObserver + , public nsIWebBrowserPersistable +{ + typedef mozilla::dom::ClonedMessageData ClonedMessageData; + + virtual ~TabParent(); + +public: + // Helper class for ContentParent::RecvCreateWindow. + struct AutoUseNewTab; + + // nsITabParent + NS_DECL_NSITABPARENT + // nsIDOMEventListener interfaces + NS_DECL_NSIDOMEVENTLISTENER + + TabParent(nsIContentParent* aManager, + const TabId& aTabId, + const TabContext& aContext, + uint32_t aChromeFlags); + + Element* GetOwnerElement() const { return mFrameElement; } + already_AddRefed<nsPIDOMWindowOuter> GetParentWindowOuter(); + + void SetOwnerElement(Element* aElement); + + void CacheFrameLoader(nsFrameLoader* aFrameLoader); + + /** + * Get the mozapptype attribute from this TabParent's owner DOM element. + */ + void GetAppType(nsAString& aOut); + + /** + * Returns true iff this TabParent's nsIFrameLoader is visible. + * + * The frameloader's visibility can be independent of e.g. its docshell's + * visibility. + */ + bool IsVisible() const; + + nsIBrowserDOMWindow *GetBrowserDOMWindow() const { return mBrowserDOMWindow; } + + void SetBrowserDOMWindow(nsIBrowserDOMWindow* aBrowserDOMWindow) + { + mBrowserDOMWindow = aBrowserDOMWindow; + } + + void SetHasContentOpener(bool aHasContentOpener); + + void SwapFrameScriptsFrom(nsTArray<FrameScriptInfo>& aFrameScripts) + { + aFrameScripts.SwapElements(mDelayedFrameScripts); + } + + already_AddRefed<nsILoadContext> GetLoadContext(); + + already_AddRefed<nsIWidget> GetTopLevelWidget(); + + nsIXULBrowserWindow* GetXULBrowserWindow(); + + void Destroy(); + + void RemoveWindowListeners(); + + void AddWindowListeners(); + + void DidRefresh() override; + + virtual bool RecvMoveFocus(const bool& aForward, + const bool& aForDocumentNavigation) override; + + virtual bool RecvSizeShellTo(const uint32_t& aFlags, + const int32_t& aWidth, + const int32_t& aHeight, + const int32_t& aShellItemWidth, + const int32_t& aShellItemHeight) override; + + virtual bool RecvDropLinks(nsTArray<nsString>&& aLinks) override; + + virtual bool RecvEvent(const RemoteDOMEvent& aEvent) override; + + virtual bool RecvReplyKeyEvent(const WidgetKeyboardEvent& aEvent) override; + + virtual bool + RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& aEvent) override; + + virtual bool + RecvAccessKeyNotHandled(const WidgetKeyboardEvent& aEvent) override; + + virtual bool RecvBrowserFrameOpenWindow(PBrowserParent* aOpener, + PRenderFrameParent* aRenderFrame, + const nsString& aURL, + const nsString& aName, + const nsString& aFeatures, + bool* aOutWindowOpened, + TextureFactoryIdentifier* aTextureFactoryIdentifier, + uint64_t* aLayersId) override; + + virtual bool + RecvSyncMessage(const nsString& aMessage, + const ClonedMessageData& aData, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + nsTArray<ipc::StructuredCloneData>* aRetVal) override; + + virtual bool + RecvRpcMessage(const nsString& aMessage, + const ClonedMessageData& aData, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + nsTArray<ipc::StructuredCloneData>* aRetVal) override; + + virtual bool RecvAsyncMessage(const nsString& aMessage, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData) override; + + virtual bool + RecvNotifyIMEFocus(const ContentCache& aContentCache, + const widget::IMENotification& aEventMessage, + nsIMEUpdatePreference* aPreference) override; + + virtual bool + RecvNotifyIMETextChange(const ContentCache& aContentCache, + const widget::IMENotification& aEventMessage) override; + + virtual bool + RecvNotifyIMECompositionUpdate(const ContentCache& aContentCache, + const widget::IMENotification& aEventMessage) override; + + virtual bool + RecvNotifyIMESelection(const ContentCache& aContentCache, + const widget::IMENotification& aEventMessage) override; + + virtual bool + RecvUpdateContentCache(const ContentCache& aContentCache) override; + + virtual bool + RecvNotifyIMEMouseButtonEvent(const widget::IMENotification& aEventMessage, + bool* aConsumedByIME) override; + + virtual bool + RecvNotifyIMEPositionChange(const ContentCache& aContentCache, + const widget::IMENotification& aEventMessage) override; + + virtual bool + RecvOnEventNeedingAckHandled(const EventMessage& aMessage) override; + + virtual bool + RecvRequestIMEToCommitComposition(const bool& aCancel, + bool* aIsCommitted, + nsString* aCommittedString) override; + + virtual bool + RecvStartPluginIME(const WidgetKeyboardEvent& aKeyboardEvent, + const int32_t& aPanelX, + const int32_t& aPanelY, + nsString* aCommitted) override; + + virtual bool RecvSetPluginFocused(const bool& aFocused) override; + + virtual bool RecvSetCandidateWindowForPlugin( + const widget::CandidateWindowPosition& aPosition) override; + + virtual bool + RecvDefaultProcOfPluginEvent(const WidgetPluginEvent& aEvent) override; + + virtual bool RecvGetInputContext(int32_t* aIMEEnabled, + int32_t* aIMEOpen) override; + + virtual bool RecvSetInputContext(const int32_t& aIMEEnabled, + const int32_t& aIMEOpen, + const nsString& aType, + const nsString& aInputmode, + const nsString& aActionHint, + const int32_t& aCause, + const int32_t& aFocusChange) override; + + + // See nsIKeyEventInPluginCallback + virtual void HandledWindowedPluginKeyEvent( + const NativeEventData& aKeyEventData, + bool aIsConsumed) override; + + virtual bool RecvOnWindowedPluginKeyEvent( + const NativeEventData& aKeyEventData) override; + + virtual bool RecvRequestFocus(const bool& aCanRaise) override; + + virtual bool RecvLookUpDictionary( + const nsString& aText, + nsTArray<mozilla::FontRange>&& aFontRangeArray, + const bool& aIsVertical, + const LayoutDeviceIntPoint& aPoint) override; + + virtual bool + RecvEnableDisableCommands(const nsString& aAction, + nsTArray<nsCString>&& aEnabledCommands, + nsTArray<nsCString>&& aDisabledCommands) override; + + virtual bool + RecvSetCursor(const uint32_t& aValue, const bool& aForce) override; + + virtual bool RecvSetCustomCursor(const nsCString& aUri, + const uint32_t& aWidth, + const uint32_t& aHeight, + const uint32_t& aStride, + const uint8_t& aFormat, + const uint32_t& aHotspotX, + const uint32_t& aHotspotY, + const bool& aForce) override; + + virtual bool RecvSetStatus(const uint32_t& aType, + const nsString& aStatus) override; + + virtual bool RecvIsParentWindowMainWidgetVisible(bool* aIsVisible) override; + + virtual bool RecvShowTooltip(const uint32_t& aX, + const uint32_t& aY, + const nsString& aTooltip, + const nsString& aDirection) override; + + virtual bool RecvHideTooltip() override; + + virtual bool RecvGetDPI(float* aValue) override; + + virtual bool RecvGetDefaultScale(double* aValue) override; + + virtual bool RecvGetWidgetRounding(int32_t* aValue) override; + + virtual bool RecvGetMaxTouchPoints(uint32_t* aTouchPoints) override; + + virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue) override; + + virtual bool RecvSetNativeChildOfShareableWindow(const uintptr_t& childWindow) override; + + virtual bool RecvDispatchFocusToTopLevelWindow() override; + + virtual bool RecvRespondStartSwipeEvent(const uint64_t& aInputBlockId, + const bool& aStartSwipe) override; + + virtual bool + RecvDispatchWheelEvent(const mozilla::WidgetWheelEvent& aEvent) override; + + virtual bool + RecvDispatchMouseEvent(const mozilla::WidgetMouseEvent& aEvent) override; + + virtual bool + RecvDispatchKeyboardEvent(const mozilla::WidgetKeyboardEvent& aEvent) override; + + virtual PColorPickerParent* + AllocPColorPickerParent(const nsString& aTitle, + const nsString& aInitialColor) override; + + virtual bool + DeallocPColorPickerParent(PColorPickerParent* aColorPicker) override; + + virtual PDatePickerParent* + AllocPDatePickerParent(const nsString& aTitle, const nsString& aInitialDate) override; + virtual bool DeallocPDatePickerParent(PDatePickerParent* aDatePicker) override; + + virtual PDocAccessibleParent* + AllocPDocAccessibleParent(PDocAccessibleParent*, const uint64_t&, + const uint32_t&, const IAccessibleHolder&) override; + + virtual bool DeallocPDocAccessibleParent(PDocAccessibleParent*) override; + + virtual bool + RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc, + PDocAccessibleParent* aParentDoc, + const uint64_t& aParentID, + const uint32_t& aMsaaID, + const IAccessibleHolder& aDocCOMProxy) override; + + /** + * Return the top level doc accessible parent for this tab. + */ + a11y::DocAccessibleParent* GetTopLevelDocAccessible() const; + + void LoadURL(nsIURI* aURI); + + // XXX/cjones: it's not clear what we gain by hiding these + // message-sending functions under a layer of indirection and + // eating the return values + void Show(const ScreenIntSize& aSize, bool aParentIsActive); + + void UpdateDimensions(const nsIntRect& aRect, const ScreenIntSize& aSize); + + void SizeModeChanged(const nsSizeMode& aSizeMode); + + void UIResolutionChanged(); + + void ThemeChanged(); + + void HandleAccessKey(const WidgetKeyboardEvent& aEvent, + nsTArray<uint32_t>& aCharCodes, + const int32_t& aModifierMask); + + void Activate(); + + void Deactivate(); + + bool MapEventCoordinatesForChildProcess(mozilla::WidgetEvent* aEvent); + + void MapEventCoordinatesForChildProcess(const LayoutDeviceIntPoint& aOffset, + mozilla::WidgetEvent* aEvent); + + LayoutDeviceToCSSScale GetLayoutDeviceToCSSScale(); + + virtual bool + RecvRequestNativeKeyBindings(const mozilla::WidgetKeyboardEvent& aEvent, + MaybeNativeKeyBinding* aBindings) override; + + virtual bool + RecvSynthesizeNativeKeyEvent(const int32_t& aNativeKeyboardLayout, + const int32_t& aNativeKeyCode, + const uint32_t& aModifierFlags, + const nsString& aCharacters, + const nsString& aUnmodifiedCharacters, + const uint64_t& aObserverId) override; + + virtual bool + RecvSynthesizeNativeMouseEvent(const LayoutDeviceIntPoint& aPoint, + const uint32_t& aNativeMessage, + const uint32_t& aModifierFlags, + const uint64_t& aObserverId) override; + + virtual bool + RecvSynthesizeNativeMouseMove(const LayoutDeviceIntPoint& aPoint, + const uint64_t& aObserverId) override; + + virtual bool + RecvSynthesizeNativeMouseScrollEvent(const LayoutDeviceIntPoint& aPoint, + const uint32_t& aNativeMessage, + const double& aDeltaX, + const double& aDeltaY, + const double& aDeltaZ, + const uint32_t& aModifierFlags, + const uint32_t& aAdditionalFlags, + const uint64_t& aObserverId) override; + + virtual bool + RecvSynthesizeNativeTouchPoint(const uint32_t& aPointerId, + const TouchPointerState& aPointerState, + const LayoutDeviceIntPoint& aPoint, + const double& aPointerPressure, + const uint32_t& aPointerOrientation, + const uint64_t& aObserverId) override; + + virtual bool + RecvSynthesizeNativeTouchTap(const LayoutDeviceIntPoint& aPoint, + const bool& aLongTap, + const uint64_t& aObserverId) override; + + virtual bool + RecvClearNativeTouchSequence(const uint64_t& aObserverId) override; + + void SendMouseEvent(const nsAString& aType, float aX, float aY, + int32_t aButton, int32_t aClickCount, + int32_t aModifiers, bool aIgnoreRootScrollFrame); + + void SendKeyEvent(const nsAString& aType, int32_t aKeyCode, + int32_t aCharCode, int32_t aModifiers, + bool aPreventDefault); + + bool SendRealMouseEvent(mozilla::WidgetMouseEvent& event); + + bool SendRealDragEvent(mozilla::WidgetDragEvent& aEvent, + uint32_t aDragAction, + uint32_t aDropEffect); + + bool SendMouseWheelEvent(mozilla::WidgetWheelEvent& event); + + bool SendRealKeyEvent(mozilla::WidgetKeyboardEvent& event); + + bool SendRealTouchEvent(WidgetTouchEvent& event); + + bool SendHandleTap(TapType aType, + const LayoutDevicePoint& aPoint, + Modifiers aModifiers, + const ScrollableLayerGuid& aGuid, + uint64_t aInputBlockId); + + virtual PDocumentRendererParent* + AllocPDocumentRendererParent(const nsRect& documentRect, + const gfx::Matrix& transform, + const nsString& bgcolor, + const uint32_t& renderFlags, + const bool& flushLayout, + const nsIntSize& renderSize) override; + + virtual bool + DeallocPDocumentRendererParent(PDocumentRendererParent* actor) override; + + virtual PFilePickerParent* + AllocPFilePickerParent(const nsString& aTitle, + const int16_t& aMode) override; + + virtual bool DeallocPFilePickerParent(PFilePickerParent* actor) override; + + virtual PIndexedDBPermissionRequestParent* + AllocPIndexedDBPermissionRequestParent(const Principal& aPrincipal) override; + + virtual bool + RecvPIndexedDBPermissionRequestConstructor( + PIndexedDBPermissionRequestParent* aActor, + const Principal& aPrincipal) + override; + + virtual bool + DeallocPIndexedDBPermissionRequestParent( + PIndexedDBPermissionRequestParent* aActor) + override; + + bool GetGlobalJSObject(JSContext* cx, JSObject** globalp); + + NS_DECL_ISUPPORTS + NS_DECL_NSIAUTHPROMPTPROVIDER + NS_DECL_NSISECUREBROWSERUI + NS_DECL_NSIWEBBROWSERPERSISTABLE + + bool HandleQueryContentEvent(mozilla::WidgetQueryContentEvent& aEvent); + + bool SendCompositionEvent(mozilla::WidgetCompositionEvent& event); + + bool SendSelectionEvent(mozilla::WidgetSelectionEvent& event); + + bool SendPasteTransferable(const IPCDataTransfer& aDataTransfer, + const bool& aIsPrivateData, + const IPC::Principal& aRequestingPrincipal); + + static TabParent* GetFrom(nsFrameLoader* aFrameLoader); + + static TabParent* GetFrom(nsIFrameLoader* aFrameLoader); + + static TabParent* GetFrom(nsITabParent* aTabParent); + + static TabParent* GetFrom(PBrowserParent* aTabParent); + + static TabParent* GetFrom(nsIContent* aContent); + + static TabId GetTabIdFrom(nsIDocShell* docshell); + + nsIContentParent* Manager() const { return mManager; } + + /** + * Let managees query if Destroy() is already called so they don't send out + * messages when the PBrowser actor is being destroyed. + */ + bool IsDestroyed() const { return mIsDestroyed; } + + already_AddRefed<nsIWidget> GetWidget() const; + + const TabId GetTabId() const + { + return mTabId; + } + + LayoutDeviceIntPoint GetChildProcessOffset(); + LayoutDevicePoint AdjustTapToChildWidget(const LayoutDevicePoint& aPoint); + + /** + * Native widget remoting protocol for use with windowed plugins with e10s. + */ + virtual PPluginWidgetParent* AllocPPluginWidgetParent() override; + + virtual bool + DeallocPPluginWidgetParent(PPluginWidgetParent* aActor) override; + + void SetInitedByParent() { mInitedByParent = true; } + + bool IsInitedByParent() const { return mInitedByParent; } + + static TabParent* GetNextTabParent(); + + bool SendLoadRemoteScript(const nsString& aURL, + const bool& aRunInGlobalScope); + + void LayerTreeUpdate(uint64_t aEpoch, bool aActive); + + virtual bool + RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers, + const uint32_t& aAction, + const OptionalShmem& aVisualDnDData, + const uint32_t& aStride, const uint8_t& aFormat, + const LayoutDeviceIntRect& aDragRect) override; + + void AddInitialDnDDataTo(DataTransfer* aDataTransfer); + + bool TakeDragVisualization(RefPtr<mozilla::gfx::SourceSurface>& aSurface, + LayoutDeviceIntRect* aDragRect); + + layout::RenderFrameParent* GetRenderFrame(); + + void AudioChannelChangeNotification(nsPIDOMWindowOuter* aWindow, + AudioChannel aAudioChannel, + float aVolume, + bool aMuted); + bool SetRenderFrame(PRenderFrameParent* aRFParent); + bool GetRenderFrameInfo(TextureFactoryIdentifier* aTextureFactoryIdentifier, + uint64_t* aLayersId); + + bool RecvEnsureLayersConnected() override; + +protected: + bool ReceiveMessage(const nsString& aMessage, + bool aSync, + ipc::StructuredCloneData* aData, + mozilla::jsipc::CpowHolder* aCpows, + nsIPrincipal* aPrincipal, + nsTArray<ipc::StructuredCloneData>* aJSONRetVal = nullptr); + + virtual bool RecvAsyncAuthPrompt(const nsCString& aUri, + const nsString& aRealm, + const uint64_t& aCallbackId) override; + + virtual bool Recv__delete__() override; + + virtual void ActorDestroy(ActorDestroyReason why) override; + + Element* mFrameElement; + nsCOMPtr<nsIBrowserDOMWindow> mBrowserDOMWindow; + + virtual PRenderFrameParent* AllocPRenderFrameParent() override; + + virtual bool DeallocPRenderFrameParent(PRenderFrameParent* aFrame) override; + + virtual bool RecvRemotePaintIsReady() override; + + virtual bool RecvForcePaintNoOp(const uint64_t& aLayerObserverEpoch) override; + + virtual bool RecvSetDimensions(const uint32_t& aFlags, + const int32_t& aX, const int32_t& aY, + const int32_t& aCx, const int32_t& aCy) override; + + virtual bool RecvGetTabCount(uint32_t* aValue) override; + + virtual bool RecvAudioChannelActivityNotification(const uint32_t& aAudioChannel, + const bool& aActive) override; + + virtual bool RecvNotifySessionHistoryChange(const uint32_t& aCount) override; + + virtual bool RecvRequestCrossBrowserNavigation(const uint32_t& aGlobalIndex) override; + + ContentCacheInParent mContentCache; + + nsIntRect mRect; + ScreenIntSize mDimensions; + ScreenOrientationInternal mOrientation; + float mDPI; + int32_t mRounding; + CSSToLayoutDeviceScale mDefaultScale; + bool mUpdatedDimensions; + nsSizeMode mSizeMode; + LayoutDeviceIntPoint mClientOffset; + LayoutDeviceIntPoint mChromeOffset; + +private: + void DestroyInternal(); + + already_AddRefed<nsFrameLoader> + GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy = false) const; + + RefPtr<nsIContentParent> mManager; + void TryCacheDPIAndScale(); + + nsresult UpdatePosition(); + + bool AsyncPanZoomEnabled() const; + + // Cached value indicating the docshell active state of the remote browser. + bool mDocShellIsActive; + + // Update state prior to routing an APZ-aware event to the child process. + // |aOutTargetGuid| will contain the identifier + // of the APZC instance that handled the event. aOutTargetGuid may be null. + // |aOutInputBlockId| will contain the identifier of the input block + // that this event was added to, if there was one. aOutInputBlockId may be null. + // |aOutApzResponse| will contain the response that the APZ gave when processing + // the input block; this is used for generating appropriate pointercancel events. + void ApzAwareEventRoutingToChild(ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId, + nsEventStatus* aOutApzResponse); + + // When true, we've initiated normal shutdown and notified our managing PContent. + bool mMarkedDestroying; + // When true, the TabParent is invalid and we should not send IPC messages anymore. + bool mIsDestroyed; + + uint32_t mChromeFlags; + + nsTArray<nsTArray<IPCDataTransferItem>> mInitialDataTransferItems; + + RefPtr<gfx::DataSourceSurface> mDnDVisualization; + bool mDragValid; + LayoutDeviceIntRect mDragRect; + + // When true, the TabParent is initialized without child side's request. + // When false, the TabParent is initialized by window.open() from child side. + bool mInitedByParent; + + nsCOMPtr<nsILoadContext> mLoadContext; + + // We keep a strong reference to the frameloader after we've sent the + // Destroy message and before we've received __delete__. This allows us to + // dispatch message manager messages during this time. + RefPtr<nsFrameLoader> mFrameLoader; + + TabId mTabId; + + // When loading a new tab or window via window.open, the child process sends + // a new PBrowser to use. We store that tab in sNextTabParent and then + // proceed through the browser's normal paths to create a new + // window/tab. When it comes time to create a new TabParent, we instead use + // sNextTabParent. + static TabParent* sNextTabParent; + + // When loading a new tab or window via window.open, the child is + // responsible for loading the URL it wants into the new TabChild. When the + // parent receives the CreateWindow message, though, it sends a LoadURL + // message, usually for about:blank. It's important for the about:blank load + // to get processed because the Firefox frontend expects every new window to + // immediately start loading something (see bug 1123090). However, we want + // the child to process the LoadURL message before it returns from + // ProvideWindow so that the URL sent from the parent doesn't override the + // child's URL. This is not possible using our IPC mechanisms. To solve the + // problem, we skip sending the LoadURL message in the parent and instead + // return the URL as a result from CreateWindow. The child simulates + // receiving a LoadURL message before returning from ProvideWindow. + // + // The mCreatingWindow flag is set while dispatching CreateWindow. During + // that time, any LoadURL calls are skipped and the URL is stored in + // mSkippedURL. + bool mCreatingWindow; + nsCString mDelayedURL; + + // When loading a new tab or window via window.open, we want to ensure that + // frame scripts for that tab are loaded before any scripts start to run in + // the window. We can't load the frame scripts the normal way, using + // separate IPC messages, since they won't be processed by the child until + // returning to the event loop, which is too late. Instead, we queue up + // frame scripts that we intend to load and send them as part of the + // CreateWindow response. Then TabChild loads them immediately. + nsTArray<FrameScriptInfo> mDelayedFrameScripts; + + // Cached cursor setting from TabChild. When the cursor is over the tab, + // it should take this appearance. + nsCursor mCursor; + nsCOMPtr<imgIContainer> mCustomCursor; + uint32_t mCustomCursorHotspotX, mCustomCursorHotspotY; + + // True if the cursor changes from the TabChild should change the widget + // cursor. This happens whenever the cursor is in the tab's region. + bool mTabSetsCursor; + + RefPtr<nsIPresShell> mPresShellWithRefreshListener; + + bool mHasContentOpener; + +#ifdef DEBUG + int32_t mActiveSupressDisplayportCount; +#endif + + ShowInfo GetShowInfo(); + +private: + // This is used when APZ needs to find the TabParent associated with a layer + // to dispatch events. + typedef nsDataHashtable<nsUint64HashKey, TabParent*> LayerToTabParentTable; + static LayerToTabParentTable* sLayerToTabParentTable; + + static void AddTabParentToTable(uint64_t aLayersId, TabParent* aTabParent); + + static void RemoveTabParentFromTable(uint64_t aLayersId); + + uint64_t mLayerTreeEpoch; + + // If this flag is set, then the tab's layers will be preserved even when + // the tab's docshell is inactive. + bool mPreserveLayers; + +public: + static TabParent* GetTabParentFromLayersId(uint64_t aLayersId); +}; + +struct MOZ_STACK_CLASS TabParent::AutoUseNewTab final +{ +public: + AutoUseNewTab(TabParent* aNewTab, bool* aWindowIsNew, nsCString* aURLToLoad) + : mNewTab(aNewTab), mWindowIsNew(aWindowIsNew), mURLToLoad(aURLToLoad) + { + MOZ_ASSERT(!TabParent::sNextTabParent); + MOZ_ASSERT(!aNewTab->mCreatingWindow); + + TabParent::sNextTabParent = aNewTab; + aNewTab->mCreatingWindow = true; + aNewTab->mDelayedURL.Truncate(); + } + + ~AutoUseNewTab() + { + mNewTab->mCreatingWindow = false; + *mURLToLoad = mNewTab->mDelayedURL; + + if (TabParent::sNextTabParent) { + MOZ_ASSERT(TabParent::sNextTabParent == mNewTab); + TabParent::sNextTabParent = nullptr; + *mWindowIsNew = false; + } + } + +private: + TabParent* mNewTab; + bool* mWindowIsNew; + nsCString* mURLToLoad; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_tabs_TabParent_h diff --git a/dom/ipc/extensions.js b/dom/ipc/extensions.js new file mode 100644 index 000000000..f6549bb3d --- /dev/null +++ b/dom/ipc/extensions.js @@ -0,0 +1,17 @@ +/* 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/. */ + +"use strict"; + +function debug(msg) { + // dump("extensions - " + msg + "\n"); +} + +debug("loaded"); + +ExtensionContent.init(this); + +addEventListener("unload", () => { + ExtensionContent.uninit(this); +}); diff --git a/dom/ipc/jar.mn b/dom/ipc/jar.mn new file mode 100644 index 000000000..86920f074 --- /dev/null +++ b/dom/ipc/jar.mn @@ -0,0 +1,13 @@ +# 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/. + +toolkit.jar: + content/global/test-ipc.xul (test.xul) + content/global/remote-test-ipc.js (remote-test.js) + content/global/BrowserElementChild.js (../browser-element/BrowserElementChild.js) + content/global/BrowserElementChildPreload.js (../browser-element/BrowserElementChildPreload.js) + content/global/BrowserElementCopyPaste.js (../browser-element/BrowserElementCopyPaste.js) + content/global/extensions.js (extensions.js) + content/global/manifestMessages.js (manifestMessages.js) + content/global/preload.js (preload.js) diff --git a/dom/ipc/manifestMessages.js b/dom/ipc/manifestMessages.js new file mode 100644 index 000000000..c6bb40494 --- /dev/null +++ b/dom/ipc/manifestMessages.js @@ -0,0 +1,108 @@ +/* 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/.*/ +/* + * Manifest obtainer frame script implementation of: + * http://www.w3.org/TR/appmanifest/#obtaining + * + * It searches a top-level browsing context for + * a <link rel=manifest> element. Then fetches + * and processes the linked manifest. + * + * BUG: https://bugzilla.mozilla.org/show_bug.cgi?id=1083410 + */ +/*globals Task, ManifestObtainer, ManifestFinder, content, sendAsyncMessage, addMessageListener, Components*/ +"use strict"; +const { + utils: Cu, +} = Components; +Cu.import("resource://gre/modules/ManifestObtainer.jsm"); +Cu.import("resource://gre/modules/ManifestFinder.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); + +const MessageHandler = { + registerListeners() { + addMessageListener( + "DOM:WebManifest:hasManifestLink", + this.hasManifestLink.bind(this) + ); + addMessageListener( + "DOM:ManifestObtainer:Obtain", + this.obtainManifest.bind(this) + ); + addMessageListener( + "DOM:Manifest:FireAppInstalledEvent", + this.fireAppInstalledEvent.bind(this) + ); + }, + + /** + * Check if the content document includes a link to a web manifest. + * @param {Object} aMsg The IPC message, which is destructured to just + * get the id. + */ + hasManifestLink({data: {id}}) { + const response = makeMsgResponse(id); + response.result = ManifestFinder.contentHasManifestLink(content); + response.success = true; + sendAsyncMessage("DOM:WebManifest:hasManifestLink", response); + }, + + /** + * Asynchronously obtains a web manifest from content by using the + * ManifestObtainer and messages back the result. + * @param {Object} aMsg The IPC message, which is destructured to just + * get the id. + */ + obtainManifest: Task.async(function* ({data: {id}}) { + const response = makeMsgResponse(id); + try { + response.result = yield ManifestObtainer.contentObtainManifest(content); + response.success = true; + } catch (err) { + response.result = serializeError(err); + } + sendAsyncMessage("DOM:ManifestObtainer:Obtain", response); + }), + + fireAppInstalledEvent({data: {id}}){ + const ev = new Event("appinstalled"); + const response = makeMsgResponse(id); + if (!content || content.top !== content) { + const msg = "Can only dispatch install event on top-level browsing contexts."; + response.result = serializeError(new Error(msg)); + } else { + response.success = true; + content.dispatchEvent(ev); + } + sendAsyncMessage("DOM:Manifest:FireAppInstalledEvent", response); + } +}; +/** + * Utility function to Serializes an JS Error, so it can be transferred over + * the message channel. + * FIX ME: https://bugzilla.mozilla.org/show_bug.cgi?id=1172586 + * @param {Error} aError The error to serialize. + * @return {Object} The serialized object. + */ +function serializeError(aError) { + const clone = { + "fileName": aError.fileName, + "lineNumber": aError.lineNumber, + "columnNumber": aError.columnNumber, + "stack": aError.stack, + "message": aError.message, + "name": aError.name + }; + return clone; +} + +function makeMsgResponse(aId) { + return { + id: aId, + success: false, + result: undefined + }; + } + +MessageHandler.registerListeners(); diff --git a/dom/ipc/moz.build b/dom/ipc/moz.build new file mode 100644 index 000000000..153bd3aae --- /dev/null +++ b/dom/ipc/moz.build @@ -0,0 +1,179 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +XPIDL_SOURCES += [ + 'nsIHangReport.idl', +] + +XPIDL_MODULE = 'dom' + +EXPORTS.mozilla.dom.ipc += [ + 'BlobChild.h', + 'BlobParent.h', + 'IdType.h', + 'nsIRemoteBlob.h', + 'StructuredCloneData.h', +] + +EXPORTS.mozilla.dom += [ + 'ContentBridgeChild.h', + 'ContentBridgeParent.h', + 'ContentChild.h', + 'ContentParent.h', + 'ContentProcess.h', + 'ContentProcessManager.h', + 'CPOWManagerGetter.h', + 'CrashReporterChild.h', + 'CrashReporterParent.h', + 'FilePickerParent.h', + 'nsIContentChild.h', + 'nsIContentParent.h', + 'PermissionMessageUtils.h', + 'TabChild.h', + 'TabContext.h', + 'TabMessageUtils.h', + 'TabParent.h', +] + +EXPORTS.mozilla += [ + 'AppProcessChecker.h', + 'PreallocatedProcessManager.h', + 'ProcessHangMonitor.h', + 'ProcessHangMonitorIPC.h', + 'ProcessPriorityManager.h', +] + +UNIFIED_SOURCES += [ + 'AppProcessChecker.cpp', + 'ColorPickerParent.cpp', + 'ContentBridgeChild.cpp', + 'ContentBridgeParent.cpp', + 'ContentParent.cpp', + 'ContentProcess.cpp', + 'ContentProcessManager.cpp', + 'CrashReporterParent.cpp', + 'DatePickerParent.cpp', + 'FilePickerParent.cpp', + 'nsIContentChild.cpp', + 'nsIContentParent.cpp', + 'PermissionMessageUtils.cpp', + 'PreallocatedProcessManager.cpp', + 'ProcessPriorityManager.cpp', + 'ScreenManagerParent.cpp', + 'StructuredCloneData.cpp', + 'TabChild.cpp', + 'TabContext.cpp', + 'TabMessageUtils.cpp', + 'TabParent.cpp', +] + +# Blob.cpp cannot be compiled in unified mode because it triggers a fatal gcc warning. +# CrashReporterChild.cpp cannot be compiled in unified mode because of name clashes +# in OS X headers. +# ContentChild.cpp cannot be compiled in unified mode on linux due to Time conflict +SOURCES += [ + 'Blob.cpp', + 'ContentChild.cpp', + 'CrashReporterChild.cpp', + 'ProcessHangMonitor.cpp', +] + +IPDL_SOURCES += [ + 'BlobTypes.ipdlh', + 'DOMTypes.ipdlh', + 'PBlob.ipdl', + 'PBlobStream.ipdl', + 'PBrowser.ipdl', + 'PBrowserOrId.ipdlh', + 'PColorPicker.ipdl', + 'PContent.ipdl', + 'PContentBridge.ipdl', + 'PContentPermission.ipdlh', + 'PContentPermissionRequest.ipdl', + 'PCrashReporter.ipdl', + 'PCycleCollectWithLogs.ipdl', + 'PDatePicker.ipdl', + 'PDocumentRenderer.ipdl', + 'PFilePicker.ipdl', + 'PMemoryReportRequest.ipdl', + 'PPluginWidget.ipdl', + 'PProcessHangMonitor.ipdl', + 'PScreenManager.ipdl', + 'PTabContext.ipdlh', + 'ServiceWorkerConfiguration.ipdlh', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' + +if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_TARGET'] == 'Darwin': + USE_LIBS += [ + 'mozsandbox', + ] + +if CONFIG['MOZ_CONTENT_SANDBOX'] and CONFIG['OS_ARCH'] == 'Linux': + USE_LIBS += [ + 'mozsandbox', + ] + +LOCAL_INCLUDES += [ + '/caps', + '/chrome', + '/docshell/base', + '/dom/base', + '/dom/events', + '/dom/filesystem', + '/dom/geolocation', + '/dom/media/webspeech/synth/ipc', + '/dom/security', + '/dom/storage', + '/dom/workers', + '/embedding/components/printingui/ipc', + '/extensions/cookie', + '/extensions/spellcheck/src', + '/gfx/2d', + '/hal/sandbox', + '/layout/base', + '/media/webrtc', + '/netwerk/base', + '/toolkit/crashreporter', + '/toolkit/xre', + '/uriloader/exthandler', + '/widget', + '/xpcom/base', + '/xpcom/threads', +] + +if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_ARCH'] == 'WINNT': + LOCAL_INCLUDES += [ + '/security/sandbox/chromium', + '/security/sandbox/chromium-shim', + ] + +if CONFIG['OS_ARCH'] != 'WINNT': + LOCAL_INCLUDES += [ + '/modules/libjar', + ] + +DEFINES['BIN_SUFFIX'] = '"%s"' % CONFIG['BIN_SUFFIX'] + +if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gonk'): + DEFINES['MOZ_ENABLE_FREETYPE'] = True + +if CONFIG['MOZ_TOOLKIT_SEARCH']: + DEFINES['MOZ_TOOLKIT_SEARCH'] = True + +JAR_MANIFESTS += ['jar.mn'] + +BROWSER_CHROME_MANIFESTS += ['tests/browser.ini'] +MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini'] +MOCHITEST_MANIFESTS += ['tests/mochitest.ini'] + +CXXFLAGS += CONFIG['TK_CFLAGS'] + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wno-error=shadow'] diff --git a/dom/ipc/nsIContentChild.cpp b/dom/ipc/nsIContentChild.cpp new file mode 100644 index 000000000..c5f748fe1 --- /dev/null +++ b/dom/ipc/nsIContentChild.cpp @@ -0,0 +1,159 @@ +/* -*- 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 "nsIContentChild.h" + +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/DOMTypes.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/PermissionMessageUtils.h" +#include "mozilla/dom/TabChild.h" +#include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/dom/ipc/StructuredCloneData.h" +#include "mozilla/ipc/FileDescriptorSetChild.h" +#include "mozilla/ipc/InputStreamUtils.h" +#include "mozilla/ipc/SendStream.h" + +#include "nsPrintfCString.h" +#include "xpcpublic.h" + +using namespace mozilla::ipc; +using namespace mozilla::jsipc; + +namespace mozilla { +namespace dom { + +PJavaScriptChild* +nsIContentChild::AllocPJavaScriptChild() +{ + return NewJavaScriptChild(); +} + +bool +nsIContentChild::DeallocPJavaScriptChild(PJavaScriptChild* aChild) +{ + ReleaseJavaScriptChild(aChild); + return true; +} + +PBrowserChild* +nsIContentChild::AllocPBrowserChild(const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) +{ + // We'll happily accept any kind of IPCTabContext here; we don't need to + // check that it's of a certain type for security purposes, because we + // believe whatever the parent process tells us. + + MaybeInvalidTabContext tc(aContext); + if (!tc.IsValid()) { + NS_ERROR(nsPrintfCString("Received an invalid TabContext from " + "the parent process. (%s) Crashing...", + tc.GetInvalidReason()).get()); + MOZ_CRASH("Invalid TabContext received from the parent process."); + } + + RefPtr<TabChild> child = + TabChild::Create(this, aTabId, tc.GetTabContext(), aChromeFlags); + + // The ref here is released in DeallocPBrowserChild. + return child.forget().take(); +} + +bool +nsIContentChild::DeallocPBrowserChild(PBrowserChild* aIframe) +{ + TabChild* child = static_cast<TabChild*>(aIframe); + NS_RELEASE(child); + return true; +} + +PBlobChild* +nsIContentChild::AllocPBlobChild(const BlobConstructorParams& aParams) +{ + return BlobChild::Create(this, aParams); +} + +bool +nsIContentChild::DeallocPBlobChild(PBlobChild* aActor) +{ + BlobChild::Destroy(aActor); + return true; +} + +BlobChild* +nsIContentChild::GetOrCreateActorForBlob(Blob* aBlob) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aBlob); + + RefPtr<BlobImpl> blobImpl = aBlob->Impl(); + MOZ_ASSERT(blobImpl); + + return GetOrCreateActorForBlobImpl(blobImpl); +} + +BlobChild* +nsIContentChild::GetOrCreateActorForBlobImpl(BlobImpl* aImpl) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aImpl); + + BlobChild* actor = BlobChild::GetOrCreate(this, aImpl); + NS_ENSURE_TRUE(actor, nullptr); + + return actor; +} + +PSendStreamChild* +nsIContentChild::AllocPSendStreamChild() +{ + MOZ_CRASH("PSendStreamChild actors should be manually constructed!"); +} + +bool +nsIContentChild::DeallocPSendStreamChild(PSendStreamChild* aActor) +{ + delete aActor; + return true; +} + +PFileDescriptorSetChild* +nsIContentChild::AllocPFileDescriptorSetChild(const FileDescriptor& aFD) +{ + return new FileDescriptorSetChild(aFD); +} + +bool +nsIContentChild::DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor) +{ + delete static_cast<FileDescriptorSetChild*>(aActor); + return true; +} + +bool +nsIContentChild::RecvAsyncMessage(const nsString& aMsg, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData) +{ + RefPtr<nsFrameMessageManager> cpm = nsFrameMessageManager::GetChildProcessManager(); + if (cpm) { + ipc::StructuredCloneData data; + ipc::UnpackClonedMessageDataForChild(aData, data); + + CrossProcessCpowHolder cpows(this, aCpows); + cpm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(cpm.get()), nullptr, + aMsg, false, &data, &cpows, aPrincipal, nullptr); + } + return true; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/nsIContentChild.h b/dom/ipc/nsIContentChild.h new file mode 100644 index 000000000..211144e9a --- /dev/null +++ b/dom/ipc/nsIContentChild.h @@ -0,0 +1,118 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_nsIContentChild_h +#define mozilla_dom_nsIContentChild_h + +#include "mozilla/dom/ipc/IdType.h" + +#include "nsISupports.h" +#include "nsTArrayForwardDeclare.h" +#include "mozilla/dom/CPOWManagerGetter.h" +#include "mozilla/ipc/Shmem.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" + +#define NS_ICONTENTCHILD_IID \ + { 0x4eed2e73, 0x94ba, 0x48a8, \ + { 0xa2, 0xd1, 0xa5, 0xed, 0x86, 0xd7, 0xbb, 0xe4 } } + +class nsString; + +namespace IPC { +class Principal; +} // namespace IPC + +namespace mozilla { +namespace ipc { +class FileDescriptor; +class PFileDescriptorSetChild; +class PSendStreamChild; +class Shmem; +} // namespace ipc + +namespace jsipc { +class PJavaScriptChild; +class CpowEntry; +} // namespace jsipc + +namespace dom { + +class Blob; +class BlobChild; +class BlobImpl; +class BlobConstructorParams; +class ClonedMessageData; +class IPCTabContext; +class PBlobChild; +class PBrowserChild; + +class nsIContentChild : public nsISupports + , public CPOWManagerGetter + , public mozilla::ipc::IShmemAllocator +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICONTENTCHILD_IID) + + BlobChild* GetOrCreateActorForBlob(Blob* aBlob); + BlobChild* GetOrCreateActorForBlobImpl(BlobImpl* aImpl); + + virtual PBlobChild* + SendPBlobConstructor(PBlobChild* aActor, + const BlobConstructorParams& aParams) = 0; + + virtual bool + SendPBrowserConstructor(PBrowserChild* aActor, + const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpID, + const bool& aIsForApp, + const bool& aIsForBrowser) = 0; + + virtual mozilla::ipc::PFileDescriptorSetChild* + SendPFileDescriptorSetConstructor(const mozilla::ipc::FileDescriptor&) = 0; + + virtual mozilla::ipc::PSendStreamChild* + SendPSendStreamConstructor(mozilla::ipc::PSendStreamChild*) = 0; + +protected: + virtual jsipc::PJavaScriptChild* AllocPJavaScriptChild(); + virtual bool DeallocPJavaScriptChild(jsipc::PJavaScriptChild*); + + virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpId, + const bool& aIsForApp, + const bool& aIsForBrowser); + virtual bool DeallocPBrowserChild(PBrowserChild*); + + virtual PBlobChild* AllocPBlobChild(const BlobConstructorParams& aParams); + + virtual bool DeallocPBlobChild(PBlobChild* aActor); + + virtual mozilla::ipc::PSendStreamChild* AllocPSendStreamChild(); + + virtual bool DeallocPSendStreamChild(mozilla::ipc::PSendStreamChild* aActor); + + virtual mozilla::ipc::PFileDescriptorSetChild* + AllocPFileDescriptorSetChild(const mozilla::ipc::FileDescriptor& aFD); + + virtual bool + DeallocPFileDescriptorSetChild(mozilla::ipc::PFileDescriptorSetChild* aActor); + + virtual bool RecvAsyncMessage(const nsString& aMsg, + InfallibleTArray<jsipc::CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData); +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIContentChild, NS_ICONTENTCHILD_IID) + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_nsIContentChild_h */ diff --git a/dom/ipc/nsIContentParent.cpp b/dom/ipc/nsIContentParent.cpp new file mode 100644 index 000000000..0ef1abdf7 --- /dev/null +++ b/dom/ipc/nsIContentParent.cpp @@ -0,0 +1,328 @@ +/* -*- 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 "nsIContentParent.h" + +#include "mozilla/AppProcessChecker.h" +#include "mozilla/Preferences.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/ContentBridgeParent.h" +#include "mozilla/dom/PTabContext.h" +#include "mozilla/dom/PermissionMessageUtils.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/dom/ipc/BlobParent.h" +#include "mozilla/dom/ipc/StructuredCloneData.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" +#include "mozilla/ipc/FileDescriptorSetParent.h" +#include "mozilla/ipc/PFileDescriptorSetParent.h" +#include "mozilla/ipc/SendStreamAlloc.h" +#include "mozilla/Unused.h" + +#include "nsFrameMessageManager.h" +#include "nsIWebBrowserChrome.h" +#include "nsPrintfCString.h" +#include "xpcpublic.h" + +using namespace mozilla::jsipc; + +// XXX need another bug to move this to a common header. +#ifdef DISABLE_ASSERTS_FOR_FUZZING +#define ASSERT_UNLESS_FUZZING(...) do { } while (0) +#else +#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__) +#endif + +namespace mozilla { +namespace dom { + +nsIContentParent::nsIContentParent() +{ + mMessageManager = nsFrameMessageManager::NewProcessMessageManager(true); +} + +ContentParent* +nsIContentParent::AsContentParent() +{ + MOZ_ASSERT(IsContentParent()); + return static_cast<ContentParent*>(this); +} + +ContentBridgeParent* +nsIContentParent::AsContentBridgeParent() +{ + MOZ_ASSERT(IsContentBridgeParent()); + return static_cast<ContentBridgeParent*>(this); +} + +PJavaScriptParent* +nsIContentParent::AllocPJavaScriptParent() +{ + return NewJavaScriptParent(); +} + +bool +nsIContentParent::DeallocPJavaScriptParent(PJavaScriptParent* aParent) +{ + ReleaseJavaScriptParent(aParent); + return true; +} + +bool +nsIContentParent::CanOpenBrowser(const IPCTabContext& aContext) +{ + // (PopupIPCTabContext lets the child process prove that it has access to + // the app it's trying to open.) + // On e10s we also allow UnsafeTabContext to allow service workers to open + // windows. This is enforced in MaybeInvalidTabContext. + if (aContext.type() != IPCTabContext::TPopupIPCTabContext && + aContext.type() != IPCTabContext::TUnsafeIPCTabContext) { + ASSERT_UNLESS_FUZZING("Unexpected IPCTabContext type. Aborting AllocPBrowserParent."); + return false; + } + + if (aContext.type() == IPCTabContext::TPopupIPCTabContext) { + const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext(); + if (popupContext.opener().type() != PBrowserOrId::TPBrowserParent) { + ASSERT_UNLESS_FUZZING("Unexpected PopupIPCTabContext type. Aborting AllocPBrowserParent."); + return false; + } + + auto opener = TabParent::GetFrom(popupContext.opener().get_PBrowserParent()); + if (!opener) { + ASSERT_UNLESS_FUZZING("Got null opener from child; aborting AllocPBrowserParent."); + return false; + } + + // Popup windows of isMozBrowserElement frames must be isMozBrowserElement if + // the parent isMozBrowserElement. Allocating a !isMozBrowserElement frame with + // same app ID would allow the content to access data it's not supposed to. + if (!popupContext.isMozBrowserElement() && opener->IsMozBrowserElement()) { + ASSERT_UNLESS_FUZZING("Child trying to escalate privileges! Aborting AllocPBrowserParent."); + return false; + } + } + + MaybeInvalidTabContext tc(aContext); + if (!tc.IsValid()) { + NS_ERROR(nsPrintfCString("Child passed us an invalid TabContext. (%s) " + "Aborting AllocPBrowserParent.", + tc.GetInvalidReason()).get()); + return false; + } + + return true; +} + +PBrowserParent* +nsIContentParent::AllocPBrowserParent(const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpId, + const bool& aIsForApp, + const bool& aIsForBrowser) +{ + Unused << aCpId; + Unused << aIsForApp; + Unused << aIsForBrowser; + + if (!CanOpenBrowser(aContext)) { + return nullptr; + } + + uint32_t chromeFlags = aChromeFlags; + if (aContext.type() == IPCTabContext::TPopupIPCTabContext) { + // CanOpenBrowser has ensured that the IPCTabContext is of + // type PopupIPCTabContext, and that the opener TabParent is + // reachable. + const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext(); + auto opener = TabParent::GetFrom(popupContext.opener().get_PBrowserParent()); + // We must ensure that the private browsing and remoteness flags + // match those of the opener. + nsCOMPtr<nsILoadContext> loadContext = opener->GetLoadContext(); + if (!loadContext) { + return nullptr; + } + + bool isPrivate; + loadContext->GetUsePrivateBrowsing(&isPrivate); + if (isPrivate) { + chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW; + } + } + + // And because we're allocating a remote browser, of course the + // window is remote. + chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW; + + MaybeInvalidTabContext tc(aContext); + MOZ_ASSERT(tc.IsValid()); + TabParent* parent = new TabParent(this, aTabId, tc.GetTabContext(), chromeFlags); + + // We release this ref in DeallocPBrowserParent() + NS_ADDREF(parent); + return parent; +} + +bool +nsIContentParent::DeallocPBrowserParent(PBrowserParent* aFrame) +{ + TabParent* parent = TabParent::GetFrom(aFrame); + NS_RELEASE(parent); + return true; +} + +PBlobParent* +nsIContentParent::AllocPBlobParent(const BlobConstructorParams& aParams) +{ + return BlobParent::Create(this, aParams); +} + +bool +nsIContentParent::DeallocPBlobParent(PBlobParent* aActor) +{ + BlobParent::Destroy(aActor); + return true; +} + +BlobParent* +nsIContentParent::GetOrCreateActorForBlob(Blob* aBlob) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aBlob); + + RefPtr<BlobImpl> blobImpl = aBlob->Impl(); + MOZ_ASSERT(blobImpl); + + return GetOrCreateActorForBlobImpl(blobImpl); +} + +BlobParent* +nsIContentParent::GetOrCreateActorForBlobImpl(BlobImpl* aImpl) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aImpl); + + BlobParent* actor = BlobParent::GetOrCreate(this, aImpl); + NS_ENSURE_TRUE(actor, nullptr); + + return actor; +} + +bool +nsIContentParent::RecvSyncMessage(const nsString& aMsg, + const ClonedMessageData& aData, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + nsTArray<ipc::StructuredCloneData>* aRetvals) +{ + // FIXME Permission check in Content process + nsIPrincipal* principal = aPrincipal; + if (IsContentParent()) { + ContentParent* parent = AsContentParent(); + if (!ContentParent::IgnoreIPCPrincipal() && + parent && principal && !AssertAppPrincipal(parent, principal)) { + return false; + } + } + + RefPtr<nsFrameMessageManager> ppm = mMessageManager; + if (ppm) { + ipc::StructuredCloneData data; + ipc::UnpackClonedMessageDataForParent(aData, data); + + CrossProcessCpowHolder cpows(this, aCpows); + ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), nullptr, + aMsg, true, &data, &cpows, aPrincipal, aRetvals); + } + return true; +} + +bool +nsIContentParent::RecvRpcMessage(const nsString& aMsg, + const ClonedMessageData& aData, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + nsTArray<ipc::StructuredCloneData>* aRetvals) +{ + // FIXME Permission check in Content process + nsIPrincipal* principal = aPrincipal; + if (IsContentParent()) { + ContentParent* parent = AsContentParent(); + if (!ContentParent::IgnoreIPCPrincipal() && + parent && principal && !AssertAppPrincipal(parent, principal)) { + return false; + } + } + + RefPtr<nsFrameMessageManager> ppm = mMessageManager; + if (ppm) { + ipc::StructuredCloneData data; + ipc::UnpackClonedMessageDataForParent(aData, data); + + CrossProcessCpowHolder cpows(this, aCpows); + ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), nullptr, + aMsg, true, &data, &cpows, aPrincipal, aRetvals); + } + return true; +} + +PFileDescriptorSetParent* +nsIContentParent::AllocPFileDescriptorSetParent(const FileDescriptor& aFD) +{ + return new FileDescriptorSetParent(aFD); +} + +bool +nsIContentParent::DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor) +{ + delete static_cast<FileDescriptorSetParent*>(aActor); + return true; +} + +PSendStreamParent* +nsIContentParent::AllocPSendStreamParent() +{ + return mozilla::ipc::AllocPSendStreamParent(); +} + +bool +nsIContentParent::DeallocPSendStreamParent(PSendStreamParent* aActor) +{ + delete aActor; + return true; +} + +bool +nsIContentParent::RecvAsyncMessage(const nsString& aMsg, + InfallibleTArray<CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData) +{ + // FIXME Permission check in Content process + nsIPrincipal* principal = aPrincipal; + if (IsContentParent()) { + ContentParent* parent = AsContentParent(); + if (!ContentParent::IgnoreIPCPrincipal() && + parent && principal && !AssertAppPrincipal(parent, principal)) { + return false; + } + } + + RefPtr<nsFrameMessageManager> ppm = mMessageManager; + if (ppm) { + ipc::StructuredCloneData data; + ipc::UnpackClonedMessageDataForParent(aData, data); + + CrossProcessCpowHolder cpows(this, aCpows); + ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), nullptr, + aMsg, false, &data, &cpows, aPrincipal, nullptr); + } + return true; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/ipc/nsIContentParent.h b/dom/ipc/nsIContentParent.h new file mode 100644 index 000000000..7ba015264 --- /dev/null +++ b/dom/ipc/nsIContentParent.h @@ -0,0 +1,145 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_nsIContentParent_h +#define mozilla_dom_nsIContentParent_h + +#include "mozilla/Attributes.h" +#include "mozilla/dom/ipc/IdType.h" +#include "mozilla/ipc/ProtocolUtils.h" + +#include "nsFrameMessageManager.h" +#include "nsISupports.h" +#include "mozilla/dom/CPOWManagerGetter.h" + +#define NS_ICONTENTPARENT_IID \ + { 0xeeec9ebf, 0x8ecf, 0x4e38, \ + { 0x81, 0xda, 0xb7, 0x34, 0x13, 0x7e, 0xac, 0xf3 } } + +namespace IPC { +class Principal; +} // namespace IPC + +namespace mozilla { + +namespace jsipc { +class PJavaScriptParent; +class CpowEntry; +} // namespace jsipc + +namespace ipc { +class PFileDescriptorSetParent; +class PSendStreamParent; +} + +namespace dom { + +class Blob; +class BlobConstructorParams; +class BlobImpl; +class BlobParent; +class ContentParent; +class ContentBridgeParent; +class IPCTabContext; +class PBlobParent; +class PBrowserParent; + +class nsIContentParent : public nsISupports + , public mozilla::dom::ipc::MessageManagerCallback + , public CPOWManagerGetter + , public mozilla::ipc::IShmemAllocator +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICONTENTPARENT_IID) + + nsIContentParent(); + + BlobParent* GetOrCreateActorForBlob(Blob* aBlob); + BlobParent* GetOrCreateActorForBlobImpl(BlobImpl* aImpl); + + virtual ContentParentId ChildID() const = 0; + virtual bool IsForApp() const = 0; + virtual bool IsForBrowser() const = 0; + + MOZ_MUST_USE virtual PBlobParent* + SendPBlobConstructor(PBlobParent* aActor, + const BlobConstructorParams& aParams) = 0; + + MOZ_MUST_USE virtual PBrowserParent* + SendPBrowserConstructor(PBrowserParent* actor, + const TabId& aTabId, + const IPCTabContext& context, + const uint32_t& chromeFlags, + const ContentParentId& aCpId, + const bool& aIsForApp, + const bool& aIsForBrowser) = 0; + + virtual bool IsContentParent() const { return false; } + + ContentParent* AsContentParent(); + + virtual bool IsContentBridgeParent() const { return false; } + + ContentBridgeParent* AsContentBridgeParent(); + + nsFrameMessageManager* GetMessageManager() const { return mMessageManager; } + + virtual int32_t Pid() const = 0; + +protected: // methods + bool CanOpenBrowser(const IPCTabContext& aContext); + +protected: // IPDL methods + virtual mozilla::jsipc::PJavaScriptParent* AllocPJavaScriptParent(); + virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*); + + virtual PBrowserParent* AllocPBrowserParent(const TabId& aTabId, + const IPCTabContext& aContext, + const uint32_t& aChromeFlags, + const ContentParentId& aCpId, + const bool& aIsForApp, + const bool& aIsForBrowser); + virtual bool DeallocPBrowserParent(PBrowserParent* frame); + + virtual PBlobParent* AllocPBlobParent(const BlobConstructorParams& aParams); + + virtual bool DeallocPBlobParent(PBlobParent* aActor); + + virtual mozilla::ipc::PFileDescriptorSetParent* + AllocPFileDescriptorSetParent(const mozilla::ipc::FileDescriptor& aFD); + + virtual bool + DeallocPFileDescriptorSetParent(mozilla::ipc::PFileDescriptorSetParent* aActor); + + virtual mozilla::ipc::PSendStreamParent* AllocPSendStreamParent(); + + virtual bool DeallocPSendStreamParent(mozilla::ipc::PSendStreamParent* aActor); + + virtual bool RecvSyncMessage(const nsString& aMsg, + const ClonedMessageData& aData, + InfallibleTArray<jsipc::CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + nsTArray<ipc::StructuredCloneData>* aRetvals); + virtual bool RecvRpcMessage(const nsString& aMsg, + const ClonedMessageData& aData, + InfallibleTArray<jsipc::CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + nsTArray<ipc::StructuredCloneData>* aRetvals); + virtual bool RecvAsyncMessage(const nsString& aMsg, + InfallibleTArray<jsipc::CpowEntry>&& aCpows, + const IPC::Principal& aPrincipal, + const ClonedMessageData& aData); + +protected: // members + RefPtr<nsFrameMessageManager> mMessageManager; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIContentParent, NS_ICONTENTPARENT_IID) + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_nsIContentParent_h */ diff --git a/dom/ipc/nsIHangReport.idl b/dom/ipc/nsIHangReport.idl new file mode 100644 index 000000000..9ccbbbbad --- /dev/null +++ b/dom/ipc/nsIHangReport.idl @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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 "nsISupports.idl" + +interface nsIDOMElement; +interface nsIFrameLoader; + +/** + * When a content process hangs, Gecko notifies "process-hang-report" observers + * and passes an nsIHangReport for the subject parameter. There is at most one + * nsIHangReport associated with a given content process. As long as the content + * process stays stuck, the "process-hang-report" observer will continue to be + * notified at regular intervals (approximately once per second). The content + * process will continue to run uninhibitedly during this time. + */ + +[scriptable, uuid(5fcffbb9-be62-49b1-b8a1-36e820787a74)] +interface nsIHangReport : nsISupports +{ + const unsigned long SLOW_SCRIPT = 1; + const unsigned long PLUGIN_HANG = 2; + + // The type of hang being reported: SLOW_SCRIPT or PLUGIN_HANG. + readonly attribute unsigned long hangType; + + // For SLOW_SCRIPT reports, these fields contain information about the + // slow script. + // Only valid for SLOW_SCRIPT reports. + readonly attribute nsIDOMElement scriptBrowser; + readonly attribute ACString scriptFileName; + readonly attribute unsigned long scriptLineNo; + + // For PLUGIN_HANGs, this field contains information about the plugin. + // Only valid for PLUGIN_HANG reports. + readonly attribute ACString pluginName; + + // Called by front end code when user ignores or cancels + // the notification. + void userCanceled(); + + // Terminate the slow script if it is still running. + // Only valid for SLOW_SCRIPT reports. + void terminateScript(); + + // Terminate the plugin if it is still hung. + // Only valid for PLUGIN_HANG reports. + void terminatePlugin(); + + // Ask the content process to start up the slow script debugger. + // Only valid for SLOW_SCRIPT reports. + void beginStartingDebugger(); + + // Inform the content process that the slow script debugger has finished + // spinning up. The content process will run a nested event loop until this + // method is called. + // Only valid for SLOW_SCRIPT reports. + void endStartingDebugger(); + + // Inquire whether the report is for a content process loaded by the given + // frameloader. + bool isReportForBrowser(in nsIFrameLoader aFrameLoader); +}; diff --git a/dom/ipc/nsIRemoteBlob.h b/dom/ipc/nsIRemoteBlob.h new file mode 100644 index 000000000..55ff0352a --- /dev/null +++ b/dom/ipc/nsIRemoteBlob.h @@ -0,0 +1,45 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_ipc_nsIRemoteBlob_h +#define mozilla_dom_ipc_nsIRemoteBlob_h + +#include "nsISupports.h" + +#ifndef NS_NO_VTABLE +#define NS_NO_VTABLE +#endif + +#define NS_IREMOTEBLOB_IID \ + {0x0b8b0091, 0xb315, 0x48a2, {0x90, 0xf1, 0x60, 0x0e, 0x78, 0x35, 0xf7, 0x2d}} + +namespace mozilla { +namespace dom { + +class BlobChild; +class BlobParent; + +} // namespace dom +} // namespace mozilla + +class NS_NO_VTABLE nsIRemoteBlob : public nsISupports +{ +public: + typedef mozilla::dom::BlobChild BlobChild; + typedef mozilla::dom::BlobParent BlobParent; + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IREMOTEBLOB_IID) + + virtual BlobChild* + GetBlobChild() = 0; + + virtual BlobParent* + GetBlobParent() = 0; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIRemoteBlob, NS_IREMOTEBLOB_IID) + +#endif // mozilla_dom_ipc_nsIRemoteBlob_h diff --git a/dom/ipc/preload.js b/dom/ipc/preload.js new file mode 100644 index 000000000..10fd15f4a --- /dev/null +++ b/dom/ipc/preload.js @@ -0,0 +1,130 @@ +/* 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/. */ + +// Preload some things, in an attempt to make app startup faster. +// +// This script is run when the preallocated process starts. It is injected as +// a frame script. + +var BrowserElementIsPreloaded = true; + +var DoPreloadPostfork = function(aCallback) { + Services.obs.addObserver({ + _callback: aCallback, + + observe: function() { + this._callback(); + Services.obs.removeObserver(this, "preload-postfork"); + } + }, "preload-postfork", false); +}; + +(function (global) { + "use strict"; + + let Cu = Components.utils; + let Cc = Components.classes; + let Ci = Components.interfaces; + + Cu.import("resource://gre/modules/AppsUtils.jsm"); + Cu.import("resource://gre/modules/BrowserElementPromptService.jsm"); + Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); + Cu.import("resource://gre/modules/FileUtils.jsm"); + Cu.import("resource://gre/modules/Geometry.jsm"); + Cu.import("resource://gre/modules/IndexedDBHelper.jsm"); + Cu.import("resource://gre/modules/NetUtil.jsm"); + Cu.import("resource://gre/modules/Services.jsm"); + Cu.import("resource://gre/modules/SettingsDB.jsm"); + Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + + Cc["@mozilla.org/appshell/appShellService;1"].getService(Ci["nsIAppShellService"]); + Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci["nsIWindowMediator"]); + Cc["@mozilla.org/base/telemetry;1"].getService(Ci["nsITelemetry"]); + Cc["@mozilla.org/categorymanager;1"].getService(Ci["nsICategoryManager"]); + Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci["nsIMessageSender"]); + Cc["@mozilla.org/consoleservice;1"].getService(Ci["nsIConsoleService"]); + Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci["nsIURIFixup"]); + Cc["@mozilla.org/dom/dom-request-service;1"].getService(Ci["nsIDOMRequestService"]); + Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci["nsIPromptService"]); + Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci["nsIWindowWatcher"]); + Cc["@mozilla.org/eventlistenerservice;1"].getService(Ci["nsIEventListenerService"]); + Cc["@mozilla.org/focus-manager;1"].getService(Ci["nsIFocusManager"]); + Cc["@mozilla.org/intl/nslocaleservice;1"].getService(Ci["nsILocaleService"]); + Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci["nsIStringBundleService"]); + Cc["@mozilla.org/layout/content-policy;1"].getService(Ci["nsIContentPolicy"]); + Cc["@mozilla.org/message-loop;1"].getService(Ci["nsIMessageLoop"]); + Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci["mozIJSSubScriptLoader"]); + Cc["@mozilla.org/network/application-cache-service;1"].getService(Ci["nsIApplicationCacheService"]); + Cc["@mozilla.org/network/dns-service;1"].getService(Ci["nsIDNSService"]); + Cc["@mozilla.org/network/effective-tld-service;1"].getService(Ci["nsIEffectiveTLDService"]); + Cc["@mozilla.org/network/idn-service;1"].getService(Ci["nsIIDNService"]); + Cc["@mozilla.org/network/io-service;1"].getService(Ci["nsIIOService2"]); + Cc["@mozilla.org/network/mime-hdrparam;1"].getService(Ci["nsIMIMEHeaderParam"]); + Cc["@mozilla.org/network/socket-transport-service;1"].getService(Ci["nsISocketTransportService"]); + Cc["@mozilla.org/network/stream-transport-service;1"].getService(Ci["nsIStreamTransportService"]); + Cc["@mozilla.org/network/url-parser;1?auth=maybe"].getService(Ci["nsIURLParser"]); + Cc["@mozilla.org/network/url-parser;1?auth=no"].getService(Ci["nsIURLParser"]); + Cc["@mozilla.org/network/url-parser;1?auth=yes"].getService(Ci["nsIURLParser"]); + Cc["@mozilla.org/observer-service;1"].getService(Ci["nsIObserverService"]); + Cc["@mozilla.org/preferences-service;1"].getService(Ci["nsIPrefBranch"]); + Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci["nsIScriptSecurityManager"]); + Cc["@mozilla.org/storage/service;1"].getService(Ci["mozIStorageService"]); + Cc["@mozilla.org/system-info;1"].getService(Ci["nsIPropertyBag2"]); + Cc["@mozilla.org/thread-manager;1"].getService(Ci["nsIThreadManager"]); + Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci["nsIAppStartup"]); + Cc["@mozilla.org/uriloader;1"].getService(Ci["nsIURILoader"]); + Cc["@mozilla.org/cspcontext;1"].createInstance(Ci["nsIContentSecurityPolicy"]); + Cc["@mozilla.org/settingsManager;1"].createInstance(Ci["nsISupports"]); + + /* Applications Specific Helper */ + try { + if (Services.prefs.getBoolPref("dom.sysmsg.enabled")) { + Cc["@mozilla.org/system-message-manager;1"].getService(Ci["nsIDOMNavigatorSystemMessages"]); + } + } catch(e) { + } + + try { + if (Services.prefs.getBoolPref("dom.mozInputMethod.enabled")) { + Services.scriptloader.loadSubScript("chrome://global/content/forms.js", global); + } + } catch (e) { + } + + Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementCopyPaste.js", global); + Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementChildPreload.js", global); + + Services.io.getProtocolHandler("app"); + Services.io.getProtocolHandler("default"); + + // Register an observer for topic "preload_postfork" after we fork a content + // process. + DoPreloadPostfork(function () { + // Load AppsServiceChild.jsm after fork since it sends an async message to + // the chrome process in its init() function. + Cu.import("resource://gre/modules/AppsServiceChild.jsm"); + + // Load nsIAppsService after fork since its implementation loads + // AppsServiceChild.jsm + Cc["@mozilla.org/AppsService;1"].getService(Ci["nsIAppsService"]); + + // Load nsICookieService after fork since it sends an IPC constructor + // message to the chrome process. + Cc["@mozilla.org/cookieService;1"].getService(Ci["nsICookieService"]); + + // Load nsIPermissionManager after fork since it sends a message to the + // chrome process to read permissions. + Cc["@mozilla.org/permissionmanager;1"].getService(Ci["nsIPermissionManager"]); + + // Load nsIProtocolProxyService after fork since it asynchronously accesses + // the "Proxy Resolution" thread after it's frozen. + Cc["@mozilla.org/network/protocol-proxy-service;1"].getService(Ci["nsIProtocolProxyService"]); + + // Call docShell.createAboutBlankContentViewer() after fork since it has IPC + // activity in the PCompositor protocol. + docShell.createAboutBlankContentViewer(null); + docShell.isActive = false; + }); +})(this); + diff --git a/dom/ipc/remote-test.js b/dom/ipc/remote-test.js new file mode 100644 index 000000000..dd21415cd --- /dev/null +++ b/dom/ipc/remote-test.js @@ -0,0 +1,54 @@ +/* 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/. */ + +dump("Loading remote script!\n"); +dump(content + "\n"); + +var cpm = Components.classes["@mozilla.org/childprocessmessagemanager;1"] + .getService(Components.interfaces.nsISyncMessageSender); +cpm.addMessageListener("cpm-async", + function(m) { + cpm.sendSyncMessage("ppm-sync"); + dump(content.document.documentElement); + cpm.sendAsyncMessage("ppm-async"); + }); + +var Cc = Components.classes; +var Ci = Components.interfaces; +var dshell = content.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Ci.nsIDocShell); + + +addEventListener("click", + function(e) { + dump(e.target + "\n"); + if (e.target instanceof Components.interfaces.nsIDOMHTMLAnchorElement && + dshell == docShell) { + var retval = docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor). + getInterface(Components.interfaces.nsIContentFrameMessageManager). + sendSyncMessage("linkclick", { href: e.target.href }); + dump(uneval(retval[0]) + "\n"); + // Test here also that both retvals are the same + sendAsyncMessage("linkclick-reply-object", uneval(retval[0]) == uneval(retval[1]) ? retval[0] : ""); + } + }, + true); + +addMessageListener("chrome-message", + function(m) { + dump(uneval(m.json) + "\n"); + sendAsyncMessage("chrome-message-reply", m.json); + }); + +addMessageListener("speed-test-start", + function(m) { + while (sendSyncMessage("speed-test")[0].message != "done"); + }); + +addMessageListener("async-echo", function(m) { + sendAsyncMessage(m.name); +}); diff --git a/dom/ipc/test.xul b/dom/ipc/test.xul new file mode 100644 index 000000000..13425779c --- /dev/null +++ b/dom/ipc/test.xul @@ -0,0 +1,274 @@ +<?xml version="1.0"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="800" height="800" orient="vertical" onload="initRemoteFrameScript();"> + <script> + + function dumpClientRect(r) { + dump(r.left + "," + r.top + "," + r.right + "," + + r.bottom + "," + r.width + "," + r.height + "\n"); + } + + function handleMozAfterPaint(e) { + return; + dump(e.type + "\n") + var rects = e.clientRects; + var i; + dump("\tclientRects:\n"); + for (i = 0; i < rects.length; ++i) { + var r = rects.item(i); + dump("\t\t"); + dumpClientRect(rects.item(i)); + } + + dump("\tboundingClientRect\n\t\t"); + dumpClientRect(e.boundingClientRect); + + var paintRequests = e.paintRequests; + dump("\tpaintRequests\n"); + for (i = 0; i < paintRequests.length; ++i) { + var pr = paintRequests.item(i); + dump("\t\t"); + dumpClientRect(pr.clientRect); + if (pr.reason) + dump("\t\t" + pr.reason + "\n"); + } + } + + function handleMozScrolledAreaChanged(e) { + return; + dump(e.type + "\n"); + dump("\t" + e.x + "," + e.y + "," + e.width + "," + e.height + "\n"); + } + + function restart() { + var y = document.getElementById('page'); + var p = y.parentNode; + p.removeChild(y); + p.appendChild(y); + + var fl = y.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader; + fl.activateFrameEvent("MozAfterPaint", true); + fl.activateFrameEvent("MozScrolledAreaChanged", true); + y.addEventListener("MozAfterPaint", handleMozAfterPaint, true); + y.addEventListener("MozScrolledAreaChanged", handleMozScrolledAreaChanged, true); + } + + function loadURL(url) { + document.getElementById('page').setAttribute('src', url); + } + + function randomClick() { + // First focus the remote frame, then dispatch click. This way remote frame gets focus before + // mouse event. + document.getElementById('page').focus(); + var frameLoader = document.getElementById('page').QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader; + var x = parseInt(Math.random() * 100); + var y = parseInt(Math.random() * 100); + frameLoader.sendCrossProcessMouseEvent("mousedown", x, y, 0, 1, 0, false); + frameLoader.sendCrossProcessMouseEvent("mouseup", x, y, 0, 1, 0, false); + } + + function keyPress() { + // First focus the remote frame, then dispatch click. This way remote frame gets focus before + // mouse event. + document.getElementById('page').focus(); + var frameLoader = document.getElementById('page').QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader; + + var keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_A; + frameLoader.sendCrossProcessKeyEvent("keydown", keyCode, 0, 0); + frameLoader.sendCrossProcessKeyEvent("keypress", keyCode, 0, 0); + frameLoader.sendCrossProcessKeyEvent("keyup", keyCode, 0, 0); + } + + function openWindow() { + window.open('chrome://global/content/test-ipc.xul', '_blank', 'chrome,resizable,width=800,height=800'); + } + + function closeWindow() { + window.close(); + } + + function initRemoteFrameScript() { + // 1. Test that loading a script works, and that accessing process level mm and + // global mm works. + var ppm = Components.classes["@mozilla.org/parentprocessmessagemanager;1"] + .getService(Components.interfaces.nsIMessageBroadcaster); + var gm = Components.classes["@mozilla.org/globalmessagemanager;1"] + .getService(Components.interfaces.nsIMessageBroadcaster); + var cpm = Components.classes["@mozilla.org/childprocessmessagemanager;1"] + .getService(Components.interfaces.nsISyncMessageSender); + + if (ppm.childCount != 2) { + alert("Should have two child processes!"); + } + var childprocessmm = ppm.getChildAt(1); // 0 is the in-process child process mm + + childprocessmm.addMessageListener("ppm-sync", + function(m) { + if (m.target != childprocessmm) alert("Wrong target!"); + document.getElementById("messageLog").value += "[SYNC1 PPM]"; + } + ); + + ppm.addMessageListener("ppm-sync", + function(m) { + // Check that global process message manager gets the per-process mm as target. + if (m.target != childprocessmm) alert("Wrong target!"); + document.getElementById("messageLog").value += "[SYNC2 PPM]"; + } + ); + childprocessmm.addMessageListener("ppm-async", + function(m) { + if (m.target != childprocessmm) alert("Wrong target!"); + document.getElementById("messageLog").value += "[ASYNC1 PPM]"; + } + ); + ppm.addMessageListener("ppm-async", + function(m) { + // Check that global process message manager gets the per-process mm as target. + if (m.target != childprocessmm) alert("Wrong target!"); + document.getElementById("messageLog").value += "[ASYNC2 PPM]"; + } + ); + messageManager.loadFrameScript("chrome://global/content/remote-test-ipc.js", true); + ppm.sendAsyncMessage("cpm-async"); + + // 2. Test that adding message listener works, and that receiving a sync message works. + messageManager.addMessageListener("linkclick", + function(m) { + // This checks that json sending works in sync messages. + document.getElementById("messageLog").value = m.name + ": " + m.json.href; + return { message: "linkclick-received" }; + }); + + // 3. Test that returning multiple json results works. + messageManager.addMessageListener("linkclick", + function(m) { + return { message: "linkclick-received" }; + }); + + // 4. Test that receiving an async message works. + // Test also that sending async message to content works. + // Test also that { receiveMessage: function(m) {} } works. + messageManager.addMessageListener("linkclick-reply-object", + { foobarObjectVar: true, + receiveMessage: function(m) { + var s = (m.json.message == "linkclick-received") && + (this.foobarObjectVar) ? "PASS" : "FAIL"; + messageManager.broadcastAsyncMessage("chrome-message", { ok : s } ); + } + } + ); + + // 5. Final test to check that everything went ok. + messageManager.addMessageListener("chrome-message-reply", + function(m) { + // Check that 'this' and .target values are handled correctly + if (m.target == document.getElementById("page") && + this == messageManager) { + // Check that the message properties are enumerable. + var hasName = false; + var hasSync = false; + var hasJSON = false; + for (i in m) { + if (i == "name") { + hasName = true; + } else if (i == "sync") { + hasSync = true; + } else if (i == "json") { + hasJSON = true; + } + } + if (hasName && hasSync && hasJSON) { + document.getElementById("messageLog").value += ", " + m.json.ok; + } + } + }); + } + + var speedTestStartTime = 0; + var speedTestCount = 0; + function messageSpeed() { + speedTestCount = 0; + messageManager.addMessageListener("speed-test", speedHandler); + messageManager.broadcastAsyncMessage("speed-test-start"); + } + + function speedHandler() { + if (!speedTestCount) { + speedTestStartTime = new Date().getTime(); + } + if (++speedTestCount == 1000) { + setTimeout("alert('" + speedTestCount + " in " + (new Date().getTime() - speedTestStartTime) + "ms')", 0); + return { message: "done" }; + } + return { message: "continue" }; + } + + var addRemoveTestCount = 0; + + function echoListener() { + if (++addRemoveTestCount == 1) { + alert("Expected echo message"); + messageManager.removeMessageListener("async-echo", echoListener); + messageManager.broadcastAsyncMessage("async-echo"); + return; + } + alert("Unexpected echo message"); + } + + function listenerAddRemove() { + addRemoveTestCount = 0; + messageManager.addMessageListener("async-echo", echoListener); + messageManager.broadcastAsyncMessage("async-echo"); + } + + var MozAfterPaintCount = 0; + function enableMozAfterPaint() { + messageManager.addMessageListener("MozAfterPaint", + function(m) { + document.getElementById("messageLog").value = m.name + "[" + (++MozAfterPaintCount) + "]"; + }); + messageManager.loadFrameScript("data:,addEventListener('MozAfterPaint', function(e) { sendAsyncMessage('MozAfterPaint'); },true);", false); + } + + var Ci = Components.interfaces; + var Cu = Components.utils; + Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + </script> + + <toolbar id="controls"> + <toolbarbutton label="Back"/> + <toolbarbutton label="Forward"/> + <textbox onchange="loadURL(this.value)" flex="1" id="URL"/> + </toolbar> + <toolbar> + <toolbarbutton onclick="restart()" label="Recover"/> + <toolbarbutton onclick="randomClick()" label="random click"/> + <toolbarbutton onclick="keyPress()" label="key press"/> + <toolbarbutton onclick="messageSpeed()" label="test message handling speed"/> + <toolbarbutton onclick="listenerAddRemove()" label="test listener add/remove"/> + <toolbarbutton onclick="enableMozAfterPaint()" label="MozAfterPaint"/> + <toolbarbutton onclick="openWindow()" label="open new window"/> + <toolbarbutton onclick="closeWindow()" label="close this window"/> + </toolbar> + <toolbar><label value="Load a script (URL) to content process:"/> + <textbox flex="1" id="script"/><button + label="send" oncommand="document.getElementById('page') + .QueryInterface(Components.interfaces.nsIFrameLoaderOwner) + .frameLoader.messageManager + .loadFrameScript(this.previousSibling.value, false);"/> + </toolbar> + <toolbar> + <label value="Eval script in chrome context"/> + <textbox flex="1"/><button label="run" oncommand="eval(this.previousSibling.value);"/> + </toolbar> + + <browser type="content" src="http://www.google.com/" flex="1" id="page" remote="true"/> + <label id="messageLog" value="" crop="center"/> +</window> diff --git a/dom/ipc/tests/blob_verify.sjs b/dom/ipc/tests/blob_verify.sjs new file mode 100644 index 000000000..62b82359e --- /dev/null +++ b/dom/ipc/tests/blob_verify.sjs @@ -0,0 +1,20 @@ +const CC = Components.Constructor; +const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream"); +const BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1", + "nsIBinaryOutputStream", + "setOutputStream"); + +function handleRequest(request, response) { + var bodyStream = new BinaryInputStream(request.bodyInputStream); + var bodyBytes = []; + while ((bodyAvail = bodyStream.available()) > 0) + Array.prototype.push.apply(bodyBytes, bodyStream.readByteArray(bodyAvail)); + + var bos = new BinaryOutputStream(response.bodyOutputStream); + + response.processAsync(); + bos.writeByteArray(bodyBytes, bodyBytes.length); + response.finish(); +} diff --git a/dom/ipc/tests/browser.ini b/dom/ipc/tests/browser.ini new file mode 100644 index 000000000..f3d8ce140 --- /dev/null +++ b/dom/ipc/tests/browser.ini @@ -0,0 +1,6 @@ +[DEFAULT] +support-files = + file_disableScript.html + file_domainPolicy_base.html + +[browser_domainPolicy.js] diff --git a/dom/ipc/tests/browser_domainPolicy.js b/dom/ipc/tests/browser_domainPolicy.js new file mode 100644 index 000000000..df06b8bc0 --- /dev/null +++ b/dom/ipc/tests/browser_domainPolicy.js @@ -0,0 +1,240 @@ +var policy; // To make sure we never leave up an activated domain policy after a failed test, let's make this global. +function activateDomainPolicy() { + const ssm = Services.scriptSecurityManager; + policy = ssm.activateDomainPolicy(); +} + +function deactivateDomainPolicy() { + if (policy) { + policy.deactivate(); + policy = null; + } +} + +function* test_domainPolicy() { + + XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm"); + let deferred = Promise.defer(); + let currentTask = deferred.promise; + SpecialPowers.pushPrefEnv( + {set: [["dom.ipc.browser_frames.oop_by_default", false], + ["browser.pagethumbnails.capturing_disabled", false], + ["dom.mozBrowserFramesEnabled", false]]}, + () => { return deferred.resolve()}); + yield currentTask; + + // Create tab + let tab; + + // Init test + function initProcess() { + tab = gBrowser.addTab(); + gBrowser.selectedTab = tab; + + let initPromise = ContentTask.spawn(tab.linkedBrowser, null, function() { + Cu.import("resource://gre/modules/PromiseUtils.jsm"); + function loadBase() { + let deferred = PromiseUtils.defer(); + let listener = (event) => { + removeEventListener("DOMDocElementInserted", listener, true); + let listener2 = (event) => { + content.removeEventListener('load', listener2); + deferred.resolve(); + } + content.addEventListener('load', listener2); + }; + addEventListener("DOMDocElementInserted", listener, true); + return deferred.promise; + } + + return loadBase(); + }); + tab.linkedBrowser.loadURI("http://mochi.test:8888/browser/dom/ipc/tests/file_domainPolicy_base.html"); + return initPromise; + } + + // We use ContentTask for the tests, but we also want to pass some data and some helper functions too. + // To do that, we serialize an input object via JSON |ipcArgs| and some shared helper functions |initUtils| + // and eval them in the content process. + var ipcArgs = {}; + function initUtils(obj) { + obj.checkScriptEnabled = function(win, expectEnabled) { + win.wrappedJSObject.gFiredOnclick = false; + win.document.body.dispatchEvent(new win.Event('click')); + return { passed: win.wrappedJSObject.gFiredOnclick == expectEnabled, + msg: `Checking script-enabled for ${win.name} (${win.location})`}; + } + + obj.navigateFrame = function(ifr, src) { + let deferred = PromiseUtils.defer(); + function onload() { + ifr.removeEventListener('load', onload); + deferred.resolve(); + } + ifr.addEventListener('load', onload, false); + ifr.setAttribute('src', src); + return deferred.promise; + } + }; + + function runTest(test) { + return ContentTask.spawn(tab.linkedBrowser, + 'ipcArgs = ' + JSON.stringify(ipcArgs) + '; (' + initUtils.toSource() + ')(utils)', test); + } + + function checkAndCleanup(result) { + result = [].concat(result); + for (var i in result) + ok(result[i].passed, result[i].msg); + gBrowser.removeTab(tab); + deactivateDomainPolicy(); + ipcArgs = {}; + } + + function testDomain(domain) { + ipcArgs.domain = domain; + return (aUtils) => { + Cu.import("resource://gre/modules/PromiseUtils.jsm"); + var ipcArgs; + var utils = {}; + eval(aUtils); + + let path = '/browser/dom/ipc/tests/file_disableScript.html'; + let deferred = PromiseUtils.defer(); + var rootFrame = content.document.getElementById('root'); + utils.navigateFrame(rootFrame, ipcArgs.domain + path).then(() => { + deferred.resolve(utils.checkScriptEnabled(rootFrame.contentWindow, false)); + }); + return deferred.promise; + } + } + + info("Testing simple blacklist policy"); + + info("Creating child process first, activating domainPolicy after"); + currentTask = initProcess(); + yield currentTask; + activateDomainPolicy(); + var bl = policy.blacklist; + bl.add(Services.io.newURI('http://example.com', null, null)); + currentTask = runTest(testDomain("http://example.com")); + checkAndCleanup(yield currentTask); + + info("Activating domainPolicy first, creating child process after"); + activateDomainPolicy(); + var bl = policy.blacklist; + bl.add(Services.io.newURI('http://example.com', null, null)); + currentTask = initProcess(); + yield currentTask; + currentTask = runTest(testDomain("http://example.com")); + checkAndCleanup(yield currentTask); + + function testList(expectEnabled, list) { + ipcArgs.expectEnabled = expectEnabled; + ipcArgs.list = list; + return (aUtils) => { + Cu.import("resource://gre/modules/PromiseUtils.jsm"); + var ipcArgs; + var utils = {}; + eval(aUtils); + + var results = []; + var testListInternal = function(expectEnabled, list, idx) { + idx = idx || 0; + let deferred = PromiseUtils.defer(); + let path = '/browser/dom/ipc/tests/file_disableScript.html'; + let target = list[idx] + path; + var rootFrame = content.document.getElementById('root'); + utils.navigateFrame(rootFrame, target).then(function() { + results.push(utils.checkScriptEnabled(rootFrame.contentWindow, expectEnabled)); + if (idx == list.length - 1) + deferred.resolve(results); + else + testListInternal(expectEnabled, list, idx + 1).then(function(retArg) { deferred.resolve(retArg); }); + }); + return deferred.promise; + } + return testListInternal(ipcArgs.expectEnabled, ipcArgs.list); + } + } + + let testPolicy = { + exceptions: ['http://test1.example.com', 'http://example.com'], + superExceptions: ['http://test2.example.org', 'https://test1.example.com'], + exempt: ['http://test1.example.com', 'http://example.com', + 'http://test2.example.org', 'http://sub1.test2.example.org', + 'https://sub1.test1.example.com'], + notExempt: ['http://test2.example.com', 'http://sub1.test1.example.com', + 'http://www.example.com', 'https://test2.example.com', + 'https://example.com', 'http://test1.example.org'], + }; + + function activate(isBlack, exceptions, superExceptions) { + activateDomainPolicy(); + let set = isBlack ? policy.blacklist : policy.whitelist; + let superSet = isBlack ? policy.superBlacklist : policy.superWhitelist; + for (var e of exceptions) + set.add(makeURI(e)); + for (var e of superExceptions) + superSet.add(makeURI(e)); + }; + + info("Testing Blacklist-style Domain Policy"); + info("Activating domainPolicy first, creating child process after"); + activate(true, testPolicy.exceptions, testPolicy.superExceptions); + currentTask = initProcess(); + yield currentTask; + let results = []; + currentTask = runTest(testList(true, testPolicy.notExempt)); + results = results.concat(yield currentTask); + currentTask = runTest(testList(false, testPolicy.exempt)); + results = results.concat(yield currentTask); + checkAndCleanup(results); + + info("Creating child process first, activating domainPolicy after"); + currentTask = initProcess(); + yield currentTask; + activate(true, testPolicy.exceptions, testPolicy.superExceptions); + results = []; + currentTask = runTest(testList(true, testPolicy.notExempt)); + results = results.concat(yield currentTask); + currentTask = runTest(testList(false, testPolicy.exempt)); + results = results.concat(yield currentTask); + checkAndCleanup(results); + + info("Testing Whitelist-style Domain Policy"); + deferred = Promise.defer(); + currentTask = deferred.promise; + SpecialPowers.pushPrefEnv({set:[["javascript.enabled", false]]}, () => { return deferred.resolve()}); + yield currentTask; + + info("Activating domainPolicy first, creating child process after"); + activate(false, testPolicy.exceptions, testPolicy.superExceptions); + currentTask = initProcess(); + yield currentTask; + results = []; + currentTask = runTest(testList(false, testPolicy.notExempt)); + results = results.concat(yield currentTask); + currentTask = runTest(testList(true, testPolicy.exempt)); + results = results.concat(yield currentTask); + checkAndCleanup(results); + + info("Creating child process first, activating domainPolicy after"); + currentTask = initProcess(); + yield currentTask; + activate(false, testPolicy.exceptions, testPolicy.superExceptions); + results = []; + currentTask = runTest(testList(false, testPolicy.notExempt)); + results = results.concat(yield currentTask); + currentTask = runTest(testList(true, testPolicy.exempt)); + results = results.concat(yield currentTask); + checkAndCleanup(results); + finish(); +} + + +add_task(test_domainPolicy); + +registerCleanupFunction(()=>{ + deactivateDomainPolicy(); +})
\ No newline at end of file diff --git a/dom/ipc/tests/chrome.ini b/dom/ipc/tests/chrome.ini new file mode 100644 index 000000000..b6e3d4801 --- /dev/null +++ b/dom/ipc/tests/chrome.ini @@ -0,0 +1,8 @@ +[DEFAULT] +skip-if = os == 'android' +support-files = + process_error.xul + process_error_contentscript.js + +[test_process_error.xul] +skip-if = !crashreporter diff --git a/dom/ipc/tests/file_bug1086684.html b/dom/ipc/tests/file_bug1086684.html new file mode 100644 index 000000000..95db6f387 --- /dev/null +++ b/dom/ipc/tests/file_bug1086684.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1086684 +--> +<head> + <title>Test for Bug 1086684</title> + <meta charset="UTF-8"> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1086684">Mozilla Bug 1086684</a> +<div id="content"> + <input type="file" id="f"> +</div> +</body> +</html> diff --git a/dom/ipc/tests/file_disableScript.html b/dom/ipc/tests/file_disableScript.html new file mode 100644 index 000000000..f4888cd58 --- /dev/null +++ b/dom/ipc/tests/file_disableScript.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> +<head> +<script> +var gFiredOnload = false; +var gFiredOnclick = false; +</script> +</head> +<body onload="gFiredOnload = true;" onclick="gFiredOnclick = true;"> +</body> +</html> diff --git a/dom/ipc/tests/file_domainPolicy_base.html b/dom/ipc/tests/file_domainPolicy_base.html new file mode 100644 index 000000000..6e3ec7aec --- /dev/null +++ b/dom/ipc/tests/file_domainPolicy_base.html @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<html> +<head> +</head> +<body> +<iframe id="root" name="root"/> +</body> +</html> diff --git a/dom/ipc/tests/mochitest.ini b/dom/ipc/tests/mochitest.ini new file mode 100644 index 000000000..cf2a20d5b --- /dev/null +++ b/dom/ipc/tests/mochitest.ini @@ -0,0 +1,22 @@ +[DEFAULT] +support-files = + file_bug1086684.html + +[test_blob_sliced_from_child_process.html] +# This test is only supposed to run in the main process. +skip-if = e10s +[test_blob_sliced_from_parent_process.html] +# This test is only supposed to run in the main process. +skip-if = e10s +[test_bug1086684.html] +# This test is only supposed to run in the main process +skip-if = e10s || toolkit == 'android' +[test_cpow_cookies.html] +[test_child_docshell.html] +skip-if = toolkit == 'cocoa' # disabled due to hangs, see changeset 6852e7c47edf +[test_CrashService_crash.html] +skip-if = !(crashreporter && !e10s && (toolkit == 'gtk2' || toolkit == 'gtk3' || toolkit == 'cocoa' || toolkit == 'windows')) +[test_temporaryfile_stream.html] +support-files = + blob_verify.sjs + !/dom/canvas/test/captureStream_common.js diff --git a/dom/ipc/tests/process_error.xul b/dom/ipc/tests/process_error.xul new file mode 100644 index 000000000..62d3d9724 --- /dev/null +++ b/dom/ipc/tests/process_error.xul @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + orient="vertical"> + + <browser id="thebrowser" type="content" remote="true" /> + <script type="application/javascript"><![CDATA[ + Components.utils.import("resource://gre/modules/Services.jsm"); + + const ok = window.opener.wrappedJSObject.ok; + const is = window.opener.wrappedJSObject.is; + const done = window.opener.wrappedJSObject.done; + const SimpleTest = window.opener.wrappedJSObject.SimpleTest; + + function getMinidumpDirectory() { + var dir = Services.dirsvc.get('ProfD', Components.interfaces.nsIFile); + dir.append("minidumps"); + return dir; + } + + function removeFile(directory, filename) { + var file = directory.clone(); + file.append(filename); + if (file.exists()) { + file.remove(false); + } + } + + function crashObserver(subject, topic, data) { + is(topic, 'ipc:content-shutdown', 'Received correct observer topic.'); + ok(subject instanceof Components.interfaces.nsIPropertyBag2, + 'Subject implements nsIPropertyBag2.'); + + var dumpID; + if ('nsICrashReporter' in Components.interfaces) { + dumpID = subject.getPropertyAsAString('dumpID'); + ok(dumpID, "dumpID is present and not an empty string"); + } + + if (dumpID) { + var minidumpDirectory = getMinidumpDirectory(); + removeFile(minidumpDirectory, dumpID + '.dmp'); + removeFile(minidumpDirectory, dumpID + '.extra'); + } + + Services.obs.removeObserver(crashObserver, 'ipc:content-shutdown'); + done(); + } + Services.obs.addObserver(crashObserver, 'ipc:content-shutdown', false); + + document.getElementById('thebrowser') + .QueryInterface(Components.interfaces.nsIFrameLoaderOwner) + .frameLoader.messageManager + .loadFrameScript('chrome://mochitests/content/chrome/dom/ipc/tests/process_error_contentscript.js', true); + ]]></script> + +</window> diff --git a/dom/ipc/tests/process_error_contentscript.js b/dom/ipc/tests/process_error_contentscript.js new file mode 100644 index 000000000..789b8a37f --- /dev/null +++ b/dom/ipc/tests/process_error_contentscript.js @@ -0,0 +1,7 @@ +Components.utils.import("resource://gre/modules/ctypes.jsm"); + +privateNoteIntentionalCrash(); + +var zero = new ctypes.intptr_t(8); +var badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t)); +var crash = badptr.contents; diff --git a/dom/ipc/tests/test_CrashService_crash.html b/dom/ipc/tests/test_CrashService_crash.html new file mode 100644 index 000000000..a1d9b938d --- /dev/null +++ b/dom/ipc/tests/test_CrashService_crash.html @@ -0,0 +1,95 @@ +<!DOCTYPE HTML> +<html> +<!-- +Ensures that content crashes are reported to the crash service +(nsICrashService and CrashManager.jsm). +--> +<head> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + +<script type="application/javascript;version=1.7"> +"use strict"; + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestFlakyTimeout("untriaged"); +SpecialPowers.addPermission("browser", true, document); +SpecialPowers.pushPrefEnv({'set':[ + ["dom.mozBrowserFramesEnabled", true], + ["network.disable.ipc.security", true], + ["dom.ipc.tabs.disabled", false] +]}, function () { + + var iframe = document.createElementNS('http://www.w3.org/1999/xhtml', 'iframe'); + iframe.setAttribute("remote", "true"); + SpecialPowers.wrap(iframe).mozbrowser = true; + document.documentElement.appendChild(iframe); + + SimpleTest.expectChildProcessCrash(); + + var crashMan = + SpecialPowers.Cu.import("resource://gre/modules/Services.jsm"). + Services.crashmanager; + + // First, clear the crash record store. + info("Waiting for pruneOldCrashes"); + var future = new Date(Date.now() + 1000 * 60 * 60 * 24); + crashMan.pruneOldCrashes(future).then(function () { + + var crashDateMS = Date.now(); + + // Inject a frame script that crashes the content process. + var mm = SpecialPowers.getBrowserFrameMessageManager(iframe); + mm.loadFrameScript('data:,new ' + function ContentScriptScope() { + let Cu = Components.utils; + Cu.import("resource://gre/modules/ctypes.jsm"); + let crash = function() { + let zero = new ctypes.intptr_t(8); + let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t)); + badptr.contents; + }; + privateNoteIntentionalCrash(); + crash(); + }, false); + + // Finally, poll for the new crash record. + function tryGetCrash() { + info("Waiting for getCrashes"); + crashMan.getCrashes().then(SpecialPowers.wrapCallback(function (crashes) { + if (crashes.length) { + is(crashes.length, 1, "There should be only one record"); + var crash = crashes[0]; + ok(crash.isOfType(crashMan.PROCESS_TYPE_CONTENT, + crashMan.CRASH_TYPE_CRASH), + "Record should be a content crash"); + ok(!!crash.id, "Record should have an ID"); + ok(!!crash.crashDate, "Record should have a crash date"); + var dateMS = crash.crashDate.valueOf(); + var twoMin = 1000 * 60 * 2; + ok(crashDateMS - twoMin <= dateMS && + dateMS <= crashDateMS + twoMin, + "Record's crash date should be nowish: " + + "now=" + crashDateMS + " recordDate=" + dateMS); + SimpleTest.finish(); + } + else { + setTimeout(tryGetCrash, 1000); + } + }), function (err) { + ok(false, "Error getting crashes: " + err); + SimpleTest.finish(); + }); + } + setTimeout(tryGetCrash, 1000); + + }, function () { + ok(false, "pruneOldCrashes error"); + SimpleTest.finish(); + }); +}); + +</script> +</body> +</html> diff --git a/dom/ipc/tests/test_blob_sliced_from_child_process.html b/dom/ipc/tests/test_blob_sliced_from_child_process.html new file mode 100644 index 000000000..0dec1d84c --- /dev/null +++ b/dom/ipc/tests/test_blob_sliced_from_child_process.html @@ -0,0 +1,185 @@ +<!DOCTYPE html> +<html> + <head> + <title>Test for slicing blobs that originated in a child process</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"> + </script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + </head> + <body onload="setup();"> + <script type="application/javascript;version=1.7"> +"use strict"; + +function childFrameScript() { + "use strict"; + + Components.utils.importGlobalProperties(["Blob"]); + + const messageName = "test:blob-slice-test"; + const blobData = ["So", " ", "many", " ", "blobs!"]; + const blobText = blobData.join(""); + const blobType = "text/plain"; + + let blob = new Blob(blobData, { type: blobType }); + + let sliceText = blobData[2][1] + blobData[2][2]; + let sliceIndex = blobText.indexOf(sliceText); + + let firstSliceStart = blobData[0].length + blobData[1].length; + let firstSliceEnd = firstSliceStart + blobData[2].length; + + let slice = blob.slice(firstSliceStart, firstSliceEnd, blobType); + + let secondSliceStart = blobData[2].indexOf("a"); + let secondSliceEnd = secondSliceStart + 2; + + slice = slice.slice(secondSliceStart, secondSliceEnd, blobType); + + sendAsyncMessage(messageName, { blob: blob }); + sendAsyncMessage(messageName, { slice: slice }); +} + +function parentFrameScript(mm) { + const messageName = "test:blob-slice-test"; + const blobData = ["So", " ", "many", " ", "blobs!"]; + const blobText = blobData.join(""); + const blobType = "text/plain"; + + const sliceText = "an"; + + let receivedBlob = false; + let receivedSlice = false; + + let finishedTestingBlob = false; + let finishedTestingSlice = false; + + mm.addMessageListener(messageName, function(message) { + if ("blob" in message.data) { + is(receivedBlob, false, "Have not yet received Blob"); + is(receivedSlice, false, "Have not yet received Slice"); + is(finishedTestingBlob, false, "Have not yet finished testing Blob"); + is(finishedTestingSlice, false, "Have not yet finished testing Slice"); + + receivedBlob = true; + + let blob = message.data.blob; + + ok(blob instanceof Blob, "Received a Blob"); + is(blob.size, blobText.length, "Blob has correct size"); + is(blob.type, blobType, "Blob has correct type"); + + let slice = blob.slice(blobText.length - + blobData[blobData.length - 1].length, + blob.size, + blobType); + + ok(slice instanceof Blob, "Slice returned a Blob"); + is(slice.size, + blobData[blobData.length - 1].length, + "Slice has correct size"); + is(slice.type, blobType, "Slice has correct type"); + + let reader = new FileReader(); + reader.onload = function() { + is(reader.result, + blobData[blobData.length - 1], + "Slice has correct data"); + + finishedTestingBlob = true; + + if (finishedTestingSlice) { + SimpleTest.finish(); + } + }; + reader.readAsText(slice); + + return; + } + + if ("slice" in message.data) { + is(receivedBlob, true, "Already received Blob"); + is(receivedSlice, false, "Have not yet received Slice"); + is(finishedTestingSlice, false, "Have not yet finished testing Slice"); + + receivedSlice = true; + + let slice = message.data.slice; + + ok(slice instanceof Blob, "Received a Blob for slice"); + is(slice.size, sliceText.length, "Slice has correct size"); + is(slice.type, blobType, "Slice has correct type"); + + let reader = new FileReader(); + reader.onload = function() { + is(reader.result, sliceText, "Slice has correct data"); + + let slice2 = slice.slice(1, 2, blobType); + + ok(slice2 instanceof Blob, "Slice returned a Blob"); + is(slice2.size, 1, "Slice has correct size"); + is(slice2.type, blobType, "Slice has correct type"); + + let reader2 = new FileReader(); + reader2.onload = function() { + is(reader2.result, sliceText[1], "Slice has correct data"); + + finishedTestingSlice = true; + + if (finishedTestingBlob) { + SimpleTest.finish(); + } + }; + reader2.readAsText(slice2); + }; + reader.readAsText(slice); + + return; + } + + ok(false, "Received a bad message: " + JSON.stringify(message.data)); + }); + + mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();", + false); +} + +function setup() { + info("Got load event"); + + SpecialPowers.pushPrefEnv( + { set: [ ["dom.ipc.browser_frames.oop_by_default", true], + ["dom.mozBrowserFramesEnabled", true], + ["network.disable.ipc.security", true], + ["browser.pagethumbnails.capturing_disabled", true] ] }, + function() { + info("Prefs set"); + + SpecialPowers.pushPermissions( + [ { type: "browser", allow: true, context: document } ], + function() { + info("Permissions set"); + + let iframe = document.createElement("iframe"); + SpecialPowers.wrap(iframe).mozbrowser = true; + iframe.id = "iframe"; + iframe.src = + "data:text/html,<!DOCTYPE HTML><html><body></body></html>"; + + iframe.addEventListener("mozbrowserloadend", function() { + info("Starting tests"); + + let mm = SpecialPowers.getBrowserFrameMessageManager(iframe) + parentFrameScript(mm); + }); + + document.body.appendChild(iframe); + } + ); + } + ); +} + +SimpleTest.waitForExplicitFinish(); + </script> + </body> +</html> diff --git a/dom/ipc/tests/test_blob_sliced_from_parent_process.html b/dom/ipc/tests/test_blob_sliced_from_parent_process.html new file mode 100644 index 000000000..7d8a1665f --- /dev/null +++ b/dom/ipc/tests/test_blob_sliced_from_parent_process.html @@ -0,0 +1,213 @@ +<!DOCTYPE html> +<html> + <head> + <title>Test for slicing blobs that originated in a parent process</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"> + </script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + </head> + <body onload="setup();"> + <script type="application/javascript;version=1.7"> +"use strict"; + +function childFrameScript() { + "use strict"; + + const { classes: Cc, interfaces: Ci } = Components; + + const messageName = "test:blob-slice-test"; + const blobData = ["So", " ", "many", " ", "blobs!"]; + const blobText = blobData.join(""); + const blobType = "text/plain"; + + const sliceText = "an"; + + function info(msg) { + sendAsyncMessage(messageName, { op: "info", msg: msg }); + } + + function ok(condition, name, diag) { + sendAsyncMessage(messageName, + { op: "ok", + condition: condition, + name: name, + diag: diag }); + } + + function is(a, b, name) { + let pass = a == b; + let diag = pass ? "" : "got " + a + ", expected " + b; + ok(pass, name, diag); + } + + function finish(result) { + sendAsyncMessage(messageName, { op: "done", result: result }); + } + + function grabAndContinue(arg) { + testGenerator.send(arg); + } + + function testSteps() { + addMessageListener(messageName, grabAndContinue); + let message = yield undefined; + + let blob = message.data; + + ok(blob instanceof Ci.nsIDOMBlob, "Received a Blob"); + is(blob.size, blobText.length, "Blob has correct length"); + is(blob.type, blobType, "Blob has correct type"); + + info("Reading blob"); + + let reader = new FileReader(); + reader.addEventListener("load", grabAndContinue); + reader.readAsText(blob); + + yield undefined; + + is(reader.result, blobText, "Blob has correct data"); + + let firstSliceStart = blobData[0].length + blobData[1].length; + let firstSliceEnd = firstSliceStart + blobData[2].length; + + let slice = blob.slice(firstSliceStart, firstSliceEnd, blobType); + + ok(slice instanceof Ci.nsIDOMBlob, "Slice returned a Blob"); + is(slice.size, blobData[2].length, "Slice has correct length"); + is(slice.type, blobType, "Slice has correct type"); + + info("Reading slice"); + + reader = new FileReader(); + reader.addEventListener("load", grabAndContinue); + reader.readAsText(slice); + + yield undefined; + + is(reader.result, blobData[2], "Slice has correct data"); + + let secondSliceStart = blobData[2].indexOf("a"); + let secondSliceEnd = secondSliceStart + sliceText.length; + + slice = slice.slice(secondSliceStart, secondSliceEnd, blobType); + + ok(slice instanceof Ci.nsIDOMBlob, "Second slice returned a Blob"); + is(slice.size, sliceText.length, "Second slice has correct length"); + is(slice.type, blobType, "Second slice has correct type"); + + info("Sending second slice"); + finish(slice); + + yield undefined; + } + + let testGenerator = testSteps(); + testGenerator.next(); +} + +function parentFrameScript(mm) { + const messageName = "test:blob-slice-test"; + const blobData = ["So", " ", "many", " ", "blobs!"]; + const blobText = blobData.join(""); + const blobType = "text/plain"; + + const sliceText = "an"; + + function grabAndContinue(arg) { + testGenerator.send(arg); + } + + function testSteps() { + let slice = yield undefined; + + ok(slice instanceof Blob, "Received a Blob"); + is(slice.size, sliceText.length, "Slice has correct size"); + is(slice.type, blobType, "Slice has correct type"); + + let reader = new FileReader(); + reader.onload = grabAndContinue; + reader.readAsText(slice); + yield undefined; + + is(reader.result, sliceText, "Slice has correct data"); + SimpleTest.finish(); + + yield undefined; + } + + let testGenerator = testSteps(); + testGenerator.next(); + + mm.addMessageListener(messageName, function(message) { + let data = message.data; + switch (data.op) { + case "info": { + info(data.msg); + break; + } + + case "ok": { + ok(data.condition, data.name, data.diag); + break; + } + + case "done": { + testGenerator.send(data.result); + break; + } + + default: { + ok(false, "Unknown op: " + data.op); + SimpleTest.finish(); + } + } + }); + + mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();", + false); + + let blob = new Blob(blobData, { type: blobType }); + mm.sendAsyncMessage(messageName, blob); +} + +function setup() { + info("Got load event"); + + SpecialPowers.pushPrefEnv( + { set: [ ["dom.ipc.browser_frames.oop_by_default", true], + ["dom.mozBrowserFramesEnabled", true], + ["network.disable.ipc.security", true], + ["browser.pagethumbnails.capturing_disabled", true] ] }, + function() { + info("Prefs set"); + + SpecialPowers.pushPermissions( + [ { type: "browser", allow: true, context: document } ], + function() { + info("Permissions set"); + + let iframe = document.createElement("iframe"); + SpecialPowers.wrap(iframe).mozbrowser = true; + iframe.id = "iframe"; + iframe.src = + "data:text/html,<!DOCTYPE HTML><html><body></body></html>"; + + iframe.addEventListener("mozbrowserloadend", function() { + info("Starting tests"); + + let mm = SpecialPowers.getBrowserFrameMessageManager(iframe) + parentFrameScript(mm); + }); + + document.body.appendChild(iframe); + } + ); + } + ); +} + +SimpleTest.waitForExplicitFinish(); + </script> + </body> +</html> diff --git a/dom/ipc/tests/test_bug1086684.html b/dom/ipc/tests/test_bug1086684.html new file mode 100644 index 000000000..962826ec6 --- /dev/null +++ b/dom/ipc/tests/test_bug1086684.html @@ -0,0 +1,107 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for recursive CPOW-getting-cookie bug</title> + <script type="application/javascript" + src="/tests/SimpleTest/SimpleTest.js"> + </script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + + <script type="application/javascript;version=1.8"> + "use strict"; + + SimpleTest.waitForExplicitFinish(); + + const childFrameURL = + "http://mochi.test:8888/tests/dom/ipc/tests/file_bug1086684.html"; + + function childFrameScript() { + "use strict"; + + let { MockFilePicker } = + Components.utils.import("chrome://specialpowers/content/MockFilePicker.jsm", {}); + + function parentReady(message) { + MockFilePicker.init(content); + MockFilePicker.returnFiles = [message.data.file]; + MockFilePicker.returnValue = MockFilePicker.returnOK; + + let input = content.document.getElementById("f"); + input.addEventListener("change", () => { + MockFilePicker.cleanup(); + let value = input.value; + message.target.sendAsyncMessage("testBug1086684:childDone", { value }); + }); + + input.focus(); + input.click(); + } + + addMessageListener("testBug1086684:parentReady", function(message) { + parentReady(message); + }); + } + + let test; + function* testStructure(mm) { + let value; + + function testDone(msg) { + test.next(msg.data.value); + } + + mm.addMessageListener("testBug1086684:childDone", testDone); + + let blob = new Blob([]); + let file = new File([blob], "helloworld.txt", { type: "text/plain" }); + + mm.sendAsyncMessage("testBug1086684:parentReady", { file }); + value = yield; + + // Note that the "helloworld.txt" passed in above doesn't affect the + // 'value' getter. Because we're mocking a file using a blob, we ask the + // blob for its path, which is the empty string. + is(value, "", "got the right answer and didn't crash"); + + SimpleTest.finish(); + } + + function runTests() { + info("Browser prefs set."); + + let iframe = document.createElement("iframe"); + SpecialPowers.wrap(iframe).mozbrowser = true; + iframe.id = "iframe"; + iframe.src = childFrameURL; + + iframe.addEventListener("mozbrowserloadend", function() { + info("Got iframe load event."); + let mm = SpecialPowers.getBrowserFrameMessageManager(iframe); + mm.loadFrameScript("data:,(" + encodeURI(childFrameScript.toString()) + ")();", + false); + + test = testStructure(mm); + test.next(); + }); + + document.body.appendChild(iframe); + } + + addEventListener("load", function() { + info("Got load event."); + + SpecialPowers.addPermission("browser", true, document); + SpecialPowers.pushPrefEnv({ + "set": [ + ["dom.ipc.browser_frames.oop_by_default", true], + ["dom.mozBrowserFramesEnabled", true], + ["network.disable.ipc.security", true], + ["browser.pagethumbnails.capturing_disabled", true] + ] + }, runTests); + }); + </script> +</body> +</html> diff --git a/dom/ipc/tests/test_child_docshell.html b/dom/ipc/tests/test_child_docshell.html new file mode 100644 index 000000000..5855569e8 --- /dev/null +++ b/dom/ipc/tests/test_child_docshell.html @@ -0,0 +1,86 @@ +<!DOCTYPE HTML> +<html> +<!-- +--> +<head> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + +<script type="application/javascript;version=1.7"> +"use strict"; + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.addPermission("browser", true, document); +SpecialPowers.pushPrefEnv({'set':[ + ["dom.mozBrowserFramesEnabled", true], + ["network.disable.ipc.security", true], + ["dom.ipc.tabs.disabled", false] +]}, function () { + + var iframe = document.createElementNS('http://www.w3.org/1999/xhtml', 'iframe'); + iframe.setAttribute("remote", "true"); + SpecialPowers.wrap(iframe).mozbrowser = true; + document.documentElement.appendChild(iframe); + + var mm = SpecialPowers.getBrowserFrameMessageManager(iframe); + mm.addMessageListener("chromeEventHandler", function (msg) { + msg = SpecialPowers.wrap(msg); + var result = msg.json; + is(result.processType, SpecialPowers.Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT, + "The frame script is running in a real distinct child process"); + ok(result.hasCorrectInterface, + "docshell.chromeEventHandler has nsIDOMEventTarget interface"); + }); + + + mm.addMessageListener("DOMWindowCreatedReceived", function (msg) { + msg = SpecialPowers.wrap(msg); + ok(true, "the chrome event handler looks functional"); + var result = msg.json; + ok(result.stableChromeEventHandler, "docShell.chromeEventHandler is stable"); + ok(result.iframeHasNewDocShell, "iframe spawns a new docShell"); + ok(result.iframeHasSameChromeEventHandler, "but iframe has the same chrome event handler"); + SimpleTest.finish(); + }); + // Inject a frame script in the child process: + mm.loadFrameScript('data:,new ' + function ContentScriptScope() { + var processType = Components.classes["@mozilla.org/xre/runtime;1"] + .getService(Components.interfaces.nsIXULRuntime) + .processType; + var chromeEventHandler = docShell.chromeEventHandler; + sendAsyncMessage("chromeEventHandler", { + processType: Services.appinfo.processType, + hasCorrectInterface: chromeEventHandler instanceof Components.interfaces.nsIDOMEventTarget + }); + + /* + Ensure that this chromeEventHandler actually works, + by creating a new window and listening for its DOMWindowCreated event + */ + chromeEventHandler.addEventListener("DOMWindowCreated", function listener(evt) { + if (evt.target == content.document) { + return; + } + chromeEventHandler.removeEventListener("DOMWindowCreated", listener); + let new_win = evt.target.defaultView; + let new_docShell = new_win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell); + sendAsyncMessage("DOMWindowCreatedReceived", { + stableChromeEventHandler: chromeEventHandler === docShell.chromeEventHandler, + iframeHasNewDocShell: new_docShell !== docShell, + iframeHasSameChromeEventHandler: new_docShell.chromeEventHandler === chromeEventHandler + }); + }); + + let i = content.document.createElement("iframe"); + i.setAttribute("src", "data:text/html,foo"); + content.document.documentElement.appendChild(i); + }, false); +}); + +</script> +</body> +</html> diff --git a/dom/ipc/tests/test_cpow_cookies.html b/dom/ipc/tests/test_cpow_cookies.html new file mode 100644 index 000000000..1e55d3878 --- /dev/null +++ b/dom/ipc/tests/test_cpow_cookies.html @@ -0,0 +1,90 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for recursive CPOW-getting-cookie bug</title> + <script type="application/javascript" + src="/tests/SimpleTest/SimpleTest.js"> + </script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + + <script type="application/javascript;version=1.8"> + "use strict"; + + SimpleTest.waitForExplicitFinish(); + + const childFrameURL = + "data:text/html,<!DOCTYPE HTML><html><body></body></html>"; + + function childFrameScript() { + "use strict"; + + const Ci = Components.interfaces; + + function test1(message) { + // NB: This is a no-op because we're a data: document with a null + // principal. + content.document.cookie = "a=b"; + message.target.sendAsyncMessage("testCPOWCookies:test1Finished", { pass: true }); + } + + addMessageListener("testCPOWCookies:test1", function(message) { + test1(message); + }); + } + + let test; + function* testStructure(mm) { + let lastResult; + + function testDone(msg) { + test.next(msg.data); + } + + mm.addMessageListener("testCPOWCookies:test1Finished", testDone); + + mm.sendAsyncMessage("testCPOWCookies:test1", {}); + lastResult = yield; + ok(lastResult.pass, "got the right answer and didn't crash"); + + SimpleTest.finish(); + } + + function runTests() { + info("Browser prefs set."); + + let iframe = document.createElement("iframe"); + SpecialPowers.wrap(iframe).mozbrowser = true; + iframe.id = "iframe"; + iframe.src = childFrameURL; + + iframe.addEventListener("mozbrowserloadend", function() { + info("Got iframe load event."); + let mm = SpecialPowers.getBrowserFrameMessageManager(iframe); + mm.loadFrameScript("data:,(" + encodeURI(childFrameScript.toString()) + ")();", + false); + + test = testStructure(mm); + test.next(); + }); + + document.body.appendChild(iframe); + } + + addEventListener("load", function() { + info("Got load event."); + + SpecialPowers.addPermission("browser", true, document); + SpecialPowers.pushPrefEnv({ + "set": [ + ["dom.ipc.browser_frames.oop_by_default", true], + ["dom.mozBrowserFramesEnabled", true], + ["network.disable.ipc.security", true], + ["browser.pagethumbnails.capturing_disabled", true] + ] + }, runTests); + }); + </script> +</body> +</html> diff --git a/dom/ipc/tests/test_process_error.xul b/dom/ipc/tests/test_process_error.xul new file mode 100644 index 000000000..72352fc88 --- /dev/null +++ b/dom/ipc/tests/test_process_error.xul @@ -0,0 +1,22 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script> + SimpleTest.waitForExplicitFinish(); + + var w = window.open('process_error.xul', '_blank', 'chrome,resizable=yes,width=400,height=600'); + + function done() + { + w.close(); + SimpleTest.finish(); + } + </script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;" /> +</window> diff --git a/dom/ipc/tests/test_temporaryfile_stream.html b/dom/ipc/tests/test_temporaryfile_stream.html new file mode 100644 index 000000000..e05accbdf --- /dev/null +++ b/dom/ipc/tests/test_temporaryfile_stream.html @@ -0,0 +1,80 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Send an nsTemporaryFileInputStream cross-process</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/dom/canvas/test/captureStream_common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<pre id="test"> +<div id="content"> +</div> +<script class="testbody" type="text/javascript"> +function startTest() { + var canvas = document.createElement("canvas"); + canvas.width = canvas.height = 100; + document.getElementById("content").appendChild(canvas); + + var helper = new CaptureStreamTestHelper2D(100, 100); + helper.drawColor(canvas, helper.red); + + var stream = canvas.captureStream(0); + + var blob; + + mediaRecorder = new MediaRecorder(stream); + is(mediaRecorder.stream, stream, + "Media recorder stream = canvas stream at the start of recording"); + + mediaRecorder.onwarning = () => ok(false, "warning unexpectedly fired"); + + mediaRecorder.onerror = () => ok(false, "Recording failed"); + + mediaRecorder.ondataavailable = ev => { + is(blob, undefined, "Should only get one dataavailable event"); + blob = ev.data; + }; + + mediaRecorder.onstart = () => { + info("Got 'start' event"); + // We just want one frame encoded, to see that the recorder produces something readable. + mediaRecorder.stop(); + }; + + mediaRecorder.onstop = () => { + info("Got 'stop' event"); + ok(blob, "Should have gotten a data blob"); + var xhr = new XMLHttpRequest(); + xhr.open('POST', 'blob_verify.sjs', true); + xhr.onload = () => { + var video = document.createElement("video"); + video.id = "recorded-video"; + video.src = URL.createObjectURL(xhr.response); + video.play(); + video.onerror = err => { + ok(false, "Should be able to play the recording. Got error. code=" + video.error.code); + SimpleTest.finish(); + }; + document.getElementById("content").appendChild(video); + helper.waitForPixelColor(video, helper.red, 128, "Should become red") + .then(SimpleTest.finish); + }; + xhr.onerror = () => { + ok(false, "XHR error"); + SimpleTest.finish(); + } + xhr.responseType = "blob"; + xhr.send(blob); + }; + + mediaRecorder.start(); + is(mediaRecorder.state, "recording", "Media recorder should be recording"); +} + +SimpleTest.waitForExplicitFinish(); +SpecialPowers.pushPrefEnv({set:[["media.recorder.max_memory", 1]]}, startTest); +</script> +</pre> +</body> +</html> |