зеркало из https://github.com/mozilla/gecko-dev.git
480 строки
18 KiB
C++
480 строки
18 KiB
C++
/* -*- 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_BrowsingContext_h
|
|
#define mozilla_dom_BrowsingContext_h
|
|
|
|
#include "mozilla/Maybe.h"
|
|
#include "mozilla/RefPtr.h"
|
|
#include "mozilla/Tuple.h"
|
|
#include "mozilla/WeakPtr.h"
|
|
#include "mozilla/dom/BindingDeclarations.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsCycleCollectionParticipant.h"
|
|
#include "nsIDocShell.h"
|
|
#include "nsString.h"
|
|
#include "nsTArray.h"
|
|
#include "nsWrapperCache.h"
|
|
#include "nsILoadInfo.h"
|
|
|
|
class nsGlobalWindowOuter;
|
|
class nsIPrincipal;
|
|
class nsOuterWindowProxy;
|
|
class PickleIterator;
|
|
|
|
namespace IPC {
|
|
class Message;
|
|
} // namespace IPC
|
|
|
|
namespace mozilla {
|
|
|
|
class ErrorResult;
|
|
class LogModule;
|
|
|
|
namespace ipc {
|
|
class IProtocol;
|
|
|
|
template <typename T>
|
|
struct IPDLParamTraits;
|
|
} // namespace ipc
|
|
|
|
namespace dom {
|
|
class BrowsingContent;
|
|
class BrowsingContextGroup;
|
|
class CanonicalBrowsingContext;
|
|
class ContentParent;
|
|
template <typename>
|
|
struct Nullable;
|
|
template <typename T>
|
|
class Sequence;
|
|
struct WindowPostMessageOptions;
|
|
class WindowProxyHolder;
|
|
|
|
class BrowsingContextBase {
|
|
protected:
|
|
BrowsingContextBase() {
|
|
// default-construct each field.
|
|
#define MOZ_BC_FIELD(name, type) m##name = type();
|
|
#include "mozilla/dom/BrowsingContextFieldList.h"
|
|
}
|
|
~BrowsingContextBase() = default;
|
|
|
|
#define MOZ_BC_FIELD(name, type) \
|
|
type m##name; \
|
|
\
|
|
/* shadow to validate fields. aSource is setter process or null*/ \
|
|
void WillSet##name(type const& aValue, ContentParent* aSource) {} \
|
|
void DidSet##name(ContentParent* aSource) {}
|
|
#include "mozilla/dom/BrowsingContextFieldList.h"
|
|
};
|
|
|
|
// BrowsingContext, in this context, is the cross process replicated
|
|
// environment in which information about documents is stored. In
|
|
// particular the tree structure of nested browsing contexts is
|
|
// represented by the tree of BrowsingContexts.
|
|
//
|
|
// The tree of BrowsingContexts is created in step with its
|
|
// corresponding nsDocShell, and when nsDocShells are connected
|
|
// through a parent/child relationship, so are BrowsingContexts. The
|
|
// major difference is that BrowsingContexts are replicated (synced)
|
|
// to the parent process, making it possible to traverse the
|
|
// BrowsingContext tree for a tab, in both the parent and the child
|
|
// process.
|
|
//
|
|
// Trees of BrowsingContexts should only ever contain nodes of the
|
|
// same BrowsingContext::Type. This is enforced by asserts in the
|
|
// BrowsingContext::Create* methods.
|
|
class BrowsingContext : public nsWrapperCache,
|
|
public SupportsWeakPtr<BrowsingContext>,
|
|
public BrowsingContextBase {
|
|
public:
|
|
enum class Type { Chrome, Content };
|
|
|
|
static void Init();
|
|
static LogModule* GetLog();
|
|
static void CleanupContexts(uint64_t aProcessId);
|
|
|
|
// Look up a BrowsingContext in the current process by ID.
|
|
static already_AddRefed<BrowsingContext> Get(uint64_t aId);
|
|
static already_AddRefed<BrowsingContext> Get(GlobalObject&, uint64_t aId) {
|
|
return Get(aId);
|
|
}
|
|
|
|
// Create a brand-new BrowsingContext object.
|
|
static already_AddRefed<BrowsingContext> Create(BrowsingContext* aParent,
|
|
BrowsingContext* aOpener,
|
|
const nsAString& aName,
|
|
Type aType);
|
|
|
|
// Cast this object to a canonical browsing context, and return it.
|
|
CanonicalBrowsingContext* Canonical();
|
|
|
|
// Is the most recent Document in this BrowsingContext loaded within this
|
|
// process? This may be true with a null mDocShell after the Window has been
|
|
// closed.
|
|
bool IsInProcess() const { return mIsInProcess; }
|
|
|
|
// Get the DocShell for this BrowsingContext if it is in-process, or
|
|
// null if it's not.
|
|
nsIDocShell* GetDocShell() { return mDocShell; }
|
|
void SetDocShell(nsIDocShell* aDocShell);
|
|
|
|
// Get the outer window object for this BrowsingContext if it is in-process
|
|
// and still has a docshell, or null otherwise.
|
|
nsPIDOMWindowOuter* GetDOMWindow() const {
|
|
return mDocShell ? mDocShell->GetWindow() : nullptr;
|
|
}
|
|
|
|
// Attach the current BrowsingContext to its parent, in both the child and the
|
|
// parent process. BrowsingContext objects are created attached by default, so
|
|
// this method need only be called when restoring cached BrowsingContext
|
|
// objects.
|
|
void Attach(bool aFromIPC = false);
|
|
|
|
// Detach the current BrowsingContext from its parent, in both the
|
|
// child and the parent process.
|
|
void Detach(bool aFromIPC = false);
|
|
|
|
// Remove all children from the current BrowsingContext and cache
|
|
// them to allow them to be attached again.
|
|
void CacheChildren(bool aFromIPC = false);
|
|
|
|
// Determine if the current BrowsingContext was 'cached' by the logic in
|
|
// CacheChildren.
|
|
bool IsCached();
|
|
|
|
const nsString& Name() const { return mName; }
|
|
void GetName(nsAString& aName) { aName = mName; }
|
|
bool NameEquals(const nsAString& aName) { return mName.Equals(aName); }
|
|
|
|
bool IsContent() const { return mType == Type::Content; }
|
|
|
|
uint64_t Id() const { return mBrowsingContextId; }
|
|
|
|
BrowsingContext* GetParent() const { return mParent; }
|
|
|
|
already_AddRefed<BrowsingContext> GetOpener() const { return Get(mOpenerId); }
|
|
void SetOpener(BrowsingContext* aOpener) {
|
|
SetOpenerId(aOpener ? aOpener->Id() : 0);
|
|
}
|
|
|
|
void GetChildren(nsTArray<RefPtr<BrowsingContext>>& aChildren);
|
|
|
|
BrowsingContextGroup* Group() { return mGroup; }
|
|
|
|
// Using the rules for choosing a browsing context we try to find
|
|
// the browsing context with the given name in the set of
|
|
// transitively reachable browsing contexts. Performs access control
|
|
// with regards to this.
|
|
// See
|
|
// https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name.
|
|
//
|
|
// BrowsingContext::FindWithName(const nsAString&) is equivalent to
|
|
// calling nsIDocShellTreeItem::FindItemWithName(aName, nullptr,
|
|
// nullptr, false, <return value>).
|
|
BrowsingContext* FindWithName(const nsAString& aName);
|
|
|
|
// Find a browsing context in this context's list of
|
|
// children. Doesn't consider the special names, '_self', '_parent',
|
|
// '_top', or '_blank'. Performs access control with regard to
|
|
// 'this'.
|
|
BrowsingContext* FindChildWithName(const nsAString& aName);
|
|
|
|
nsISupports* GetParentObject() const;
|
|
JSObject* WrapObject(JSContext* aCx,
|
|
JS::Handle<JSObject*> aGivenProto) override;
|
|
|
|
// This function would be called when its corresponding document is activated
|
|
// by user gesture, and we would set the flag in the top level browsing
|
|
// context.
|
|
void NotifyUserGestureActivation();
|
|
|
|
// This function would be called when we want to reset the user gesture
|
|
// activation flag of the top level browsing context.
|
|
void NotifyResetUserGestureActivation();
|
|
|
|
// Return true if it corresponding document is activated by user gesture.
|
|
bool GetUserGestureActivation();
|
|
|
|
// Return the window proxy object that corresponds to this browsing context.
|
|
inline JSObject* GetWindowProxy() const { return mWindowProxy; }
|
|
// Set the window proxy object that corresponds to this browsing context.
|
|
void SetWindowProxy(JS::Handle<JSObject*> aWindowProxy) {
|
|
mWindowProxy = aWindowProxy;
|
|
}
|
|
|
|
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(BrowsingContext)
|
|
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(BrowsingContext)
|
|
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(BrowsingContext)
|
|
|
|
using Children = nsTArray<RefPtr<BrowsingContext>>;
|
|
const Children& GetChildren() { return mChildren; }
|
|
|
|
// Perform a pre-order walk of this BrowsingContext subtree.
|
|
template <typename Func>
|
|
void PreOrderWalk(Func&& aCallback) {
|
|
aCallback(this);
|
|
for (auto& child : GetChildren()) {
|
|
child->PreOrderWalk(aCallback);
|
|
}
|
|
}
|
|
|
|
// Window APIs that are cross-origin-accessible (from the HTML spec).
|
|
BrowsingContext* Window() { return Self(); }
|
|
BrowsingContext* Self() { return this; }
|
|
void Location(JSContext* aCx, JS::MutableHandle<JSObject*> aLocation,
|
|
ErrorResult& aError);
|
|
void Close(CallerType aCallerType, ErrorResult& aError);
|
|
bool GetClosed(ErrorResult&) { return mClosed; }
|
|
void Focus(ErrorResult& aError);
|
|
void Blur(ErrorResult& aError);
|
|
BrowsingContext* GetFrames(ErrorResult& aError) { return Self(); }
|
|
int32_t Length() const { return mChildren.Length(); }
|
|
Nullable<WindowProxyHolder> GetTop(ErrorResult& aError);
|
|
void GetOpener(JSContext* aCx, JS::MutableHandle<JS::Value> aOpener,
|
|
ErrorResult& aError) const;
|
|
Nullable<WindowProxyHolder> GetParent(ErrorResult& aError) const;
|
|
void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
|
const nsAString& aTargetOrigin,
|
|
const Sequence<JSObject*>& aTransfer,
|
|
nsIPrincipal& aSubjectPrincipal, ErrorResult& aError);
|
|
void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
|
const WindowPostMessageOptions& aOptions,
|
|
nsIPrincipal& aSubjectPrincipal, ErrorResult& aError);
|
|
|
|
JSObject* WrapObject(JSContext* aCx);
|
|
|
|
void StartDelayedAutoplayMediaComponents();
|
|
|
|
/**
|
|
* Transaction object. This object is used to specify and then commit
|
|
* modifications to synchronized fields in BrowsingContexts.
|
|
*/
|
|
class Transaction {
|
|
public:
|
|
// Apply the changes from this transaction to the specified BrowsingContext
|
|
// in all processes. This method will call the correct `WillSet` and
|
|
// `DidSet` methods, as well as move the value.
|
|
//
|
|
// NOTE: This method mutates `this`, resetting all members to `Nothing()`
|
|
void Commit(BrowsingContext* aOwner);
|
|
|
|
// You probably don't want to directly call this method - instead call
|
|
// `Commit`, which will perform the necessary synchronization.
|
|
//
|
|
// |aSource| is the ContentParent which is performing the mutation in the
|
|
// parent process.
|
|
void Apply(BrowsingContext* aOwner, ContentParent* aSource);
|
|
|
|
#define MOZ_BC_FIELD(name, type) mozilla::Maybe<type> m##name;
|
|
#include "mozilla/dom/BrowsingContextFieldList.h"
|
|
|
|
private:
|
|
friend struct mozilla::ipc::IPDLParamTraits<Transaction>;
|
|
};
|
|
|
|
#define MOZ_BC_FIELD(name, type) \
|
|
template <typename... Args> \
|
|
void Set##name(Args&&... aValue) { \
|
|
Transaction txn; \
|
|
txn.m##name.emplace(std::forward<Args>(aValue)...); \
|
|
txn.Commit(this); \
|
|
} \
|
|
\
|
|
type const& Get##name() const { return m##name; }
|
|
#include "mozilla/dom/BrowsingContextFieldList.h"
|
|
|
|
/**
|
|
* Information required to initialize a BrowsingContext in another process.
|
|
* This object may be serialized over IPC.
|
|
*/
|
|
struct IPCInitializer {
|
|
uint64_t mId;
|
|
|
|
// IDs are used for Parent and Opener to allow for this object to be
|
|
// deserialized before other BrowsingContext in the BrowsingContextGroup
|
|
// have been initialized.
|
|
uint64_t mParentId;
|
|
already_AddRefed<BrowsingContext> GetParent();
|
|
already_AddRefed<BrowsingContext> GetOpener();
|
|
|
|
// Include each field, skipping mOpener, as we want to handle it
|
|
// separately.
|
|
#define MOZ_BC_FIELD(name, type) type m##name;
|
|
#include "mozilla/dom/BrowsingContextFieldList.h"
|
|
};
|
|
|
|
// Create an IPCInitializer object for this BrowsingContext.
|
|
IPCInitializer GetIPCInitializer() {
|
|
IPCInitializer init;
|
|
init.mId = Id();
|
|
init.mParentId = mParent ? mParent->Id() : 0;
|
|
|
|
#define MOZ_BC_FIELD(name, type) init.m##name = m##name;
|
|
#include "mozilla/dom/BrowsingContextFieldList.h"
|
|
return init;
|
|
}
|
|
|
|
// Create a BrowsingContext object from over IPC.
|
|
static already_AddRefed<BrowsingContext> CreateFromIPC(
|
|
IPCInitializer&& aInitializer, BrowsingContextGroup* aGroup,
|
|
ContentParent* aOriginProcess);
|
|
|
|
protected:
|
|
virtual ~BrowsingContext();
|
|
BrowsingContext(BrowsingContext* aParent, BrowsingContextGroup* aGroup,
|
|
uint64_t aBrowsingContextId, Type aType);
|
|
|
|
private:
|
|
// Find the special browsing context if aName is '_self', '_parent',
|
|
// '_top', but not '_blank'. The latter is handled in FindWithName
|
|
BrowsingContext* FindWithSpecialName(const nsAString& aName);
|
|
|
|
// Find a browsing context in the subtree rooted at 'this' Doesn't
|
|
// consider the special names, '_self', '_parent', '_top', or
|
|
// '_blank'. Performs access control with regard to
|
|
// 'aRequestingContext'.
|
|
BrowsingContext* FindWithNameInSubtree(const nsAString& aName,
|
|
BrowsingContext* aRequestingContext);
|
|
|
|
// Performs access control to check that 'this' can access 'aContext'.
|
|
bool CanAccess(BrowsingContext* aContext);
|
|
|
|
bool IsActive() const;
|
|
|
|
friend class ::nsOuterWindowProxy;
|
|
friend class ::nsGlobalWindowOuter;
|
|
// Update the window proxy object that corresponds to this browsing context.
|
|
// This should be called from the window proxy object's objectMoved hook, if
|
|
// the object mWindowProxy points to was moved by the JS GC.
|
|
void UpdateWindowProxy(JSObject* obj, JSObject* old) {
|
|
if (mWindowProxy) {
|
|
MOZ_ASSERT(mWindowProxy == old);
|
|
mWindowProxy = obj;
|
|
}
|
|
}
|
|
// Clear the window proxy object that corresponds to this browsing context.
|
|
// This should be called if the window proxy object is finalized, or it can't
|
|
// reach its browsing context anymore.
|
|
void ClearWindowProxy() { mWindowProxy = nullptr; }
|
|
|
|
BrowsingContext* TopLevelBrowsingContext();
|
|
|
|
friend class Location;
|
|
friend class RemoteLocationProxy;
|
|
/**
|
|
* LocationProxy is the class for the native object stored as a private in a
|
|
* RemoteLocationProxy proxy representing a Location object in a different
|
|
* process. It forwards all operations to its BrowsingContext and aggregates
|
|
* its refcount to that BrowsingContext.
|
|
*/
|
|
class LocationProxy {
|
|
public:
|
|
MozExternalRefCountType AddRef() { return GetBrowsingContext()->AddRef(); }
|
|
MozExternalRefCountType Release() {
|
|
return GetBrowsingContext()->Release();
|
|
}
|
|
|
|
void SetHref(const nsAString& aHref, nsIPrincipal& aSubjectPrincipal,
|
|
ErrorResult& aError);
|
|
void Replace(const nsAString& aUrl, nsIPrincipal& aSubjectPrincipal,
|
|
ErrorResult& aError);
|
|
|
|
private:
|
|
friend class RemoteLocationProxy;
|
|
BrowsingContext* GetBrowsingContext() {
|
|
return reinterpret_cast<BrowsingContext*>(
|
|
uintptr_t(this) - offsetof(BrowsingContext, mLocation));
|
|
}
|
|
};
|
|
|
|
// Ensure that opener is in the same BrowsingContextGroup.
|
|
void WillSetOpener(const uint64_t& aValue, ContentParent* aSource) {
|
|
if (aValue != 0) {
|
|
RefPtr<BrowsingContext> opener = Get(aValue);
|
|
MOZ_RELEASE_ASSERT(opener && opener->Group() == Group());
|
|
}
|
|
}
|
|
|
|
// Ensure that we only set the flag on the top level browsing context.
|
|
void DidSetIsActivatedByUserGesture(ContentParent* aSource);
|
|
|
|
// Type of BrowsingContent
|
|
const Type mType;
|
|
|
|
// Unique id identifying BrowsingContext
|
|
const uint64_t mBrowsingContextId;
|
|
|
|
RefPtr<BrowsingContextGroup> mGroup;
|
|
RefPtr<BrowsingContext> mParent;
|
|
Children mChildren;
|
|
nsCOMPtr<nsIDocShell> mDocShell;
|
|
|
|
// This is not a strong reference, but using a JS::Heap for that should be
|
|
// fine. The JSObject stored in here should be a proxy with a
|
|
// nsOuterWindowProxy handler, which will update the pointer from its
|
|
// objectMoved hook and clear it from its finalize hook.
|
|
JS::Heap<JSObject*> mWindowProxy;
|
|
LocationProxy mLocation;
|
|
|
|
// Is the most recent Document in this BrowsingContext loaded within this
|
|
// process? This may be true with a null mDocShell after the Window has been
|
|
// closed.
|
|
bool mIsInProcess : 1;
|
|
};
|
|
|
|
/**
|
|
* Gets a WindowProxy object for a BrowsingContext that lives in a different
|
|
* process (creating the object if it doesn't already exist). The WindowProxy
|
|
* object will be in the compartment that aCx is currently in. This should only
|
|
* be called if aContext doesn't hold a docshell, otherwise the BrowsingContext
|
|
* lives in this process, and a same-process WindowProxy should be used (see
|
|
* nsGlobalWindowOuter). This should only be called by bindings code, ToJSValue
|
|
* is the right API to get a WindowProxy for a BrowsingContext.
|
|
*/
|
|
extern bool GetRemoteOuterWindowProxy(JSContext* aCx, BrowsingContext* aContext,
|
|
JS::MutableHandle<JSObject*> aRetVal);
|
|
|
|
typedef BrowsingContext::Transaction BrowsingContextTransaction;
|
|
typedef BrowsingContext::IPCInitializer BrowsingContextInitializer;
|
|
|
|
} // namespace dom
|
|
|
|
// Allow sending BrowsingContext objects over IPC.
|
|
namespace ipc {
|
|
template <>
|
|
struct IPDLParamTraits<dom::BrowsingContext> {
|
|
static void Write(IPC::Message* aMsg, IProtocol* aActor,
|
|
dom::BrowsingContext* aParam);
|
|
static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
|
|
IProtocol* aActor, RefPtr<dom::BrowsingContext>* aResult);
|
|
};
|
|
|
|
template <>
|
|
struct IPDLParamTraits<dom::BrowsingContext::Transaction> {
|
|
static void Write(IPC::Message* aMessage, IProtocol* aActor,
|
|
const dom::BrowsingContext::Transaction& aTransaction);
|
|
|
|
static bool Read(const IPC::Message* aMessage, PickleIterator* aIterator,
|
|
IProtocol* aActor,
|
|
dom::BrowsingContext::Transaction* aTransaction);
|
|
};
|
|
|
|
template <>
|
|
struct IPDLParamTraits<dom::BrowsingContext::IPCInitializer> {
|
|
static void Write(IPC::Message* aMessage, IProtocol* aActor,
|
|
const dom::BrowsingContext::IPCInitializer& aInitializer);
|
|
|
|
static bool Read(const IPC::Message* aMessage, PickleIterator* aIterator,
|
|
IProtocol* aActor,
|
|
dom::BrowsingContext::IPCInitializer* aInitializer);
|
|
};
|
|
|
|
} // namespace ipc
|
|
} // namespace mozilla
|
|
|
|
#endif // !defined(mozilla_dom_BrowsingContext_h)
|