Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(183)

Side by Side Diff: third_party/WebKit/Source/bindings/core/v8/LocalWindowProxy.cpp

Issue 2841443005: [Bindings] Create and use V8 context snapshots (Closed)
Patch Set: Fix some behaviors Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 21 matching lines...) Expand all
32 32
33 #include "bindings/core/v8/ConditionalFeaturesForCore.h" 33 #include "bindings/core/v8/ConditionalFeaturesForCore.h"
34 #include "bindings/core/v8/ScriptController.h" 34 #include "bindings/core/v8/ScriptController.h"
35 #include "bindings/core/v8/ToV8ForCore.h" 35 #include "bindings/core/v8/ToV8ForCore.h"
36 #include "bindings/core/v8/V8BindingForCore.h" 36 #include "bindings/core/v8/V8BindingForCore.h"
37 #include "bindings/core/v8/V8DOMActivityLogger.h" 37 #include "bindings/core/v8/V8DOMActivityLogger.h"
38 #include "bindings/core/v8/V8GCForContextDispose.h" 38 #include "bindings/core/v8/V8GCForContextDispose.h"
39 #include "bindings/core/v8/V8HTMLDocument.h" 39 #include "bindings/core/v8/V8HTMLDocument.h"
40 #include "bindings/core/v8/V8Initializer.h" 40 #include "bindings/core/v8/V8Initializer.h"
41 #include "bindings/core/v8/V8PagePopupControllerBinding.h" 41 #include "bindings/core/v8/V8PagePopupControllerBinding.h"
42 #include "bindings/core/v8/V8SnapshotCreator.h"
42 #include "bindings/core/v8/V8Window.h" 43 #include "bindings/core/v8/V8Window.h"
43 #include "core/dom/Modulator.h" 44 #include "core/dom/Modulator.h"
44 #include "core/frame/LocalFrame.h" 45 #include "core/frame/LocalFrame.h"
45 #include "core/frame/LocalFrameClient.h" 46 #include "core/frame/LocalFrameClient.h"
46 #include "core/frame/csp/ContentSecurityPolicy.h" 47 #include "core/frame/csp/ContentSecurityPolicy.h"
47 #include "core/html/DocumentNameCollection.h" 48 #include "core/html/DocumentNameCollection.h"
48 #include "core/html/HTMLIFrameElement.h" 49 #include "core/html/HTMLIFrameElement.h"
49 #include "core/inspector/MainThreadDebugger.h" 50 #include "core/inspector/MainThreadDebugger.h"
50 #include "core/loader/FrameLoader.h" 51 #include "core/loader/FrameLoader.h"
51 #include "core/origin_trials/OriginTrialContext.h" 52 #include "core/origin_trials/OriginTrialContext.h"
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
150 context->AllowCodeGenerationFromStrings(csp->AllowEval( 151 context->AllowCodeGenerationFromStrings(csp->AllowEval(
151 0, SecurityViolationReportingPolicy::kSuppressReporting)); 152 0, SecurityViolationReportingPolicy::kSuppressReporting));
152 context->SetErrorMessageForCodeGenerationFromStrings( 153 context->SetErrorMessageForCodeGenerationFromStrings(
153 V8String(GetIsolate(), csp->EvalDisabledErrorMessage())); 154 V8String(GetIsolate(), csp->EvalDisabledErrorMessage()));
154 } else { 155 } else {
155 UpdateActivityLogger(); 156 UpdateActivityLogger();
156 origin = world_->IsolatedWorldSecurityOrigin(); 157 origin = world_->IsolatedWorldSecurityOrigin();
157 SetSecurityToken(origin); 158 SetSecurityToken(origin);
158 } 159 }
159 160
160 MainThreadDebugger::Instance()->ContextCreated(script_state_.Get(), 161 {
161 GetFrame(), origin); 162 TRACE_EVENT1("v8", "notification", "isMainWindow",
Yuki 2017/05/10 08:58:42 nit: I'm not clear about what "main window" is. W
peria 2017/06/01 08:33:31 Done.
162 GetFrame()->Loader().Client()->DidCreateScriptContext(context, 163 GetFrame()->IsMainFrame());
163 world_->GetWorldId()); 164 MainThreadDebugger::Instance()->ContextCreated(script_state_.Get(),
164 // If conditional features for window have been queued before the V8 context 165 GetFrame(), origin);
165 // was ready, then inject them into the context now 166 GetFrame()->Loader().Client()->DidCreateScriptContext(context,
166 if (world_->IsMainWorld()) { 167 world_->GetWorldId());
167 InstallConditionalFeaturesOnWindow(script_state_.Get()); 168 // If conditional features for window have been queued before the V8 context
169 // was ready, then inject them into the context now
170 if (world_->IsMainWorld()) {
171 InstallConditionalFeaturesOnWindow(script_state_.Get());
172 GetFrame()->Loader().DispatchDidClearWindowObjectInMainWorld();
173 }
168 } 174 }
169
170 if (world_->IsMainWorld())
171 GetFrame()->Loader().DispatchDidClearWindowObjectInMainWorld();
172 } 175 }
173 176
177 namespace {
178
179 struct DataForDeserializer {
Yuki 2017/05/10 08:58:43 You don't want to expose this kind of hack outside
peria 2017/05/30 08:25:41 Done.
180 STACK_ALLOCATED();
181 Member<Document> document;
182 };
183
184 const WrapperTypeInfo* FieldTypeToWrapperTypeInfo(
Yuki 2017/05/10 08:58:43 V8SnapshotCreater should be responsible about the
peria 2017/05/30 08:25:41 Done.
185 V8SnapshotCreator::FieldType type) {
186 switch (type) {
187 case V8SnapshotCreator::kNodeType:
188 return &V8Node::wrapperTypeInfo;
189 case V8SnapshotCreator::kDocumentType:
190 return &V8Document::wrapperTypeInfo;
191 case V8SnapshotCreator::kHTMLDocumentType:
192 return &V8HTMLDocument::wrapperTypeInfo;
193 case V8SnapshotCreator::kHTMLDocumentObject:
194 return &V8HTMLDocument::wrapperTypeInfo;
195 case V8SnapshotCreator::kNone:
196 NOTREACHED();
197 break;
198 }
199 NOTREACHED();
200 return nullptr;
201 }
202
203 void deserialize(v8::Local<v8::Object> wrapper,
204 int index,
205 v8::StartupData payload,
206 void* ptr) {
207 CHECK_EQ(payload.raw_size,
208 static_cast<int>(sizeof(V8SnapshotCreator::FieldType)));
209 V8SnapshotCreator::FieldType type =
210 *reinterpret_cast<const V8SnapshotCreator::FieldType*>(payload.data);
211
212 const WrapperTypeInfo* wrapper_type_info = FieldTypeToWrapperTypeInfo(type);
213 switch (type) {
214 case V8SnapshotCreator::kNodeType:
Yuki 2017/05/10 08:58:42 nit: Probably it's cleaner to use bit flags here.
peria 2017/05/30 08:25:41 Acknowledged.
215 case V8SnapshotCreator::kDocumentType:
216 case V8SnapshotCreator::kHTMLDocumentType: {
217 CHECK_EQ(index, kV8DOMWrapperTypeIndex);
218 wrapper->SetAlignedPointerInInternalField(
219 index, const_cast<WrapperTypeInfo*>(wrapper_type_info));
Yuki 2017/05/10 08:58:43 I'm not confident, but we wouldn't need to set an
peria 2017/05/30 08:25:41 Acknowledged.
220 wrapper_type_info->WrapperCreated();
Yuki 2017/05/10 08:58:43 IIUC, interface objects are not considered as "wra
peria 2017/05/30 08:25:41 Done.
221 break;
222 }
223 case V8SnapshotCreator::kHTMLDocumentObject: {
224 CHECK_EQ(index, kV8DOMWrapperObjectIndex);
225 v8::Isolate* isolate = v8::Isolate::GetCurrent();
226 DataForDeserializer* data = static_cast<DataForDeserializer*>(ptr);
227 ScriptWrappable* document = data->document;
228
229 // Make reference from wrapper to document
230 wrapper->SetAlignedPointerInInternalField(index, document);
231 // Make reference from document to wrapper
232 CHECK(document->SetWrapper(isolate, wrapper_type_info, wrapper));
Yuki 2017/05/10 08:58:42 I think that you should use document->AssociateWit
peria 2017/05/30 08:25:41 This routine is run during creating v8::Context, s
233 break;
234 }
235 case V8SnapshotCreator::kNone:
236 NOTREACHED();
237 break;
238 }
239 }
240
241 } // namespace
242
174 void LocalWindowProxy::CreateContext() { 243 void LocalWindowProxy::CreateContext() {
175 // Create a new v8::Context with the window object as the global object 244 TRACE_EVENT1("v8", "LocalWindowProxy::CreateContext", "isMainWindow",
176 // (aka the inner global). Reuse the outer global proxy if it already exists. 245 GetFrame()->IsMainFrame());
177 v8::Local<v8::ObjectTemplate> global_template = 246
178 V8Window::domTemplate(GetIsolate(), *world_)->InstanceTemplate(); 247 if (V8PerIsolateData::From(GetIsolate())->UseSnapshot()) {
Yuki 2017/05/10 08:58:43 This kind of code should live hidden in V8Snapshot
peria 2017/05/30 08:25:41 Done. This part should be run once per context. I
179 CHECK(!global_template.IsEmpty()); 248 // To store function templates into V8PerIsolateData.
249 v8::Local<v8::FunctionTemplate> eventTargetFunctionTemplate =
250 V8EventTarget::domTemplate(GetIsolate(), World());
251 CHECK(!eventTargetFunctionTemplate.IsEmpty());
252 v8::Local<v8::FunctionTemplate> windowFunctionTemplate =
253 V8Window::domTemplate(GetIsolate(), World());
254 CHECK(!windowFunctionTemplate.IsEmpty());
255 v8::Local<v8::FunctionTemplate> nodeFunctionTemplate =
256 V8Node::domTemplate(GetIsolate(), World());
257 CHECK(!nodeFunctionTemplate.IsEmpty());
258 v8::Local<v8::FunctionTemplate> documentFunctionTemplate =
259 V8Document::domTemplate(GetIsolate(), World());
260 CHECK(!documentFunctionTemplate.IsEmpty());
261 v8::Local<v8::FunctionTemplate> htmlDocumentFunctionTemplate =
262 V8HTMLDocument::domTemplate(GetIsolate(), World());
263 CHECK(!htmlDocumentFunctionTemplate.IsEmpty());
264 }
180 265
181 Vector<const char*> extension_names; 266 Vector<const char*> extension_names;
182 // Dynamically tell v8 about our extensions now. 267 // Dynamically tell v8 about our extensions now.
183 if (GetFrame()->Loader().Client()->AllowScriptExtensions()) { 268 if (GetFrame()->Loader().Client()->AllowScriptExtensions()) {
184 const V8Extensions& extensions = ScriptController::RegisteredExtensions(); 269 const V8Extensions& extensions = ScriptController::RegisteredExtensions();
185 extension_names.ReserveInitialCapacity(extensions.size()); 270 extension_names.ReserveInitialCapacity(extensions.size());
186 for (const auto* extension : extensions) 271 for (const auto* extension : extensions)
187 extension_names.push_back(extension->name()); 272 extension_names.push_back(extension->name());
188 } 273 }
189 v8::ExtensionConfiguration extension_configuration(extension_names.size(), 274 v8::ExtensionConfiguration extension_configuration(extension_names.size(),
190 extension_names.data()); 275 extension_names.data());
191 276
192 v8::Local<v8::Context> context; 277 v8::Local<v8::Context> context;
193 { 278 {
279 V8PerIsolateData* per_isolate_data = V8PerIsolateData::From(GetIsolate());
194 V8PerIsolateData::UseCounterDisabledScope use_counter_disabled( 280 V8PerIsolateData::UseCounterDisabledScope use_counter_disabled(
195 V8PerIsolateData::From(GetIsolate())); 281 V8PerIsolateData::From(GetIsolate()));
196 context = 282 TRACE_EVENT1("v8", "contextCreation", "isMainWindow",
197 v8::Context::New(GetIsolate(), &extension_configuration, 283 GetFrame()->IsMainFrame());
198 global_template, global_proxy_.NewLocal(GetIsolate())); 284
285 v8::Local<v8::Object> global_proxy = global_proxy_.NewLocal(GetIsolate());
286 Document* document = GetFrame()->GetDocument();
287
288 if (per_isolate_data->UseSnapshot() &&
289 (!World().IsMainWorld() || (document && document->IsHTMLDocument()))) {
Yuki 2017/05/10 08:58:42 I don't understand these conditions. I thought th
peria 2017/05/30 08:25:41 First of all, we need at least two types of contex
290 const int index = World().IsMainWorld() ? 0 : 1;
Yuki 2017/05/10 08:58:43 Where these magic numbers 0 and 1 come from? You
peria 2017/05/30 08:25:41 Done.
291 DataForDeserializer data{document};
292 CHECK(v8::Context::FromSnapshot(
293 GetIsolate(), index,
294 v8::DeserializeInternalFieldsCallback(&deserialize, &data),
295 &extension_configuration, global_proxy)
296 .ToLocal(&context));
297 }
298
299 if (context.IsEmpty()) {
Yuki 2017/05/10 08:58:43 Should this simply be } else { ? It seems that
peria 2017/05/30 08:25:41 Done.
300 v8::Local<v8::ObjectTemplate> global_template =
301 V8Window::domTemplate(GetIsolate(), *world_)->InstanceTemplate();
302 CHECK(!global_template.IsEmpty());
303 context = v8::Context::New(GetIsolate(), &extension_configuration,
304 global_template, global_proxy);
305 }
199 } 306 }
200 CHECK(!context.IsEmpty()); 307 CHECK(!context.IsEmpty());
201 308
202 #if DCHECK_IS_ON() 309 #if DCHECK_IS_ON()
203 DidAttachGlobalObject(); 310 DidAttachGlobalObject();
204 #endif 311 #endif
205 312
206 script_state_ = ScriptState::Create(context, world_); 313 script_state_ = ScriptState::Create(context, world_);
207 314
208 DCHECK(lifecycle_ == Lifecycle::kContextIsUninitialized || 315 DCHECK(lifecycle_ == Lifecycle::kContextIsUninitialized ||
209 lifecycle_ == Lifecycle::kGlobalObjectIsDetached); 316 lifecycle_ == Lifecycle::kGlobalObjectIsDetached);
210 lifecycle_ = Lifecycle::kContextIsInitialized; 317 lifecycle_ = Lifecycle::kContextIsInitialized;
211 DCHECK(script_state_->ContextIsValid()); 318 DCHECK(script_state_->ContextIsValid());
212 } 319 }
213 320
214 void LocalWindowProxy::SetupWindowPrototypeChain() { 321 void LocalWindowProxy::SetupWindowPrototypeChain() {
322 TRACE_EVENT1("v8", "LocalWindowProxy::setupWindowPrototypeChain",
Yuki 2017/05/10 08:58:43 nit: s/setup/Setup/
peria 2017/05/30 08:25:41 Done.
323 "isMainWindow", GetFrame()->IsMainFrame());
324
215 // Associate the window wrapper object and its prototype chain with the 325 // Associate the window wrapper object and its prototype chain with the
216 // corresponding native DOMWindow object. 326 // corresponding native DOMWindow object.
217 DOMWindow* window = GetFrame()->DomWindow(); 327 DOMWindow* window = GetFrame()->DomWindow();
218 const WrapperTypeInfo* wrapper_type_info = window->GetWrapperTypeInfo(); 328 const WrapperTypeInfo* wrapper_type_info = window->GetWrapperTypeInfo();
219 v8::Local<v8::Context> context = script_state_->GetContext(); 329 v8::Local<v8::Context> context = script_state_->GetContext();
220 330
221 // The global proxy object. Note this is not the global object. 331 // The global proxy object. Note this is not the global object.
222 v8::Local<v8::Object> global_proxy = context->Global(); 332 v8::Local<v8::Object> global_proxy = context->Global();
223 CHECK(global_proxy_ == global_proxy); 333 CHECK(global_proxy_ == global_proxy);
224 V8DOMWrapper::SetNativeInfo(GetIsolate(), global_proxy, wrapper_type_info, 334 V8DOMWrapper::SetNativeInfo(GetIsolate(), global_proxy, wrapper_type_info,
(...skipping 16 matching lines...) Expand all
241 V8DOMWrapper::SetNativeInfo(GetIsolate(), window_prototype, wrapper_type_info, 351 V8DOMWrapper::SetNativeInfo(GetIsolate(), window_prototype, wrapper_type_info,
242 window); 352 window);
243 353
244 // The named properties object of Window interface. 354 // The named properties object of Window interface.
245 v8::Local<v8::Object> window_properties = 355 v8::Local<v8::Object> window_properties =
246 window_prototype->GetPrototype().As<v8::Object>(); 356 window_prototype->GetPrototype().As<v8::Object>();
247 CHECK(!window_properties.IsEmpty()); 357 CHECK(!window_properties.IsEmpty());
248 V8DOMWrapper::SetNativeInfo(GetIsolate(), window_properties, 358 V8DOMWrapper::SetNativeInfo(GetIsolate(), window_properties,
249 wrapper_type_info, window); 359 wrapper_type_info, window);
250 360
361 if (V8PerIsolateData::From(GetIsolate())->UseSnapshot()) {
362 v8::Local<v8::Function> window_interface =
363 V8Window::domTemplate(GetIsolate(), World())->GetFunction();
364 V8Window::installV8WindowRuntimeEnabledFunction(
365 GetIsolate(), World(), window_wrapper, window_prototype,
366 window_interface);
367 }
368
251 // TODO(keishi): Remove installPagePopupController and implement 369 // TODO(keishi): Remove installPagePopupController and implement
252 // PagePopupController in another way. 370 // PagePopupController in another way.
253 V8PagePopupControllerBinding::InstallPagePopupController(context, 371 V8PagePopupControllerBinding::InstallPagePopupController(context,
254 window_wrapper); 372 window_wrapper);
255 } 373 }
256 374
257 void LocalWindowProxy::UpdateDocumentProperty() { 375 void LocalWindowProxy::UpdateDocumentProperty() {
258 DCHECK(world_->IsMainWorld()); 376 DCHECK(world_->IsMainWorld());
377 TRACE_EVENT1("v8", "LocalWindowProxy::updateDocumentProperty", "isMainWindow",
Yuki 2017/05/10 08:58:43 nit: s/update/Update/
peria 2017/05/30 08:25:41 Done.
378 GetFrame()->IsMainFrame());
259 379
260 ScriptState::Scope scope(script_state_.Get()); 380 ScriptState::Scope scope(script_state_.Get());
261 v8::Local<v8::Context> context = script_state_->GetContext(); 381 v8::Local<v8::Context> context = script_state_->GetContext();
262 v8::Local<v8::Value> document_wrapper = 382 v8::Local<v8::Value> document_wrapper =
263 ToV8(GetFrame()->GetDocument(), context->Global(), GetIsolate()); 383 ToV8(GetFrame()->GetDocument(), context->Global(), GetIsolate());
264 DCHECK(document_wrapper->IsObject()); 384 DCHECK(document_wrapper->IsObject());
385
386 if (V8PerIsolateData::From(GetIsolate())->UseSnapshot()) {
387 v8::Local<v8::Object> wrapper = document_wrapper.As<v8::Object>();
Yuki 2017/05/10 08:58:42 s/wrapper/htmldocument_wrapper/ and make it clear
peria 2017/05/30 08:25:41 Done.
388 v8::Local<v8::Object> document_prototype = wrapper->GetPrototype()
389 .As<v8::Object>()
390 ->GetPrototype()
391 .As<v8::Object>();
392 V8Document::installRuntimeEnabledFeatures(GetIsolate(), World(), wrapper,
Yuki 2017/05/10 08:58:43 Although it's noop, we do have {V8HTMLDocument,V8N
peria 2017/05/30 08:25:41 Done.
393 document_prototype,
394 v8::Local<v8::Function>());
395 V8Document::preparePrototypeAndInterfaceObject(
396 context, World(), document_prototype, v8::Local<v8::Function>(),
397 v8::Local<v8::FunctionTemplate>());
398 }
399
265 // Update the cached accessor for window.document. 400 // Update the cached accessor for window.document.
266 CHECK(V8PrivateProperty::GetWindowDocumentCachedAccessor(GetIsolate()) 401 CHECK(V8PrivateProperty::GetWindowDocumentCachedAccessor(GetIsolate())
267 .Set(context->Global(), document_wrapper)); 402 .Set(context->Global(), document_wrapper));
268 } 403 }
269 404
270 void LocalWindowProxy::UpdateActivityLogger() { 405 void LocalWindowProxy::UpdateActivityLogger() {
271 script_state_->PerContextData()->SetActivityLogger( 406 script_state_->PerContextData()->SetActivityLogger(
272 V8DOMActivityLogger::ActivityLogger( 407 V8DOMActivityLogger::ActivityLogger(
273 world_->GetWorldId(), GetFrame()->GetDocument() 408 world_->GetWorldId(), GetFrame()->GetDocument()
274 ? GetFrame()->GetDocument()->baseURI() 409 ? GetFrame()->GetDocument()->baseURI()
(...skipping 23 matching lines...) Expand all
298 context->UseDefaultSecurityToken(); 433 context->UseDefaultSecurityToken();
299 return; 434 return;
300 } 435 }
301 436
302 if (world_->IsIsolatedWorld()) { 437 if (world_->IsIsolatedWorld()) {
303 SecurityOrigin* frame_security_origin = 438 SecurityOrigin* frame_security_origin =
304 GetFrame()->GetDocument()->GetSecurityOrigin(); 439 GetFrame()->GetDocument()->GetSecurityOrigin();
305 String frame_security_token = frame_security_origin->ToString(); 440 String frame_security_token = frame_security_origin->ToString();
306 // We need to check the return value of domainWasSetInDOM() on the 441 // We need to check the return value of domainWasSetInDOM() on the
307 // frame's SecurityOrigin because, if that's the case, only 442 // frame's SecurityOrigin because, if that's the case, only
308 // SecurityOrigin::m_domain would have been modified. 443 // SecurityOrigin::domain_ would have been modified.
309 // m_domain is not used by SecurityOrigin::toString(), so we would end 444 // domain_ is not used by SecurityOrigin::toString(), so we would end
310 // up generating the same token that was already set. 445 // up generating the same token that was already set.
311 if (frame_security_origin->DomainWasSetInDOM() || 446 if (frame_security_origin->DomainWasSetInDOM() ||
312 frame_security_token.IsEmpty() || frame_security_token == "null") { 447 frame_security_token.IsEmpty() || frame_security_token == "null") {
313 context->UseDefaultSecurityToken(); 448 context->UseDefaultSecurityToken();
314 return; 449 return;
315 } 450 }
316 token = frame_security_token + token; 451 token = frame_security_token + token;
317 } 452 }
318 453
319 // NOTE: V8 does identity comparison in fast path, must use a symbol 454 // NOTE: V8 does identity comparison in fast path, must use a symbol
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
449 584
450 SetSecurityToken(origin); 585 SetSecurityToken(origin);
451 } 586 }
452 587
453 LocalWindowProxy::LocalWindowProxy(v8::Isolate* isolate, 588 LocalWindowProxy::LocalWindowProxy(v8::Isolate* isolate,
454 LocalFrame& frame, 589 LocalFrame& frame,
455 RefPtr<DOMWrapperWorld> world) 590 RefPtr<DOMWrapperWorld> world)
456 : WindowProxy(isolate, frame, std::move(world)) {} 591 : WindowProxy(isolate, frame, std::move(world)) {}
457 592
458 } // namespace blink 593 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698