OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 2008, 2009, 2011 Google Inc. All rights reserved. | 2 * Copyright (C) 2008, 2009, 2011 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 10 matching lines...) Expand all Loading... | |
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 */ | 29 */ |
30 | 30 |
31 #include "bindings/core/v8/WindowProxy.h" | 31 #include "bindings/core/v8/LocalWindowProxy.h" |
32 | 32 |
33 #include "bindings/core/v8/ConditionalFeatures.h" | 33 #include "bindings/core/v8/ConditionalFeatures.h" |
34 #include "bindings/core/v8/DOMWrapperWorld.h" | 34 #include "bindings/core/v8/DOMWrapperWorld.h" |
35 #include "bindings/core/v8/ScriptController.h" | 35 #include "bindings/core/v8/ScriptController.h" |
36 #include "bindings/core/v8/ToV8.h" | 36 #include "bindings/core/v8/ToV8.h" |
37 #include "bindings/core/v8/V8Binding.h" | 37 #include "bindings/core/v8/V8Binding.h" |
38 #include "bindings/core/v8/V8DOMActivityLogger.h" | 38 #include "bindings/core/v8/V8DOMActivityLogger.h" |
39 #include "bindings/core/v8/V8Document.h" | |
40 #include "bindings/core/v8/V8GCForContextDispose.h" | |
41 #include "bindings/core/v8/V8HTMLCollection.h" | |
42 #include "bindings/core/v8/V8HTMLDocument.h" | 39 #include "bindings/core/v8/V8HTMLDocument.h" |
43 #include "bindings/core/v8/V8HiddenValue.h" | 40 #include "bindings/core/v8/V8HiddenValue.h" |
44 #include "bindings/core/v8/V8Initializer.h" | 41 #include "bindings/core/v8/V8Initializer.h" |
45 #include "bindings/core/v8/V8ObjectConstructor.h" | |
46 #include "bindings/core/v8/V8PagePopupControllerBinding.h" | |
47 #include "bindings/core/v8/V8PrivateProperty.h" | 42 #include "bindings/core/v8/V8PrivateProperty.h" |
48 #include "bindings/core/v8/V8Window.h" | 43 #include "bindings/core/v8/V8Window.h" |
49 #include "core/frame/LocalFrame.h" | 44 #include "core/frame/LocalFrame.h" |
50 #include "core/frame/csp/ContentSecurityPolicy.h" | 45 #include "core/frame/csp/ContentSecurityPolicy.h" |
51 #include "core/html/DocumentNameCollection.h" | 46 #include "core/html/DocumentNameCollection.h" |
52 #include "core/html/HTMLCollection.h" | |
53 #include "core/html/HTMLIFrameElement.h" | 47 #include "core/html/HTMLIFrameElement.h" |
54 #include "core/inspector/InspectorInstrumentation.h" | |
55 #include "core/inspector/MainThreadDebugger.h" | 48 #include "core/inspector/MainThreadDebugger.h" |
56 #include "core/loader/DocumentLoader.h" | |
57 #include "core/loader/FrameLoader.h" | 49 #include "core/loader/FrameLoader.h" |
58 #include "core/loader/FrameLoaderClient.h" | 50 #include "core/loader/FrameLoaderClient.h" |
59 #include "core/origin_trials/OriginTrialContext.h" | 51 #include "core/origin_trials/OriginTrialContext.h" |
60 #include "platform/Histogram.h" | 52 #include "platform/Histogram.h" |
61 #include "platform/RuntimeEnabledFeatures.h" | 53 #include "platform/RuntimeEnabledFeatures.h" |
62 #include "platform/ScriptForbiddenScope.h" | 54 #include "platform/ScriptForbiddenScope.h" |
63 #include "platform/heap/Handle.h" | 55 #include "platform/heap/Handle.h" |
64 #include "platform/instrumentation/tracing/TraceEvent.h" | 56 #include "platform/instrumentation/tracing/TraceEvent.h" |
65 #include "platform/weborigin/SecurityOrigin.h" | 57 #include "platform/weborigin/SecurityOrigin.h" |
66 #include "public/platform/Platform.h" | |
67 #include "wtf/Assertions.h" | 58 #include "wtf/Assertions.h" |
68 #include "wtf/StringExtras.h" | |
69 #include "wtf/text/CString.h" | |
70 #include <algorithm> | |
71 #include <utility> | |
72 #include <v8-debug.h> | |
73 #include <v8.h> | 59 #include <v8.h> |
74 | 60 |
75 namespace blink { | 61 namespace blink { |
76 | 62 |
77 WindowProxy* WindowProxy::create(v8::Isolate* isolate, | 63 LocalWindowProxy::~LocalWindowProxy() { |
78 Frame* frame, | |
79 DOMWrapperWorld& world) { | |
80 return new WindowProxy(frame, &world, isolate); | |
81 } | |
82 | |
83 WindowProxy::WindowProxy(Frame* frame, | |
84 PassRefPtr<DOMWrapperWorld> world, | |
85 v8::Isolate* isolate) | |
86 : m_frame(frame), | |
87 m_isolate(isolate), | |
88 m_world(world), | |
89 m_lifecycle(Lifecycle::ContextUninitialized) {} | |
90 | |
91 WindowProxy::~WindowProxy() { | |
92 // clearForClose() or clearForNavigation() must be invoked before destruction | 64 // clearForClose() or clearForNavigation() must be invoked before destruction |
93 // starts. | 65 // starts. |
94 DCHECK(m_lifecycle != Lifecycle::ContextInitialized); | 66 DCHECK(m_lifecycle != Lifecycle::ContextInitialized); |
95 } | 67 } |
96 | 68 |
97 DEFINE_TRACE(WindowProxy) { | 69 void LocalWindowProxy::disposeContext(GlobalDetachmentBehavior behavior) { |
98 visitor->trace(m_frame); | |
99 } | |
100 | |
101 void WindowProxy::disposeContext(GlobalDetachmentBehavior behavior) { | |
102 if (m_lifecycle != Lifecycle::ContextInitialized) | 70 if (m_lifecycle != Lifecycle::ContextInitialized) |
103 return; | 71 return; |
104 | 72 |
105 ScriptState::Scope scope(m_scriptState.get()); | 73 ScriptState::Scope scope(m_scriptState.get()); |
106 v8::Local<v8::Context> context = m_scriptState->context(); | 74 v8::Local<v8::Context> context = m_scriptState->context(); |
107 if (m_frame->isLocalFrame()) { | 75 // The embedder could run arbitrary code in response to the |
108 LocalFrame* frame = toLocalFrame(m_frame); | 76 // willReleaseScriptContext callback, so all disposing should happen after |
109 // The embedder could run arbitrary code in response to the | 77 // it returns. |
110 // willReleaseScriptContext callback, so all disposing should happen after | 78 frame()->loader().client()->willReleaseScriptContext(context, |
111 // it returns. | |
112 frame->loader().client()->willReleaseScriptContext(context, | |
113 m_world->worldId()); | 79 m_world->worldId()); |
114 MainThreadDebugger::instance()->contextWillBeDestroyed(m_scriptState.get()); | 80 MainThreadDebugger::instance()->contextWillBeDestroyed(m_scriptState.get()); |
115 } | |
116 | 81 |
117 if (behavior == DetachGlobal) { | 82 WindowProxy::disposeContext(behavior); |
118 // Clean up state on the global proxy, which will be reused. | |
119 if (!m_globalProxy.isEmpty()) { | |
120 // TODO(yukishiino): This DCHECK failed on Canary (M57) and Dev (M56). | |
121 // We need to figure out why m_globalProxy != context->Global(). | |
122 DCHECK(m_globalProxy == context->Global()); | |
123 DCHECK_EQ(toScriptWrappable(context->Global()), | |
124 toScriptWrappable( | |
125 context->Global()->GetPrototype().As<v8::Object>())); | |
126 m_globalProxy.get().SetWrapperClassId(0); | |
127 } | |
128 V8DOMWrapper::clearNativeInfo(m_isolate, context->Global()); | |
129 m_scriptState->detachGlobalObject(); | |
130 } | |
131 | |
132 m_scriptState->disposePerContextData(); | |
133 | |
134 // It's likely that disposing the context has created a lot of | |
135 // garbage. Notify V8 about this so it'll have a chance of cleaning | |
136 // it up when idle. | |
137 V8GCForContextDispose::instance().notifyContextDisposed( | |
138 m_frame->isMainFrame()); | |
139 | |
140 DCHECK(m_lifecycle == Lifecycle::ContextInitialized); | |
141 m_lifecycle = Lifecycle::ContextDetached; | |
142 } | 83 } |
143 | 84 |
144 void WindowProxy::clearForClose() { | 85 void LocalWindowProxy::initialize() { |
145 disposeContext(DoNotDetachGlobal); | 86 TRACE_EVENT1("v8", "LocalWindowProxy::initialize", "isMainWindow", |
146 } | 87 frame()->isMainFrame()); |
147 | |
148 void WindowProxy::clearForNavigation() { | |
149 disposeContext(DetachGlobal); | |
150 } | |
151 | |
152 v8::Local<v8::Object> WindowProxy::globalIfNotDetached() { | |
153 if (m_lifecycle == Lifecycle::ContextInitialized) { | |
154 DCHECK(m_scriptState->contextIsValid()); | |
155 DCHECK(m_globalProxy == m_scriptState->context()->Global()); | |
156 return m_globalProxy.newLocal(m_isolate); | |
157 } | |
158 return v8::Local<v8::Object>(); | |
159 } | |
160 | |
161 v8::Local<v8::Object> WindowProxy::releaseGlobal() { | |
162 DCHECK(m_lifecycle != Lifecycle::ContextInitialized); | |
163 // Make sure the global object was detached from the proxy by calling | |
164 // clearForNavigation(). | |
165 if (m_lifecycle == Lifecycle::ContextDetached) | |
166 ASSERT(m_scriptState->isGlobalObjectDetached()); | |
167 | |
168 v8::Local<v8::Object> global = m_globalProxy.newLocal(m_isolate); | |
169 m_globalProxy.clear(); | |
170 return global; | |
171 } | |
172 | |
173 void WindowProxy::setGlobal(v8::Local<v8::Object> global) { | |
174 m_globalProxy.set(m_isolate, global); | |
175 | |
176 // Initialize the window proxy now, to re-establish the connection between | |
177 // the global object and the v8::Context. This is really only needed for a | |
178 // RemoteDOMWindow, since it has no scripting environment of its own. | |
179 // Without this, existing script references to a swapped in RemoteDOMWindow | |
180 // would be broken until that RemoteDOMWindow was vended again through an | |
181 // interface like window.frames. | |
182 initializeIfNeeded(); | |
183 } | |
184 | |
185 // Create a new environment and setup the global object. | |
186 // | |
187 // The global object corresponds to a DOMWindow instance. However, to | |
188 // allow properties of the JS DOMWindow instance to be shadowed, we | |
189 // use a shadow object as the global object and use the JS DOMWindow | |
190 // instance as the prototype for that shadow object. The JS DOMWindow | |
191 // instance is undetectable from JavaScript code because the __proto__ | |
192 // accessors skip that object. | |
193 // | |
194 // The shadow object and the DOMWindow instance are seen as one object | |
195 // from JavaScript. The JavaScript object that corresponds to a | |
196 // DOMWindow instance is the shadow object. When mapping a DOMWindow | |
197 // instance to a V8 object, we return the shadow object. | |
198 // | |
199 // To implement split-window, see | |
200 // 1) https://bugs.webkit.org/show_bug.cgi?id=17249 | |
201 // 2) https://wiki.mozilla.org/Gecko:SplitWindow | |
202 // 3) https://bugzilla.mozilla.org/show_bug.cgi?id=296639 | |
203 // we need to split the shadow object further into two objects: | |
204 // an outer window and an inner window. The inner window is the hidden | |
205 // prototype of the outer window. The inner window is the default | |
206 // global object of the context. A variable declared in the global | |
207 // scope is a property of the inner window. | |
208 // | |
209 // The outer window sticks to a LocalFrame, it is exposed to JavaScript | |
210 // via window.window, window.self, window.parent, etc. The outer window | |
211 // has a security token which is the domain. The outer window cannot | |
212 // have its own properties. window.foo = 'x' is delegated to the | |
213 // inner window. | |
214 // | |
215 // When a frame navigates to a new page, the inner window is cut off | |
216 // the outer window, and the outer window identify is preserved for | |
217 // the frame. However, a new inner window is created for the new page. | |
218 // If there are JS code holds a closure to the old inner window, | |
219 // it won't be able to reach the outer window via its global object. | |
220 void WindowProxy::initializeIfNeeded() { | |
221 // TODO(haraken): It is wrong to re-initialize an already detached window | |
222 // proxy. This must be 'if(m_lifecycle == Lifecycle::ContextUninitialized)'. | |
223 if (m_lifecycle != Lifecycle::ContextInitialized) { | |
224 initialize(); | |
225 if (m_world->isMainWorld() && m_frame->isLocalFrame()) | |
226 toLocalFrame(m_frame)->loader().dispatchDidClearWindowObjectInMainWorld(); | |
227 } | |
228 } | |
229 | |
230 void WindowProxy::initialize() { | |
231 TRACE_EVENT1("v8", "WindowProxy::initialize", "isMainWindow", | |
232 m_frame->isMainFrame()); | |
233 SCOPED_BLINK_UMA_HISTOGRAM_TIMER( | 88 SCOPED_BLINK_UMA_HISTOGRAM_TIMER( |
234 m_frame->isMainFrame() ? "Blink.Binding.InitializeMainWindowProxy" | 89 frame()->isMainFrame() ? "Blink.Binding.InitializeMainWindowProxy" |
235 : "Blink.Binding.InitializeNonMainWindowProxy"); | 90 : "Blink.Binding.InitializeNonMainWindowProxy"); |
236 | 91 |
237 ScriptForbiddenScope::AllowUserAgentScript allowScript; | 92 ScriptForbiddenScope::AllowUserAgentScript allowScript; |
238 | 93 |
239 v8::HandleScope handleScope(m_isolate); | 94 v8::HandleScope handleScope(isolate()); |
240 | 95 |
241 createContext(); | 96 createContext(); |
242 | 97 |
243 ScriptState::Scope scope(m_scriptState.get()); | 98 ScriptState::Scope scope(m_scriptState.get()); |
244 v8::Local<v8::Context> context = m_scriptState->context(); | 99 v8::Local<v8::Context> context = m_scriptState->context(); |
245 if (m_globalProxy.isEmpty()) { | 100 if (m_globalProxy.isEmpty()) { |
246 m_globalProxy.set(m_isolate, context->Global()); | 101 m_globalProxy.set(isolate(), context->Global()); |
247 CHECK(!m_globalProxy.isEmpty()); | 102 CHECK(!m_globalProxy.isEmpty()); |
248 } | 103 } |
249 | 104 |
250 setupWindowPrototypeChain(); | 105 setupWindowPrototypeChain(); |
251 | 106 |
252 SecurityOrigin* origin = 0; | 107 SecurityOrigin* origin = 0; |
253 if (m_world->isMainWorld()) { | 108 if (m_world->isMainWorld()) { |
254 // ActivityLogger for main world is updated within updateDocument(). | 109 // ActivityLogger for main world is updated within updateDocument(). |
255 updateDocument(); | 110 updateDocument(); |
256 origin = m_frame->securityContext()->getSecurityOrigin(); | 111 origin = frame()->document()->getSecurityOrigin(); |
257 // FIXME: Can this be removed when CSP moves to browser? | 112 // FIXME: Can this be removed when CSP moves to browser? |
258 ContentSecurityPolicy* csp = | 113 ContentSecurityPolicy* csp = frame()->document()->contentSecurityPolicy(); |
259 m_frame->securityContext()->contentSecurityPolicy(); | |
260 context->AllowCodeGenerationFromStrings( | 114 context->AllowCodeGenerationFromStrings( |
261 csp->allowEval(0, ContentSecurityPolicy::SuppressReport)); | 115 csp->allowEval(0, ContentSecurityPolicy::SuppressReport)); |
262 context->SetErrorMessageForCodeGenerationFromStrings( | 116 context->SetErrorMessageForCodeGenerationFromStrings( |
263 v8String(m_isolate, csp->evalDisabledErrorMessage())); | 117 v8String(isolate(), csp->evalDisabledErrorMessage())); |
264 } else { | 118 } else { |
265 updateActivityLogger(); | 119 updateActivityLogger(); |
266 origin = m_world->isolatedWorldSecurityOrigin(); | 120 origin = m_world->isolatedWorldSecurityOrigin(); |
267 setSecurityToken(origin); | 121 setSecurityToken(origin); |
268 } | 122 } |
269 | 123 |
270 if (m_frame->isLocalFrame()) { | 124 MainThreadDebugger::instance()->contextCreated(m_scriptState.get(), frame(), |
271 LocalFrame* frame = toLocalFrame(m_frame); | 125 origin); |
272 MainThreadDebugger::instance()->contextCreated(m_scriptState.get(), frame, | 126 frame()->loader().client()->didCreateScriptContext( |
273 origin); | 127 context, m_world->extensionGroup(), m_world->worldId()); |
274 frame->loader().client()->didCreateScriptContext( | |
275 context, m_world->extensionGroup(), m_world->worldId()); | |
276 } | |
277 // If conditional features for window have been queued before the V8 context | 128 // If conditional features for window have been queued before the V8 context |
278 // was ready, then inject them into the context now | 129 // was ready, then inject them into the context now |
279 if (m_world->isMainWorld()) { | 130 if (m_world->isMainWorld()) { |
280 installPendingConditionalFeaturesOnWindow(m_scriptState.get()); | 131 installPendingConditionalFeaturesOnWindow(m_scriptState.get()); |
281 } | 132 } |
133 | |
134 if (m_world->isMainWorld()) | |
135 frame()->loader().dispatchDidClearWindowObjectInMainWorld(); | |
dcheng
2017/01/11 08:35:16
Moved from initializeIfNeeded(), since this logic
| |
282 } | 136 } |
283 | 137 |
284 void WindowProxy::createContext() { | 138 void LocalWindowProxy::createContext() { |
285 // Create a new v8::Context with the window object as the global object | 139 // Create a new v8::Context with the window object as the global object |
286 // (aka the inner global). Reuse the global proxy object (aka the outer | 140 // (aka the inner global). Reuse the global proxy object (aka the outer |
287 // global) if it already exists. See the comments in | 141 // global) if it already exists. See the comments in |
288 // setupWindowPrototypeChain for the structure of the prototype chain of | 142 // setupWindowPrototypeChain for the structure of the prototype chain of |
289 // the global object. | 143 // the global object. |
290 v8::Local<v8::ObjectTemplate> globalTemplate = | 144 v8::Local<v8::ObjectTemplate> globalTemplate = |
291 V8Window::domTemplate(m_isolate, *m_world)->InstanceTemplate(); | 145 V8Window::domTemplate(isolate(), *m_world)->InstanceTemplate(); |
292 CHECK(!globalTemplate.IsEmpty()); | 146 CHECK(!globalTemplate.IsEmpty()); |
293 | 147 |
294 // FIXME: It's not clear what the right thing to do for remote frames is. | 148 // FIXME: It's not clear what the right thing to do for remote frames is. |
295 // The extensions registered don't generally seem to make sense for remote | 149 // The extensions registered don't generally seem to make sense for remote |
296 // frames, so skip it for now. | 150 // frames, so skip it for now. |
297 Vector<const char*> extensionNames; | 151 Vector<const char*> extensionNames; |
298 if (m_frame->isLocalFrame()) { | 152 // Dynamically tell v8 about our extensions now. |
299 LocalFrame* frame = toLocalFrame(m_frame); | 153 const V8Extensions& extensions = ScriptController::registeredExtensions(); |
300 // Dynamically tell v8 about our extensions now. | 154 extensionNames.reserveInitialCapacity(extensions.size()); |
301 const V8Extensions& extensions = ScriptController::registeredExtensions(); | 155 int extensionGroup = m_world->extensionGroup(); |
302 extensionNames.reserveInitialCapacity(extensions.size()); | 156 int worldId = m_world->worldId(); |
303 int extensionGroup = m_world->extensionGroup(); | 157 for (const auto* extension : extensions) { |
304 int worldId = m_world->worldId(); | 158 if (!frame()->loader().client()->allowScriptExtension( |
305 for (const auto* extension : extensions) { | 159 extension->name(), extensionGroup, worldId)) |
306 if (!frame->loader().client()->allowScriptExtension( | 160 continue; |
307 extension->name(), extensionGroup, worldId)) | |
308 continue; | |
309 | 161 |
310 extensionNames.push_back(extension->name()); | 162 extensionNames.push_back(extension->name()); |
311 } | |
312 } | 163 } |
313 v8::ExtensionConfiguration extensionConfiguration(extensionNames.size(), | 164 v8::ExtensionConfiguration extensionConfiguration(extensionNames.size(), |
314 extensionNames.data()); | 165 extensionNames.data()); |
315 | 166 |
316 v8::Local<v8::Context> context; | 167 v8::Local<v8::Context> context; |
317 { | 168 { |
318 V8PerIsolateData::UseCounterDisabledScope useCounterDisabled( | 169 V8PerIsolateData::UseCounterDisabledScope useCounterDisabled( |
319 V8PerIsolateData::from(m_isolate)); | 170 V8PerIsolateData::from(isolate())); |
320 context = | 171 context = |
321 v8::Context::New(m_isolate, &extensionConfiguration, globalTemplate, | 172 v8::Context::New(isolate(), &extensionConfiguration, globalTemplate, |
322 m_globalProxy.newLocal(m_isolate)); | 173 m_globalProxy.newLocal(isolate())); |
323 } | 174 } |
324 CHECK(!context.IsEmpty()); | 175 CHECK(!context.IsEmpty()); |
325 | 176 |
326 m_scriptState = ScriptState::create(context, m_world); | 177 m_scriptState = ScriptState::create(context, m_world); |
327 | 178 |
328 // TODO(haraken): Currently we cannot enable the following DCHECK because | 179 // TODO(haraken): Currently we cannot enable the following DCHECK because |
329 // an already detached window proxy can be re-initialized. This is wrong. | 180 // an already detached window proxy can be re-initialized. This is wrong. |
330 // DCHECK(m_lifecycle == Lifecycle::ContextUninitialized); | 181 // DCHECK(m_lifecycle == Lifecycle::ContextUninitialized); |
331 m_lifecycle = Lifecycle::ContextInitialized; | 182 m_lifecycle = Lifecycle::ContextInitialized; |
332 DCHECK(m_scriptState->contextIsValid()); | 183 DCHECK(m_scriptState->contextIsValid()); |
333 } | 184 } |
334 | 185 |
335 void WindowProxy::setupWindowPrototypeChain() { | 186 void LocalWindowProxy::updateDocumentProperty() { |
336 // Associate the window wrapper object and its prototype chain with the | |
337 // corresponding native DOMWindow object. | |
338 // The full structure of the global object's prototype chain is as follows: | |
339 // | |
340 // global proxy object [1] | |
341 // -- has prototype --> global object (window wrapper object) [2] | |
342 // -- has prototype --> Window.prototype | |
343 // -- has prototype --> WindowProperties [3] | |
344 // -- has prototype --> EventTarget.prototype | |
345 // -- has prototype --> Object.prototype | |
346 // -- has prototype --> null | |
347 // | |
348 // [1] Global proxy object is as known as "outer global object". It's an | |
349 // empty object and remains after navigation. When navigated, points to | |
350 // a different global object as the prototype object. | |
351 // [2] Global object is as known as "inner global object" or "window wrapper | |
352 // object". The prototype chain between global proxy object and global | |
353 // object is NOT observable from user JavaScript code. All other | |
354 // prototype chains are observable. Global proxy object and global object | |
355 // together appear to be the same single JavaScript object. See also: | |
356 // https://wiki.mozilla.org/Gecko:SplitWindow | |
357 // global object (= window wrapper object) provides most of Window's DOM | |
358 // attributes and operations. Also global variables defined by user | |
359 // JavaScript are placed on this object. When navigated, a new global | |
360 // object is created together with a new v8::Context, but the global proxy | |
361 // object doesn't change. | |
362 // [3] WindowProperties is a named properties object of Window interface. | |
363 | |
364 DOMWindow* window = m_frame->domWindow(); | |
365 const WrapperTypeInfo* wrapperTypeInfo = window->wrapperTypeInfo(); | |
366 v8::Local<v8::Context> context = m_scriptState->context(); | |
367 | |
368 // The global proxy object. Note this is not the global object. | |
369 v8::Local<v8::Object> globalProxy = context->Global(); | |
370 CHECK(m_globalProxy == globalProxy); | |
371 V8DOMWrapper::setNativeInfo(m_isolate, globalProxy, wrapperTypeInfo, window); | |
372 // Mark the handle to be traced by Oilpan, since the global proxy has a | |
373 // reference to the DOMWindow. | |
374 m_globalProxy.get().SetWrapperClassId(wrapperTypeInfo->wrapperClassId); | |
375 | |
376 // The global object, aka window wrapper object. | |
377 v8::Local<v8::Object> windowWrapper = | |
378 globalProxy->GetPrototype().As<v8::Object>(); | |
379 windowWrapper = V8DOMWrapper::associateObjectWithWrapper( | |
380 m_isolate, window, wrapperTypeInfo, windowWrapper); | |
381 | |
382 // The prototype object of Window interface. | |
383 v8::Local<v8::Object> windowPrototype = | |
384 windowWrapper->GetPrototype().As<v8::Object>(); | |
385 CHECK(!windowPrototype.IsEmpty()); | |
386 V8DOMWrapper::setNativeInfo(m_isolate, windowPrototype, wrapperTypeInfo, | |
387 window); | |
388 | |
389 // The named properties object of Window interface. | |
390 v8::Local<v8::Object> windowProperties = | |
391 windowPrototype->GetPrototype().As<v8::Object>(); | |
392 CHECK(!windowProperties.IsEmpty()); | |
393 V8DOMWrapper::setNativeInfo(m_isolate, windowProperties, wrapperTypeInfo, | |
394 window); | |
395 | |
396 // TODO(keishi): Remove installPagePopupController and implement | |
397 // PagePopupController in another way. | |
398 V8PagePopupControllerBinding::installPagePopupController(context, | |
399 windowWrapper); | |
400 } | |
401 | |
402 void WindowProxy::updateDocumentProperty() { | |
403 DCHECK(m_world->isMainWorld()); | 187 DCHECK(m_world->isMainWorld()); |
404 | 188 |
405 if (m_frame->isRemoteFrame()) | |
406 return; | |
407 | |
408 ScriptState::Scope scope(m_scriptState.get()); | 189 ScriptState::Scope scope(m_scriptState.get()); |
409 v8::Local<v8::Context> context = m_scriptState->context(); | 190 v8::Local<v8::Context> context = m_scriptState->context(); |
410 LocalFrame* frame = toLocalFrame(m_frame); | |
411 v8::Local<v8::Value> documentWrapper = | 191 v8::Local<v8::Value> documentWrapper = |
412 ToV8(frame->document(), context->Global(), m_isolate); | 192 ToV8(frame()->document(), context->Global(), isolate()); |
413 DCHECK(documentWrapper->IsObject()); | 193 DCHECK(documentWrapper->IsObject()); |
414 // Update the cached accessor for window.document. | 194 // Update the cached accessor for window.document. |
415 CHECK(V8PrivateProperty::getWindowDocumentCachedAccessor(m_isolate).set( | 195 CHECK(V8PrivateProperty::getWindowDocumentCachedAccessor(isolate()).set( |
416 context, context->Global(), documentWrapper)); | 196 context, context->Global(), documentWrapper)); |
417 } | 197 } |
418 | 198 |
419 void WindowProxy::updateActivityLogger() { | 199 void LocalWindowProxy::updateActivityLogger() { |
420 m_scriptState->perContextData()->setActivityLogger( | 200 m_scriptState->perContextData()->setActivityLogger( |
421 V8DOMActivityLogger::activityLogger( | 201 V8DOMActivityLogger::activityLogger( |
422 m_world->worldId(), | 202 m_world->worldId(), |
423 m_frame->isLocalFrame() && toLocalFrame(m_frame)->document() | 203 frame()->document() ? frame()->document()->baseURI() : KURL())); |
424 ? toLocalFrame(m_frame)->document()->baseURI() | |
425 : KURL())); | |
426 } | 204 } |
427 | 205 |
428 void WindowProxy::setSecurityToken(SecurityOrigin* origin) { | 206 void LocalWindowProxy::setSecurityToken(SecurityOrigin* origin) { |
429 // If two tokens are equal, then the SecurityOrigins canAccess each other. | 207 // If two tokens are equal, then the SecurityOrigins canAccess each other. |
430 // If two tokens are not equal, then we have to call canAccess. | 208 // If two tokens are not equal, then we have to call canAccess. |
431 // Note: we can't use the HTTPOrigin if it was set from the DOM. | 209 // Note: we can't use the HTTPOrigin if it was set from the DOM. |
432 String token; | 210 String token; |
433 // There are two situations where v8 needs to do a full canAccess check, | 211 // If document.domain is modified, v8 needs to do a full canAccess check, |
434 // so set an empty security token instead: | 212 // so always use an empty security token in that case. |
435 // - document.domain was modified | 213 bool delaySet = m_world->isMainWorld() && origin->domainWasSetInDOM(); |
436 // - the frame is remote | |
437 bool delaySet = m_frame->isRemoteFrame() || | |
438 (m_world->isMainWorld() && origin->domainWasSetInDOM()); | |
439 if (origin && !delaySet) | 214 if (origin && !delaySet) |
440 token = origin->toString(); | 215 token = origin->toString(); |
441 | 216 |
442 // An empty or "null" token means we always have to call | 217 // An empty or "null" token means we always have to call |
443 // canAccess. The toString method on securityOrigins returns the | 218 // canAccess. The toString method on securityOrigins returns the |
444 // string "null" for empty security origins and for security | 219 // string "null" for empty security origins and for security |
445 // origins that should only allow access to themselves. In this | 220 // origins that should only allow access to themselves. In this |
446 // case, we use the global object as the security token to avoid | 221 // case, we use the global object as the security token to avoid |
447 // calling canAccess when a script accesses its own objects. | 222 // calling canAccess when a script accesses its own objects. |
448 v8::HandleScope handleScope(m_isolate); | 223 v8::HandleScope handleScope(isolate()); |
449 v8::Local<v8::Context> context = m_scriptState->context(); | 224 v8::Local<v8::Context> context = m_scriptState->context(); |
450 if (token.isEmpty() || token == "null") { | 225 if (token.isEmpty() || token == "null") { |
451 context->UseDefaultSecurityToken(); | 226 context->UseDefaultSecurityToken(); |
452 return; | 227 return; |
453 } | 228 } |
454 | 229 |
455 if (m_world->isIsolatedWorld()) { | 230 if (m_world->isIsolatedWorld()) { |
456 SecurityOrigin* frameSecurityOrigin = | 231 SecurityOrigin* frameSecurityOrigin = |
457 m_frame->securityContext()->getSecurityOrigin(); | 232 frame()->document()->getSecurityOrigin(); |
458 String frameSecurityToken = frameSecurityOrigin->toString(); | 233 String frameSecurityToken = frameSecurityOrigin->toString(); |
459 // We need to check the return value of domainWasSetInDOM() on the | 234 // We need to check the return value of domainWasSetInDOM() on the |
460 // frame's SecurityOrigin because, if that's the case, only | 235 // frame's SecurityOrigin because, if that's the case, only |
461 // SecurityOrigin::m_domain would have been modified. | 236 // SecurityOrigin::m_domain would have been modified. |
462 // m_domain is not used by SecurityOrigin::toString(), so we would end | 237 // m_domain is not used by SecurityOrigin::toString(), so we would end |
463 // up generating the same token that was already set. | 238 // up generating the same token that was already set. |
464 if (frameSecurityOrigin->domainWasSetInDOM() || | 239 if (frameSecurityOrigin->domainWasSetInDOM() || |
465 frameSecurityToken.isEmpty() || frameSecurityToken == "null") { | 240 frameSecurityToken.isEmpty() || frameSecurityToken == "null") { |
466 context->UseDefaultSecurityToken(); | 241 context->UseDefaultSecurityToken(); |
467 return; | 242 return; |
468 } | 243 } |
469 token = frameSecurityToken + token; | 244 token = frameSecurityToken + token; |
470 } | 245 } |
471 | 246 |
472 // NOTE: V8 does identity comparison in fast path, must use a symbol | 247 // NOTE: V8 does identity comparison in fast path, must use a symbol |
473 // as the security token. | 248 // as the security token. |
474 context->SetSecurityToken(v8AtomicString(m_isolate, token)); | 249 context->SetSecurityToken(v8AtomicString(isolate(), token)); |
475 } | 250 } |
476 | 251 |
477 void WindowProxy::updateDocument() { | 252 void LocalWindowProxy::updateDocument() { |
478 DCHECK(m_world->isMainWorld()); | 253 DCHECK(m_world->isMainWorld()); |
479 // For an uninitialized main window proxy, there's nothing we need | 254 // For an uninitialized main window proxy, there's nothing we need |
480 // to update. The update is done when the window proxy gets initialized later. | 255 // to update. The update is done when the window proxy gets initialized later. |
481 if (m_lifecycle == Lifecycle::ContextUninitialized) | 256 if (m_lifecycle == Lifecycle::ContextUninitialized) |
482 return; | 257 return; |
483 // TODO(yukishiino): Is it okay to not update document when the context | 258 // TODO(yukishiino): Is it okay to not update document when the context |
484 // is detached? It's not trivial to fix this because udpateDocumentProperty | 259 // is detached? It's not trivial to fix this because udpateDocumentProperty |
485 // requires a not-yet-detached context to instantiate a document wrapper. | 260 // requires a not-yet-detached context to instantiate a document wrapper. |
486 if (m_lifecycle == Lifecycle::ContextDetached) | 261 if (m_lifecycle == Lifecycle::ContextDetached) |
487 return; | 262 return; |
488 | 263 |
489 updateActivityLogger(); | 264 updateActivityLogger(); |
490 updateDocumentProperty(); | 265 updateDocumentProperty(); |
491 updateSecurityOrigin(m_frame->securityContext()->getSecurityOrigin()); | 266 updateSecurityOrigin(frame()->document()->getSecurityOrigin()); |
492 } | 267 } |
493 | 268 |
494 static v8::Local<v8::Value> getNamedProperty( | 269 static v8::Local<v8::Value> getNamedProperty( |
495 HTMLDocument* htmlDocument, | 270 HTMLDocument* htmlDocument, |
496 const AtomicString& key, | 271 const AtomicString& key, |
497 v8::Local<v8::Object> creationContext, | 272 v8::Local<v8::Object> creationContext, |
498 v8::Isolate* isolate) { | 273 v8::Isolate* isolate) { |
499 if (!htmlDocument->hasNamedItem(key) && !htmlDocument->hasExtraNamedItem(key)) | 274 if (!htmlDocument->hasNamedItem(key) && !htmlDocument->hasExtraNamedItem(key)) |
500 return v8Undefined(); | 275 return v8Undefined(); |
501 | 276 |
502 DocumentNameCollection* items = htmlDocument->documentNamedItems(key); | 277 DocumentNameCollection* items = htmlDocument->documentNamedItems(key); |
503 if (items->isEmpty()) | 278 if (items->isEmpty()) |
504 return v8Undefined(); | 279 return v8Undefined(); |
505 | 280 |
506 if (items->hasExactlyOneItem()) { | 281 if (items->hasExactlyOneItem()) { |
507 HTMLElement* element = items->item(0); | 282 HTMLElement* element = items->item(0); |
508 ASSERT(element); | 283 DCHECK(element); |
509 Frame* frame = isHTMLIFrameElement(*element) | 284 Frame* frame = isHTMLIFrameElement(*element) |
510 ? toHTMLIFrameElement(*element).contentFrame() | 285 ? toHTMLIFrameElement(*element).contentFrame() |
511 : 0; | 286 : 0; |
512 if (frame) | 287 if (frame) |
513 return ToV8(frame->domWindow(), creationContext, isolate); | 288 return ToV8(frame->domWindow(), creationContext, isolate); |
514 return ToV8(element, creationContext, isolate); | 289 return ToV8(element, creationContext, isolate); |
515 } | 290 } |
516 return ToV8(items, creationContext, isolate); | 291 return ToV8(items, creationContext, isolate); |
517 } | 292 } |
518 | 293 |
519 static void getter(v8::Local<v8::Name> property, | 294 static void getter(v8::Local<v8::Name> property, |
520 const v8::PropertyCallbackInfo<v8::Value>& info) { | 295 const v8::PropertyCallbackInfo<v8::Value>& info) { |
521 if (!property->IsString()) | 296 if (!property->IsString()) |
522 return; | 297 return; |
523 // FIXME: Consider passing StringImpl directly. | 298 // FIXME: Consider passing StringImpl directly. |
524 AtomicString name = toCoreAtomicString(property.As<v8::String>()); | 299 AtomicString name = toCoreAtomicString(property.As<v8::String>()); |
525 HTMLDocument* htmlDocument = V8HTMLDocument::toImpl(info.Holder()); | 300 HTMLDocument* htmlDocument = V8HTMLDocument::toImpl(info.Holder()); |
526 ASSERT(htmlDocument); | 301 DCHECK(htmlDocument); |
527 v8::Local<v8::Value> result = | 302 v8::Local<v8::Value> result = |
528 getNamedProperty(htmlDocument, name, info.Holder(), info.GetIsolate()); | 303 getNamedProperty(htmlDocument, name, info.Holder(), info.GetIsolate()); |
529 if (!result.IsEmpty()) { | 304 if (!result.IsEmpty()) { |
530 v8SetReturnValue(info, result); | 305 v8SetReturnValue(info, result); |
531 return; | 306 return; |
532 } | 307 } |
533 v8::Local<v8::Value> value; | 308 v8::Local<v8::Value> value; |
534 if (info.Holder() | 309 if (info.Holder() |
535 ->GetRealNamedPropertyInPrototypeChain( | 310 ->GetRealNamedPropertyInPrototypeChain( |
536 info.GetIsolate()->GetCurrentContext(), property.As<v8::String>()) | 311 info.GetIsolate()->GetCurrentContext(), property.As<v8::String>()) |
537 .ToLocal(&value)) | 312 .ToLocal(&value)) |
538 v8SetReturnValue(info, value); | 313 v8SetReturnValue(info, value); |
539 } | 314 } |
540 | 315 |
541 void WindowProxy::namedItemAdded(HTMLDocument* document, | 316 void LocalWindowProxy::namedItemAdded(HTMLDocument* document, |
542 const AtomicString& name) { | 317 const AtomicString& name) { |
543 DCHECK(m_world->isMainWorld()); | 318 DCHECK(m_world->isMainWorld()); |
544 | 319 |
545 // Context must be initialized before this point. | 320 // Context must be initialized before this point. |
546 DCHECK(m_lifecycle >= Lifecycle::ContextInitialized); | 321 DCHECK(m_lifecycle >= Lifecycle::ContextInitialized); |
547 // TODO(yukishiino): Is it okay to not update named properties | 322 // TODO(yukishiino): Is it okay to not update named properties |
548 // after the context gets detached? | 323 // after the context gets detached? |
549 if (m_lifecycle == Lifecycle::ContextDetached) | 324 if (m_lifecycle == Lifecycle::ContextDetached) |
550 return; | 325 return; |
551 | 326 |
552 ScriptState::Scope scope(m_scriptState.get()); | 327 ScriptState::Scope scope(m_scriptState.get()); |
553 v8::Local<v8::Object> documentWrapper = | 328 v8::Local<v8::Object> documentWrapper = |
554 m_world->domDataStore().get(document, m_isolate); | 329 m_world->domDataStore().get(document, isolate()); |
555 // TODO(yukishiino,peria): We should check if the own property with the same | 330 // TODO(yukishiino,peria): We should check if the own property with the same |
556 // name already exists or not, and if it exists, we shouldn't define a new | 331 // name already exists or not, and if it exists, we shouldn't define a new |
557 // accessor property (it fails). | 332 // accessor property (it fails). |
558 documentWrapper->SetAccessor(m_isolate->GetCurrentContext(), | 333 documentWrapper->SetAccessor(isolate()->GetCurrentContext(), |
559 v8String(m_isolate, name), getter); | 334 v8String(isolate(), name), getter); |
560 } | 335 } |
561 | 336 |
562 void WindowProxy::namedItemRemoved(HTMLDocument* document, | 337 void LocalWindowProxy::namedItemRemoved(HTMLDocument* document, |
563 const AtomicString& name) { | 338 const AtomicString& name) { |
564 DCHECK(m_world->isMainWorld()); | 339 DCHECK(m_world->isMainWorld()); |
565 | 340 |
566 // Context must be initialized before this point. | 341 // Context must be initialized before this point. |
567 DCHECK(m_lifecycle >= Lifecycle::ContextInitialized); | 342 DCHECK(m_lifecycle >= Lifecycle::ContextInitialized); |
568 // TODO(yukishiino): Is it okay to not update named properties | 343 // TODO(yukishiino): Is it okay to not update named properties |
569 // after the context gets detached? | 344 // after the context gets detached? |
570 if (m_lifecycle == Lifecycle::ContextDetached) | 345 if (m_lifecycle == Lifecycle::ContextDetached) |
571 return; | 346 return; |
572 | 347 |
573 if (document->hasNamedItem(name) || document->hasExtraNamedItem(name)) | 348 if (document->hasNamedItem(name) || document->hasExtraNamedItem(name)) |
574 return; | 349 return; |
575 ScriptState::Scope scope(m_scriptState.get()); | 350 ScriptState::Scope scope(m_scriptState.get()); |
576 v8::Local<v8::Object> documentWrapper = | 351 v8::Local<v8::Object> documentWrapper = |
577 m_world->domDataStore().get(document, m_isolate); | 352 m_world->domDataStore().get(document, isolate()); |
578 documentWrapper | 353 documentWrapper |
579 ->Delete(m_isolate->GetCurrentContext(), v8String(m_isolate, name)) | 354 ->Delete(isolate()->GetCurrentContext(), v8String(isolate(), name)) |
580 .ToChecked(); | 355 .ToChecked(); |
581 } | 356 } |
582 | 357 |
583 void WindowProxy::updateSecurityOrigin(SecurityOrigin* origin) { | 358 void LocalWindowProxy::updateSecurityOrigin(SecurityOrigin* origin) { |
584 // For an uninitialized main window proxy, there's nothing we need | 359 // For an uninitialized main window proxy, there's nothing we need |
585 // to update. The update is done when the window proxy gets initialized later. | 360 // to update. The update is done when the window proxy gets initialized later. |
586 if (m_lifecycle == Lifecycle::ContextUninitialized) | 361 if (m_lifecycle == Lifecycle::ContextUninitialized) |
587 return; | 362 return; |
588 // TODO(yukishiino): Is it okay to not update security origin when the context | 363 // TODO(yukishiino): Is it okay to not update security origin when the context |
589 // is detached? | 364 // is detached? |
590 if (m_lifecycle == Lifecycle::ContextDetached) | 365 if (m_lifecycle == Lifecycle::ContextDetached) |
591 return; | 366 return; |
592 | 367 |
593 setSecurityToken(origin); | 368 setSecurityToken(origin); |
594 } | 369 } |
595 | 370 |
371 LocalWindowProxy::LocalWindowProxy(LocalFrame& frame, | |
372 v8::Isolate* isolate, | |
373 RefPtr<DOMWrapperWorld> world) | |
374 : WindowProxy(frame, isolate, std::move(world)) {} | |
375 | |
596 } // namespace blink | 376 } // namespace blink |
OLD | NEW |