summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/bindings/core/v8/window_proxy.h
blob: ce6aac22126e0a39fa28fcde56e903abd3a9d510 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
/*
 * 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/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;

// 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 GarbageCollected<WindowProxy> {
 public:
  virtual ~WindowProxy();

  virtual void Trace(Visitor*);

  void InitializeIfNeeded();

  void ClearForClose();
  void ClearForNavigation();
  void ClearForSwap();
  void ClearForV8MemoryPurge();

  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| or
  // |kV8MemoryIsForciblyPurged| 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,
  // kV8MemoryIsForciblyPurged
  //
  // * kV8MemoryIsForciblyPurged
  // The context is initialized and its frame is still attached to the DOM, but
  // the global object is detached from the global proxy in order to drop all
  // references to v8, hopefully causing all JS objects to be collected for
  // memory reduction.
  // - Possible next states: kGlobalObjectIsDetached,
  // kFrameIsDetachedAndV8MemoryIsPurged
  // Navigation can occur after V8 memory purge, and the state will transition
  // to kGlobalObjectIsDetached in that case. When frame is detached after V8
  // memory purge, the global proxy will be a weak reference and will transition
  // to kFrameIsDetachedAndV8MemoryIsPurged.
  //
  // * 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
  //
  // * kFrameIsDetachedAndV8MemoryIsPurged
  // V8 memory is purged for memory reduction and thus global object is detached
  // from the global proxy, and also frame is detached from the DOM. Like
  // kFrameIsDetached, |global_proxy_| becomes a weak 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). Could transition to kGlobalObjectIsDetached.
    kV8MemoryIsForciblyPurged,
    // The global object (inner global) is detached from the global proxy (outer
    // global).
    kGlobalObjectIsDetached,
    // The context's frame is detached from the DOM.
    kFrameIsDetached,
    // The context's frame is detached from the DOM, and global object is
    // detached from the global proxy.
    kFrameIsDetachedAndV8MemoryIsPurged,
  };

  WindowProxy(v8::Isolate*, Frame&, scoped_refptr<DOMWrapperWorld>);

  virtual void Initialize() = 0;

  virtual void DisposeContext(Lifecycle next_status, FrameReuseStatus) = 0;

  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_;
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_WINDOW_PROXY_H_