| /* |
| * Copyright (C) 2009 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_WINDOW_PROXY_H_ |
| #define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_WINDOW_PROXY_H_ |
| |
| #include "base/debug/stack_trace.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "third_party/blink/renderer/core/core_export.h" |
| #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" |
| #include "third_party/blink/renderer/platform/bindings/scoped_persistent.h" |
| #include "third_party/blink/renderer/platform/heap/handle.h" |
| #include "v8/include/v8.h" |
| |
| namespace blink { |
| |
| class DOMWindow; |
| class Frame; |
| struct WrapperTypeInfo; |
| |
| // WindowProxy implements the split window model of a window for a frame. In the |
| // HTML standard, the split window model is composed of the Window interface |
| // (the inner global object) and the WindowProxy interface (the outer global |
| // proxy). |
| // |
| // The Window interface is backed by the Blink DOMWindow C++ implementation. |
| // In contrast, the WindowProxy interface does not have a corresponding |
| // C++ implementation in Blink: the WindowProxy class defined here only manages |
| // context initialization and detach. Instead, the behavior of the WindowProxy |
| // interface is defined by JSGlobalProxy in v8 and the prototype chain set up |
| // during context initialization. |
| // |
| // ====== Inner Global Object ====== |
| // The inner global object is the global for the script environment of a Frame. |
| // Since Window and Document also have a 1:1 relationship, this means that each |
| // inner global object has an associated Document which does not change. On |
| // navigation, the new Document receives a new inner global object. |
| // |
| // However, there is one exception to the 1:1 DOMWindow:Document rule. If: |
| // - the previous Document is the initial empty document |
| // - the new Document is same-origin to the previous Document |
| // then the inner global object will be reused for the new Document. This is the |
| // only case where the associated Document of an inner global object can change. |
| // |
| // All methods and attributes defined on the Window interface are exposed via |
| // the inner global object. Global variables defined by script running in the |
| // Document also live on the inner global object. |
| // |
| // ====== Outer Global Proxy ==== |
| // The outer global proxy is reused across navigations. It implements the |
| // security checks for same-origin/cross-origin access to the Window interface. |
| // When the check passes (i.e. the access is same-origin), the access is |
| // forwarded to the inner global object of the active Document in this |
| // WindowProxy's Frame. |
| // |
| // When the security check fails, the access is delegated to the outer global |
| // proxy's cross-origin interceptors. The cross-origin interceptors may choose |
| // to return a value (if the property is exposed cross-origin) or throw an |
| // exception otherwise. |
| // |
| // Note that the cross-origin interceptors are only used for cross-origin |
| // accesses: a same-origin access to a method that is available cross-origin, |
| // such as Window.postMessage, will be delegated to the inner global object. |
| // |
| // ====== LocalWindowProxy vs RemoteWindowProxy ====== |
| // WindowProxy has two concrete subclasses: |
| // - LocalWindowProxy: implements the split window model for a frame in the same |
| // process, i.e. a LocalFrame. |
| // - RemoteWindowProxy: implements the split window model for a frame in a |
| // different process, i.e. a RemoteFrame. |
| // |
| // While having a RemoteFrame implies the frame must be cross-origin, the |
| // opposite is not true: a LocalFrame can be same-origin or cross-origin. One |
| // additional complexity (which slightly violates the HTML standard): it is |
| // possible to have SecurityOrigin::CanAccess() return true for a RemoteFrame's |
| // security origin; however, it is important to still deny access as if the |
| // frame were cross-origin. This is due to complexities in the process |
| // allocation model for renderer processes. See https://crbug.com/601629. |
| // |
| // ====== LocalWindowProxy ====== |
| // Since a LocalWindowProxy can represent a same-origin or cross-origin frame, |
| // the entire prototype chain must be available: |
| // |
| // outer global proxy |
| // -- has prototype --> inner global object |
| // -- has prototype --> Window.prototype |
| // -- has prototype --> WindowProperties [1] |
| // -- has prototype --> EventTarget.prototype |
| // -- has prototype --> Object.prototype |
| // -- has prototype --> null |
| // |
| // [1] WindowProperties is the named properties object of the Window interface. |
| // |
| // ====== RemoteWindowProxy ====== |
| // Since a RemoteWindowProxy only represents a cross-origin frame, it has a much |
| // simpler prototype chain. |
| // |
| // outer global proxy |
| // -- has prototype --> inner global object |
| // -- has prototype --> null |
| // |
| // Property access to get/set attributes and methods on the outer global proxy |
| // are redirected through the cross-origin interceptors, since any access will |
| // fail the security check, by definition. |
| // |
| // However, note that method invocations still use the inner global object as |
| // the receiver object. Blink bindings use v8::Signature to perform a strict |
| // receiver check, which requires that the FunctionTemplate used to instantiate |
| // the receiver object matches exactly. However, when creating a new context, |
| // only inner global object is instantiated using Blink's global template, so by |
| // definition, it is the only receiver object in the prototype chain that will |
| // match. |
| // |
| // |
| // ====== References ====== |
| // https://wiki.mozilla.org/Gecko:SplitWindow |
| // https://whatwg.org/C/browsers.html#the-windowproxy-exotic-object |
| class WindowProxy : public GarbageCollectedFinalized<WindowProxy> { |
| public: |
| virtual ~WindowProxy(); |
| |
| void Trace(blink::Visitor*); |
| |
| void InitializeIfNeeded(); |
| |
| void ClearForClose(); |
| void ClearForNavigation(); |
| void ClearForSwap(); |
| |
| CORE_EXPORT v8::Local<v8::Object> GlobalProxyIfNotDetached(); |
| v8::Local<v8::Object> ReleaseGlobalProxy(); |
| void SetGlobalProxy(v8::Local<v8::Object>); |
| |
| // TODO(dcheng): Temporarily exposed to avoid include cycles. Remove the need |
| // for this and remove this getter. |
| DOMWrapperWorld& World() { return *world_; } |
| |
| virtual bool IsLocal() const { return false; } |
| |
| enum FrameReuseStatus { kFrameWillNotBeReused, kFrameWillBeReused }; |
| |
| protected: |
| // Lifecycle represents the following four states. |
| // |
| // * kContextIsUninitialized |
| // We lazily initialize WindowProxies for performance reasons, and this state |
| // is "to be initialized on demand". WindowProxy basically behaves the same as |
| // |kContextIsInitialized| from a point of view of call sites. |
| // - Possible next states: kContextIsInitialized |
| // It's possible to detach the context's frame from the DOM or navigate to a |
| // new page without initializing the WindowProxy, however, there is no |
| // transition to |kFrameIsDetached| or |kGlobalObjectIsDetached| |
| // because |DisposeContext| does not change the state if the state is |
| // |kContextIsUninitialized|. In either case of a) the browsing context |
| // container is detached from the DOM or b) the page is navigated away, there |
| // must be no way for author script to access the context of |
| // |kContextIsUninitialized| because |kContextIsUninitialized| means that |
| // author script has never accessed the context, hence there must exist no |
| // reference to the context. |
| // |
| // * kContextIsInitialized |
| // The context is initialized and its frame is still attached to the DOM. |
| // - Possible next states: kFrameIsDetached, kGlobalObjectIsDetached |
| // |
| // * kGlobalObjectIsDetached |
| // The context is initialized and its frame is still attached to the DOM, but |
| // the global object(inner global)'s Document is no longer the active Document |
| // of the frame (i.e. it is being navigated away). The global object (inner |
| // global) is detached from the global proxy (outer global), but the |
| // (detached) global object and context are still alive, and author script may |
| // have references to the context. |
| // The spec does not support full web features in this state. Blink supports |
| // less things than the spec. |
| // This state is also used when swapping frames. See also |WebFrame::Swap|. |
| // - Possible next states: kContextIsInitialized |
| // This state is in the middle of navigation. Once document loading is |
| // completed, the WindowProxy will always be reinitialized, as |
| // |DocumentLoader::InstallNewDocument| ends up calling to |
| // |WindowProxy::UpdateDocument|, which reinitializes the WindowProxy. |
| // |
| // * kFrameIsDetached |
| // The context was initialized, but its frame has been detached from the DOM. |
| // Note that the context is still alive and author script may have references |
| // to the context and hence author script may run in the context. |
| // The spec does not support some of web features such as setTimeout, etc. on |
| // a detached window. Blink supports less things than the spec. |
| // V8PerContextData is cut off from the context. |global_proxy_| becomes a |
| // weak reference so that it's collectable when author script has no |
| // reference. |
| // - Possible next states: n/a |
| enum class Lifecycle { |
| // v8::Context is not yet initialized. |
| kContextIsUninitialized, |
| // v8::Context is initialized. |
| kContextIsInitialized, |
| // The global object (inner global) is detached from the global proxy (outer |
| // global). |
| kGlobalObjectIsDetached, |
| // The context's frame is detached from the DOM. |
| kFrameIsDetached, |
| }; |
| |
| WindowProxy(v8::Isolate*, Frame&, scoped_refptr<DOMWrapperWorld>); |
| |
| virtual void Initialize() = 0; |
| |
| virtual void DisposeContext(Lifecycle next_status, FrameReuseStatus) = 0; |
| |
| WARN_UNUSED_RESULT v8::Local<v8::Object> AssociateWithWrapper( |
| DOMWindow*, |
| const WrapperTypeInfo*, |
| v8::Local<v8::Object> wrapper); |
| |
| v8::Isolate* GetIsolate() const { return isolate_; } |
| Frame* GetFrame() const { return frame_.Get(); } |
| |
| #if DCHECK_IS_ON() |
| void DidAttachGlobalObject() { is_global_object_attached_ = true; } |
| void DidDetachGlobalObject() { is_global_object_attached_ = false; } |
| #endif |
| |
| private: |
| v8::Isolate* const isolate_; |
| const Member<Frame> frame_; |
| #if DCHECK_IS_ON() |
| bool is_global_object_attached_ = false; |
| #endif |
| |
| protected: |
| // TODO(dcheng): Consider making these private and using getters. |
| const scoped_refptr<DOMWrapperWorld> world_; |
| // |global_proxy_| is the root reference from Blink to v8::Context (a strong |
| // reference to the global proxy makes the entire context alive). In order to |
| // discard the v8::Context, |global_proxy_| needs to be a weak reference or |
| // to be destroyed. |
| ScopedPersistent<v8::Object> global_proxy_; |
| Lifecycle lifecycle_; |
| |
| // TODO(dcheng): Remove this temporary code for debugging |
| // https://crbug.com/728693. |
| base::debug::StackTrace initialization_stack_; |
| }; |
| |
| } // namespace blink |
| |
| #endif // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_WINDOW_PROXY_H_ |