| 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 void LocalWindowProxy::disposeContext(GlobalDetachmentBehavior behavior) { |
| 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 | |
| 93 // starts. | |
| 94 DCHECK(m_lifecycle != Lifecycle::ContextInitialized); | |
| 95 } | |
| 96 | |
| 97 DEFINE_TRACE(WindowProxy) { | |
| 98 visitor->trace(m_frame); | |
| 99 } | |
| 100 | |
| 101 void WindowProxy::disposeContext(GlobalDetachmentBehavior behavior) { | |
| 102 if (m_lifecycle != Lifecycle::ContextInitialized) | 64 if (m_lifecycle != Lifecycle::ContextInitialized) |
| 103 return; | 65 return; |
| 104 | 66 |
| 105 ScriptState::Scope scope(m_scriptState.get()); | 67 ScriptState::Scope scope(m_scriptState.get()); |
| 106 v8::Local<v8::Context> context = m_scriptState->context(); | 68 v8::Local<v8::Context> context = m_scriptState->context(); |
| 107 if (m_frame->isLocalFrame()) { | 69 // The embedder could run arbitrary code in response to the |
| 108 LocalFrame* frame = toLocalFrame(m_frame); | 70 // willReleaseScriptContext callback, so all disposing should happen after |
| 109 // The embedder could run arbitrary code in response to the | 71 // it returns. |
| 110 // willReleaseScriptContext callback, so all disposing should happen after | 72 frame()->loader().client()->willReleaseScriptContext(context, |
| 111 // it returns. | |
| 112 frame->loader().client()->willReleaseScriptContext(context, | |
| 113 m_world->worldId()); | 73 m_world->worldId()); |
| 114 MainThreadDebugger::instance()->contextWillBeDestroyed(m_scriptState.get()); | 74 MainThreadDebugger::instance()->contextWillBeDestroyed(m_scriptState.get()); |
| 115 } | |
| 116 | 75 |
| 117 if (behavior == DetachGlobal) { | 76 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 } | 77 } |
| 143 | 78 |
| 144 void WindowProxy::clearForClose() { | 79 void LocalWindowProxy::initialize() { |
| 145 disposeContext(DoNotDetachGlobal); | 80 TRACE_EVENT1("v8", "LocalWindowProxy::initialize", "isMainWindow", |
| 146 } | 81 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( | 82 SCOPED_BLINK_UMA_HISTOGRAM_TIMER( |
| 234 m_frame->isMainFrame() ? "Blink.Binding.InitializeMainWindowProxy" | 83 frame()->isMainFrame() ? "Blink.Binding.InitializeMainWindowProxy" |
| 235 : "Blink.Binding.InitializeNonMainWindowProxy"); | 84 : "Blink.Binding.InitializeNonMainWindowProxy"); |
| 236 | 85 |
| 237 ScriptForbiddenScope::AllowUserAgentScript allowScript; | 86 ScriptForbiddenScope::AllowUserAgentScript allowScript; |
| 238 | 87 |
| 239 v8::HandleScope handleScope(m_isolate); | 88 v8::HandleScope handleScope(isolate()); |
| 240 | 89 |
| 241 createContext(); | 90 createContext(); |
| 242 | 91 |
| 243 ScriptState::Scope scope(m_scriptState.get()); | 92 ScriptState::Scope scope(m_scriptState.get()); |
| 244 v8::Local<v8::Context> context = m_scriptState->context(); | 93 v8::Local<v8::Context> context = m_scriptState->context(); |
| 245 if (m_globalProxy.isEmpty()) { | 94 if (m_globalProxy.isEmpty()) { |
| 246 m_globalProxy.set(m_isolate, context->Global()); | 95 m_globalProxy.set(isolate(), context->Global()); |
| 247 CHECK(!m_globalProxy.isEmpty()); | 96 CHECK(!m_globalProxy.isEmpty()); |
| 248 } | 97 } |
| 249 | 98 |
| 250 setupWindowPrototypeChain(); | 99 setupWindowPrototypeChain(); |
| 251 | 100 |
| 252 SecurityOrigin* origin = 0; | 101 SecurityOrigin* origin = 0; |
| 253 if (m_world->isMainWorld()) { | 102 if (m_world->isMainWorld()) { |
| 254 // ActivityLogger for main world is updated within updateDocument(). | 103 // ActivityLogger for main world is updated within updateDocument(). |
| 255 updateDocument(); | 104 updateDocument(); |
| 256 origin = m_frame->securityContext()->getSecurityOrigin(); | 105 origin = frame()->document()->getSecurityOrigin(); |
| 257 // FIXME: Can this be removed when CSP moves to browser? | 106 // FIXME: Can this be removed when CSP moves to browser? |
| 258 ContentSecurityPolicy* csp = | 107 ContentSecurityPolicy* csp = frame()->document()->contentSecurityPolicy(); |
| 259 m_frame->securityContext()->contentSecurityPolicy(); | |
| 260 context->AllowCodeGenerationFromStrings( | 108 context->AllowCodeGenerationFromStrings( |
| 261 csp->allowEval(0, ContentSecurityPolicy::SuppressReport)); | 109 csp->allowEval(0, ContentSecurityPolicy::SuppressReport)); |
| 262 context->SetErrorMessageForCodeGenerationFromStrings( | 110 context->SetErrorMessageForCodeGenerationFromStrings( |
| 263 v8String(m_isolate, csp->evalDisabledErrorMessage())); | 111 v8String(isolate(), csp->evalDisabledErrorMessage())); |
| 264 } else { | 112 } else { |
| 265 updateActivityLogger(); | 113 updateActivityLogger(); |
| 266 origin = m_world->isolatedWorldSecurityOrigin(); | 114 origin = m_world->isolatedWorldSecurityOrigin(); |
| 267 setSecurityToken(origin); | 115 setSecurityToken(origin); |
| 268 } | 116 } |
| 269 | 117 |
| 270 if (m_frame->isLocalFrame()) { | 118 MainThreadDebugger::instance()->contextCreated(m_scriptState.get(), frame(), |
| 271 LocalFrame* frame = toLocalFrame(m_frame); | 119 origin); |
| 272 MainThreadDebugger::instance()->contextCreated(m_scriptState.get(), frame, | 120 frame()->loader().client()->didCreateScriptContext( |
| 273 origin); | 121 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 | 122 // If conditional features for window have been queued before the V8 context |
| 278 // was ready, then inject them into the context now | 123 // was ready, then inject them into the context now |
| 279 if (m_world->isMainWorld()) { | 124 if (m_world->isMainWorld()) { |
| 280 installPendingConditionalFeaturesOnWindow(m_scriptState.get()); | 125 installPendingConditionalFeaturesOnWindow(m_scriptState.get()); |
| 281 } | 126 } |
| 127 |
| 128 if (m_world->isMainWorld()) |
| 129 frame()->loader().dispatchDidClearWindowObjectInMainWorld(); |
| 282 } | 130 } |
| 283 | 131 |
| 284 void WindowProxy::createContext() { | 132 void LocalWindowProxy::createContext() { |
| 285 // Create a new v8::Context with the window object as the global object | 133 // 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 | 134 // (aka the inner global). Reuse the global proxy object (aka the outer |
| 287 // global) if it already exists. See the comments in | 135 // global) if it already exists. See the comments in |
| 288 // setupWindowPrototypeChain for the structure of the prototype chain of | 136 // setupWindowPrototypeChain for the structure of the prototype chain of |
| 289 // the global object. | 137 // the global object. |
| 290 v8::Local<v8::ObjectTemplate> globalTemplate = | 138 v8::Local<v8::ObjectTemplate> globalTemplate = |
| 291 V8Window::domTemplate(m_isolate, *m_world)->InstanceTemplate(); | 139 V8Window::domTemplate(isolate(), *m_world)->InstanceTemplate(); |
| 292 CHECK(!globalTemplate.IsEmpty()); | 140 CHECK(!globalTemplate.IsEmpty()); |
| 293 | 141 |
| 294 // FIXME: It's not clear what the right thing to do for remote frames is. | 142 // 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 | 143 // The extensions registered don't generally seem to make sense for remote |
| 296 // frames, so skip it for now. | 144 // frames, so skip it for now. |
| 297 Vector<const char*> extensionNames; | 145 Vector<const char*> extensionNames; |
| 298 if (m_frame->isLocalFrame()) { | 146 // Dynamically tell v8 about our extensions now. |
| 299 LocalFrame* frame = toLocalFrame(m_frame); | 147 const V8Extensions& extensions = ScriptController::registeredExtensions(); |
| 300 // Dynamically tell v8 about our extensions now. | 148 extensionNames.reserveInitialCapacity(extensions.size()); |
| 301 const V8Extensions& extensions = ScriptController::registeredExtensions(); | 149 int extensionGroup = m_world->extensionGroup(); |
| 302 extensionNames.reserveInitialCapacity(extensions.size()); | 150 int worldId = m_world->worldId(); |
| 303 int extensionGroup = m_world->extensionGroup(); | 151 for (const auto* extension : extensions) { |
| 304 int worldId = m_world->worldId(); | 152 if (!frame()->loader().client()->allowScriptExtension( |
| 305 for (const auto* extension : extensions) { | 153 extension->name(), extensionGroup, worldId)) |
| 306 if (!frame->loader().client()->allowScriptExtension( | 154 continue; |
| 307 extension->name(), extensionGroup, worldId)) | |
| 308 continue; | |
| 309 | 155 |
| 310 extensionNames.push_back(extension->name()); | 156 extensionNames.push_back(extension->name()); |
| 311 } | |
| 312 } | 157 } |
| 313 v8::ExtensionConfiguration extensionConfiguration(extensionNames.size(), | 158 v8::ExtensionConfiguration extensionConfiguration(extensionNames.size(), |
| 314 extensionNames.data()); | 159 extensionNames.data()); |
| 315 | 160 |
| 316 v8::Local<v8::Context> context; | 161 v8::Local<v8::Context> context; |
| 317 { | 162 { |
| 318 V8PerIsolateData::UseCounterDisabledScope useCounterDisabled( | 163 V8PerIsolateData::UseCounterDisabledScope useCounterDisabled( |
| 319 V8PerIsolateData::from(m_isolate)); | 164 V8PerIsolateData::from(isolate())); |
| 320 context = | 165 context = |
| 321 v8::Context::New(m_isolate, &extensionConfiguration, globalTemplate, | 166 v8::Context::New(isolate(), &extensionConfiguration, globalTemplate, |
| 322 m_globalProxy.newLocal(m_isolate)); | 167 m_globalProxy.newLocal(isolate())); |
| 323 } | 168 } |
| 324 CHECK(!context.IsEmpty()); | 169 CHECK(!context.IsEmpty()); |
| 325 | 170 |
| 326 m_scriptState = ScriptState::create(context, m_world); | 171 m_scriptState = ScriptState::create(context, m_world); |
| 327 | 172 |
| 328 // TODO(haraken): Currently we cannot enable the following DCHECK because | 173 // TODO(haraken): Currently we cannot enable the following DCHECK because |
| 329 // an already detached window proxy can be re-initialized. This is wrong. | 174 // an already detached window proxy can be re-initialized. This is wrong. |
| 330 // DCHECK(m_lifecycle == Lifecycle::ContextUninitialized); | 175 // DCHECK(m_lifecycle == Lifecycle::ContextUninitialized); |
| 331 m_lifecycle = Lifecycle::ContextInitialized; | 176 m_lifecycle = Lifecycle::ContextInitialized; |
| 332 DCHECK(m_scriptState->contextIsValid()); | 177 DCHECK(m_scriptState->contextIsValid()); |
| 333 } | 178 } |
| 334 | 179 |
| 335 void WindowProxy::setupWindowPrototypeChain() { | 180 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()); | 181 DCHECK(m_world->isMainWorld()); |
| 404 | 182 |
| 405 if (m_frame->isRemoteFrame()) | |
| 406 return; | |
| 407 | |
| 408 ScriptState::Scope scope(m_scriptState.get()); | 183 ScriptState::Scope scope(m_scriptState.get()); |
| 409 v8::Local<v8::Context> context = m_scriptState->context(); | 184 v8::Local<v8::Context> context = m_scriptState->context(); |
| 410 LocalFrame* frame = toLocalFrame(m_frame); | |
| 411 v8::Local<v8::Value> documentWrapper = | 185 v8::Local<v8::Value> documentWrapper = |
| 412 ToV8(frame->document(), context->Global(), m_isolate); | 186 ToV8(frame()->document(), context->Global(), isolate()); |
| 413 DCHECK(documentWrapper->IsObject()); | 187 DCHECK(documentWrapper->IsObject()); |
| 414 // Update the cached accessor for window.document. | 188 // Update the cached accessor for window.document. |
| 415 CHECK(V8PrivateProperty::getWindowDocumentCachedAccessor(m_isolate).set( | 189 CHECK(V8PrivateProperty::getWindowDocumentCachedAccessor(isolate()).set( |
| 416 context, context->Global(), documentWrapper)); | 190 context, context->Global(), documentWrapper)); |
| 417 } | 191 } |
| 418 | 192 |
| 419 void WindowProxy::updateActivityLogger() { | 193 void LocalWindowProxy::updateActivityLogger() { |
| 420 m_scriptState->perContextData()->setActivityLogger( | 194 m_scriptState->perContextData()->setActivityLogger( |
| 421 V8DOMActivityLogger::activityLogger( | 195 V8DOMActivityLogger::activityLogger( |
| 422 m_world->worldId(), | 196 m_world->worldId(), |
| 423 m_frame->isLocalFrame() && toLocalFrame(m_frame)->document() | 197 frame()->document() ? frame()->document()->baseURI() : KURL())); |
| 424 ? toLocalFrame(m_frame)->document()->baseURI() | |
| 425 : KURL())); | |
| 426 } | 198 } |
| 427 | 199 |
| 428 void WindowProxy::setSecurityToken(SecurityOrigin* origin) { | 200 void LocalWindowProxy::setSecurityToken(SecurityOrigin* origin) { |
| 429 // If two tokens are equal, then the SecurityOrigins canAccess each other. | 201 // If two tokens are equal, then the SecurityOrigins canAccess each other. |
| 430 // If two tokens are not equal, then we have to call canAccess. | 202 // 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. | 203 // Note: we can't use the HTTPOrigin if it was set from the DOM. |
| 432 String token; | 204 String token; |
| 433 // There are two situations where v8 needs to do a full canAccess check, | 205 // If document.domain is modified, v8 needs to do a full canAccess check, |
| 434 // so set an empty security token instead: | 206 // so always use an empty security token in that case. |
| 435 // - document.domain was modified | 207 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) | 208 if (origin && !delaySet) |
| 440 token = origin->toString(); | 209 token = origin->toString(); |
| 441 | 210 |
| 442 // An empty or "null" token means we always have to call | 211 // An empty or "null" token means we always have to call |
| 443 // canAccess. The toString method on securityOrigins returns the | 212 // canAccess. The toString method on securityOrigins returns the |
| 444 // string "null" for empty security origins and for security | 213 // string "null" for empty security origins and for security |
| 445 // origins that should only allow access to themselves. In this | 214 // origins that should only allow access to themselves. In this |
| 446 // case, we use the global object as the security token to avoid | 215 // case, we use the global object as the security token to avoid |
| 447 // calling canAccess when a script accesses its own objects. | 216 // calling canAccess when a script accesses its own objects. |
| 448 v8::HandleScope handleScope(m_isolate); | 217 v8::HandleScope handleScope(isolate()); |
| 449 v8::Local<v8::Context> context = m_scriptState->context(); | 218 v8::Local<v8::Context> context = m_scriptState->context(); |
| 450 if (token.isEmpty() || token == "null") { | 219 if (token.isEmpty() || token == "null") { |
| 451 context->UseDefaultSecurityToken(); | 220 context->UseDefaultSecurityToken(); |
| 452 return; | 221 return; |
| 453 } | 222 } |
| 454 | 223 |
| 455 if (m_world->isIsolatedWorld()) { | 224 if (m_world->isIsolatedWorld()) { |
| 456 SecurityOrigin* frameSecurityOrigin = | 225 SecurityOrigin* frameSecurityOrigin = |
| 457 m_frame->securityContext()->getSecurityOrigin(); | 226 frame()->document()->getSecurityOrigin(); |
| 458 String frameSecurityToken = frameSecurityOrigin->toString(); | 227 String frameSecurityToken = frameSecurityOrigin->toString(); |
| 459 // We need to check the return value of domainWasSetInDOM() on the | 228 // We need to check the return value of domainWasSetInDOM() on the |
| 460 // frame's SecurityOrigin because, if that's the case, only | 229 // frame's SecurityOrigin because, if that's the case, only |
| 461 // SecurityOrigin::m_domain would have been modified. | 230 // SecurityOrigin::m_domain would have been modified. |
| 462 // m_domain is not used by SecurityOrigin::toString(), so we would end | 231 // m_domain is not used by SecurityOrigin::toString(), so we would end |
| 463 // up generating the same token that was already set. | 232 // up generating the same token that was already set. |
| 464 if (frameSecurityOrigin->domainWasSetInDOM() || | 233 if (frameSecurityOrigin->domainWasSetInDOM() || |
| 465 frameSecurityToken.isEmpty() || frameSecurityToken == "null") { | 234 frameSecurityToken.isEmpty() || frameSecurityToken == "null") { |
| 466 context->UseDefaultSecurityToken(); | 235 context->UseDefaultSecurityToken(); |
| 467 return; | 236 return; |
| 468 } | 237 } |
| 469 token = frameSecurityToken + token; | 238 token = frameSecurityToken + token; |
| 470 } | 239 } |
| 471 | 240 |
| 472 // NOTE: V8 does identity comparison in fast path, must use a symbol | 241 // NOTE: V8 does identity comparison in fast path, must use a symbol |
| 473 // as the security token. | 242 // as the security token. |
| 474 context->SetSecurityToken(v8AtomicString(m_isolate, token)); | 243 context->SetSecurityToken(v8AtomicString(isolate(), token)); |
| 475 } | 244 } |
| 476 | 245 |
| 477 void WindowProxy::updateDocument() { | 246 void LocalWindowProxy::updateDocument() { |
| 478 DCHECK(m_world->isMainWorld()); | 247 DCHECK(m_world->isMainWorld()); |
| 479 // For an uninitialized main window proxy, there's nothing we need | 248 // 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. | 249 // to update. The update is done when the window proxy gets initialized later. |
| 481 if (m_lifecycle == Lifecycle::ContextUninitialized) | 250 if (m_lifecycle == Lifecycle::ContextUninitialized) |
| 482 return; | 251 return; |
| 483 // TODO(yukishiino): Is it okay to not update document when the context | 252 // TODO(yukishiino): Is it okay to not update document when the context |
| 484 // is detached? It's not trivial to fix this because udpateDocumentProperty | 253 // is detached? It's not trivial to fix this because udpateDocumentProperty |
| 485 // requires a not-yet-detached context to instantiate a document wrapper. | 254 // requires a not-yet-detached context to instantiate a document wrapper. |
| 486 if (m_lifecycle == Lifecycle::ContextDetached) | 255 if (m_lifecycle == Lifecycle::ContextDetached) |
| 487 return; | 256 return; |
| 488 | 257 |
| 489 updateActivityLogger(); | 258 updateActivityLogger(); |
| 490 updateDocumentProperty(); | 259 updateDocumentProperty(); |
| 491 updateSecurityOrigin(m_frame->securityContext()->getSecurityOrigin()); | 260 updateSecurityOrigin(frame()->document()->getSecurityOrigin()); |
| 492 } | 261 } |
| 493 | 262 |
| 494 static v8::Local<v8::Value> getNamedProperty( | 263 static v8::Local<v8::Value> getNamedProperty( |
| 495 HTMLDocument* htmlDocument, | 264 HTMLDocument* htmlDocument, |
| 496 const AtomicString& key, | 265 const AtomicString& key, |
| 497 v8::Local<v8::Object> creationContext, | 266 v8::Local<v8::Object> creationContext, |
| 498 v8::Isolate* isolate) { | 267 v8::Isolate* isolate) { |
| 499 if (!htmlDocument->hasNamedItem(key) && !htmlDocument->hasExtraNamedItem(key)) | 268 if (!htmlDocument->hasNamedItem(key) && !htmlDocument->hasExtraNamedItem(key)) |
| 500 return v8Undefined(); | 269 return v8Undefined(); |
| 501 | 270 |
| 502 DocumentNameCollection* items = htmlDocument->documentNamedItems(key); | 271 DocumentNameCollection* items = htmlDocument->documentNamedItems(key); |
| 503 if (items->isEmpty()) | 272 if (items->isEmpty()) |
| 504 return v8Undefined(); | 273 return v8Undefined(); |
| 505 | 274 |
| 506 if (items->hasExactlyOneItem()) { | 275 if (items->hasExactlyOneItem()) { |
| 507 HTMLElement* element = items->item(0); | 276 HTMLElement* element = items->item(0); |
| 508 ASSERT(element); | 277 DCHECK(element); |
| 509 Frame* frame = isHTMLIFrameElement(*element) | 278 Frame* frame = isHTMLIFrameElement(*element) |
| 510 ? toHTMLIFrameElement(*element).contentFrame() | 279 ? toHTMLIFrameElement(*element).contentFrame() |
| 511 : 0; | 280 : 0; |
| 512 if (frame) | 281 if (frame) |
| 513 return ToV8(frame->domWindow(), creationContext, isolate); | 282 return ToV8(frame->domWindow(), creationContext, isolate); |
| 514 return ToV8(element, creationContext, isolate); | 283 return ToV8(element, creationContext, isolate); |
| 515 } | 284 } |
| 516 return ToV8(items, creationContext, isolate); | 285 return ToV8(items, creationContext, isolate); |
| 517 } | 286 } |
| 518 | 287 |
| 519 static void getter(v8::Local<v8::Name> property, | 288 static void getter(v8::Local<v8::Name> property, |
| 520 const v8::PropertyCallbackInfo<v8::Value>& info) { | 289 const v8::PropertyCallbackInfo<v8::Value>& info) { |
| 521 if (!property->IsString()) | 290 if (!property->IsString()) |
| 522 return; | 291 return; |
| 523 // FIXME: Consider passing StringImpl directly. | 292 // FIXME: Consider passing StringImpl directly. |
| 524 AtomicString name = toCoreAtomicString(property.As<v8::String>()); | 293 AtomicString name = toCoreAtomicString(property.As<v8::String>()); |
| 525 HTMLDocument* htmlDocument = V8HTMLDocument::toImpl(info.Holder()); | 294 HTMLDocument* htmlDocument = V8HTMLDocument::toImpl(info.Holder()); |
| 526 ASSERT(htmlDocument); | 295 DCHECK(htmlDocument); |
| 527 v8::Local<v8::Value> result = | 296 v8::Local<v8::Value> result = |
| 528 getNamedProperty(htmlDocument, name, info.Holder(), info.GetIsolate()); | 297 getNamedProperty(htmlDocument, name, info.Holder(), info.GetIsolate()); |
| 529 if (!result.IsEmpty()) { | 298 if (!result.IsEmpty()) { |
| 530 v8SetReturnValue(info, result); | 299 v8SetReturnValue(info, result); |
| 531 return; | 300 return; |
| 532 } | 301 } |
| 533 v8::Local<v8::Value> value; | 302 v8::Local<v8::Value> value; |
| 534 if (info.Holder() | 303 if (info.Holder() |
| 535 ->GetRealNamedPropertyInPrototypeChain( | 304 ->GetRealNamedPropertyInPrototypeChain( |
| 536 info.GetIsolate()->GetCurrentContext(), property.As<v8::String>()) | 305 info.GetIsolate()->GetCurrentContext(), property.As<v8::String>()) |
| 537 .ToLocal(&value)) | 306 .ToLocal(&value)) |
| 538 v8SetReturnValue(info, value); | 307 v8SetReturnValue(info, value); |
| 539 } | 308 } |
| 540 | 309 |
| 541 void WindowProxy::namedItemAdded(HTMLDocument* document, | 310 void LocalWindowProxy::namedItemAdded(HTMLDocument* document, |
| 542 const AtomicString& name) { | 311 const AtomicString& name) { |
| 543 DCHECK(m_world->isMainWorld()); | 312 DCHECK(m_world->isMainWorld()); |
| 544 | 313 |
| 545 // Context must be initialized before this point. | 314 // Context must be initialized before this point. |
| 546 DCHECK(m_lifecycle >= Lifecycle::ContextInitialized); | 315 DCHECK(m_lifecycle >= Lifecycle::ContextInitialized); |
| 547 // TODO(yukishiino): Is it okay to not update named properties | 316 // TODO(yukishiino): Is it okay to not update named properties |
| 548 // after the context gets detached? | 317 // after the context gets detached? |
| 549 if (m_lifecycle == Lifecycle::ContextDetached) | 318 if (m_lifecycle == Lifecycle::ContextDetached) |
| 550 return; | 319 return; |
| 551 | 320 |
| 552 ScriptState::Scope scope(m_scriptState.get()); | 321 ScriptState::Scope scope(m_scriptState.get()); |
| 553 v8::Local<v8::Object> documentWrapper = | 322 v8::Local<v8::Object> documentWrapper = |
| 554 m_world->domDataStore().get(document, m_isolate); | 323 m_world->domDataStore().get(document, isolate()); |
| 555 // TODO(yukishiino,peria): We should check if the own property with the same | 324 // 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 | 325 // name already exists or not, and if it exists, we shouldn't define a new |
| 557 // accessor property (it fails). | 326 // accessor property (it fails). |
| 558 documentWrapper->SetAccessor(m_isolate->GetCurrentContext(), | 327 documentWrapper->SetAccessor(isolate()->GetCurrentContext(), |
| 559 v8String(m_isolate, name), getter); | 328 v8String(isolate(), name), getter); |
| 560 } | 329 } |
| 561 | 330 |
| 562 void WindowProxy::namedItemRemoved(HTMLDocument* document, | 331 void LocalWindowProxy::namedItemRemoved(HTMLDocument* document, |
| 563 const AtomicString& name) { | 332 const AtomicString& name) { |
| 564 DCHECK(m_world->isMainWorld()); | 333 DCHECK(m_world->isMainWorld()); |
| 565 | 334 |
| 566 // Context must be initialized before this point. | 335 // Context must be initialized before this point. |
| 567 DCHECK(m_lifecycle >= Lifecycle::ContextInitialized); | 336 DCHECK(m_lifecycle >= Lifecycle::ContextInitialized); |
| 568 // TODO(yukishiino): Is it okay to not update named properties | 337 // TODO(yukishiino): Is it okay to not update named properties |
| 569 // after the context gets detached? | 338 // after the context gets detached? |
| 570 if (m_lifecycle == Lifecycle::ContextDetached) | 339 if (m_lifecycle == Lifecycle::ContextDetached) |
| 571 return; | 340 return; |
| 572 | 341 |
| 573 if (document->hasNamedItem(name) || document->hasExtraNamedItem(name)) | 342 if (document->hasNamedItem(name) || document->hasExtraNamedItem(name)) |
| 574 return; | 343 return; |
| 575 ScriptState::Scope scope(m_scriptState.get()); | 344 ScriptState::Scope scope(m_scriptState.get()); |
| 576 v8::Local<v8::Object> documentWrapper = | 345 v8::Local<v8::Object> documentWrapper = |
| 577 m_world->domDataStore().get(document, m_isolate); | 346 m_world->domDataStore().get(document, isolate()); |
| 578 documentWrapper | 347 documentWrapper |
| 579 ->Delete(m_isolate->GetCurrentContext(), v8String(m_isolate, name)) | 348 ->Delete(isolate()->GetCurrentContext(), v8String(isolate(), name)) |
| 580 .ToChecked(); | 349 .ToChecked(); |
| 581 } | 350 } |
| 582 | 351 |
| 583 void WindowProxy::updateSecurityOrigin(SecurityOrigin* origin) { | 352 void LocalWindowProxy::updateSecurityOrigin(SecurityOrigin* origin) { |
| 584 // For an uninitialized main window proxy, there's nothing we need | 353 // 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. | 354 // to update. The update is done when the window proxy gets initialized later. |
| 586 if (m_lifecycle == Lifecycle::ContextUninitialized) | 355 if (m_lifecycle == Lifecycle::ContextUninitialized) |
| 587 return; | 356 return; |
| 588 // TODO(yukishiino): Is it okay to not update security origin when the context | 357 // TODO(yukishiino): Is it okay to not update security origin when the context |
| 589 // is detached? | 358 // is detached? |
| 590 if (m_lifecycle == Lifecycle::ContextDetached) | 359 if (m_lifecycle == Lifecycle::ContextDetached) |
| 591 return; | 360 return; |
| 592 | 361 |
| 593 setSecurityToken(origin); | 362 setSecurityToken(origin); |
| 594 } | 363 } |
| 595 | 364 |
| 365 LocalWindowProxy::LocalWindowProxy(v8::Isolate* isolate, |
| 366 LocalFrame& frame, |
| 367 RefPtr<DOMWrapperWorld> world) |
| 368 : WindowProxy(isolate, frame, std::move(world)) {} |
| 369 |
| 596 } // namespace blink | 370 } // namespace blink |
| OLD | NEW |