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

Side by Side Diff: third_party/WebKit/Source/core/dom/ScriptLoader.cpp

Issue 2690273002: [Script Spec Annotation] Annotate "prepare a script" (Closed)
Patch Set: Rebase Created 3 years, 10 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) 1999 Lars Knoll (knoll@kde.org) 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org) 4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights 5 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights
6 * reserved. 6 * reserved.
7 * Copyright (C) 2008 Nikolas Zimmermann <zimmermann@kde.org> 7 * Copyright (C) 2008 Nikolas Zimmermann <zimmermann@kde.org>
8 * 8 *
9 * This library is free software; you can redistribute it and/or 9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public 10 * modify it under the terms of the GNU Library General Public
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after
213 return false; 213 return false;
214 } 214 }
215 215
216 bool ScriptLoader::isScriptTypeSupported( 216 bool ScriptLoader::isScriptTypeSupported(
217 LegacyTypeSupport supportLegacyTypes) const { 217 LegacyTypeSupport supportLegacyTypes) const {
218 return isValidScriptTypeAndLanguage(client()->typeAttributeValue(), 218 return isValidScriptTypeAndLanguage(client()->typeAttributeValue(),
219 client()->languageAttributeValue(), 219 client()->languageAttributeValue(),
220 supportLegacyTypes); 220 supportLegacyTypes);
221 } 221 }
222 222
223 // http://dev.w3.org/html5/spec/Overview.html#prepare-a-script 223 // https://html.spec.whatwg.org/#prepare-a-script
224 bool ScriptLoader::prepareScript(const TextPosition& scriptStartPosition, 224 bool ScriptLoader::prepareScript(const TextPosition& scriptStartPosition,
225 LegacyTypeSupport supportLegacyTypes) { 225 LegacyTypeSupport supportLegacyTypes) {
226 // 1. "If the script element is marked as having "already started", then
227 // abort these steps at this point. The script is not executed."
226 if (m_alreadyStarted) 228 if (m_alreadyStarted)
227 return false; 229 return false;
228 230
229 ScriptLoaderClient* client = this->client(); 231 ScriptLoaderClient* client = this->client();
230 232
233 // 2. "If the element has its "parser-inserted" flag set, then
234 // set was-parser-inserted to true and unset the element's
235 // "parser-inserted" flag.
236 // Otherwise, set was-parser-inserted to false."
231 bool wasParserInserted; 237 bool wasParserInserted;
232 if (m_parserInserted) { 238 if (m_parserInserted) {
233 wasParserInserted = true; 239 wasParserInserted = true;
234 m_parserInserted = false; 240 m_parserInserted = false;
235 } else { 241 } else {
236 wasParserInserted = false; 242 wasParserInserted = false;
237 } 243 }
238 244
245 // 3. "If was-parser-inserted is true and the element does not have an
246 // async attribute, then set the element's "non-blocking" flag to true."
239 if (wasParserInserted && !client->asyncAttributeValue()) 247 if (wasParserInserted && !client->asyncAttributeValue())
240 m_nonBlocking = true; 248 m_nonBlocking = true;
241 249
250 // 4. "If the element has no src attribute, and its child nodes, if any,
251 // consist only of comment nodes and empty Text nodes,
252 // then abort these steps at this point. The script is not executed."
242 // FIXME: HTML5 spec says we should check that all children are either 253 // FIXME: HTML5 spec says we should check that all children are either
243 // comments or empty text nodes. 254 // comments or empty text nodes.
244 if (!client->hasSourceAttribute() && !m_element->hasChildren()) 255 if (!client->hasSourceAttribute() && !m_element->hasChildren())
245 return false; 256 return false;
246 257
258 // 5. "If the element is not connected, then abort these steps.
259 // The script is not executed."
247 if (!m_element->isConnected()) 260 if (!m_element->isConnected())
248 return false; 261 return false;
249 262
263 // 6.
264 // TODO(hiroshige): Annotate and/or cleanup this step.
250 if (!isScriptTypeSupported(supportLegacyTypes)) 265 if (!isScriptTypeSupported(supportLegacyTypes))
251 return false; 266 return false;
252 267
268 // 7. "If was-parser-inserted is true,
269 // then flag the element as "parser-inserted" again,
270 // and set the element's "non-blocking" flag to false."
253 if (wasParserInserted) { 271 if (wasParserInserted) {
254 m_parserInserted = true; 272 m_parserInserted = true;
255 m_nonBlocking = false; 273 m_nonBlocking = false;
256 } 274 }
257 275
276 // 8. "Set the element's "already started" flag."
258 m_alreadyStarted = true; 277 m_alreadyStarted = true;
259 278
279 // 9. "If the element is flagged as "parser-inserted", but the element's
280 // node document is not the Document of the parser that created the element,
281 // then abort these steps."
260 // FIXME: If script is parser inserted, verify it's still in the original 282 // FIXME: If script is parser inserted, verify it's still in the original
261 // document. 283 // document.
262 Document& elementDocument = m_element->document(); 284 Document& elementDocument = m_element->document();
263 Document* contextDocument = elementDocument.contextDocument(); 285 Document* contextDocument = elementDocument.contextDocument();
264 286 if (!contextDocument)
265 if (!contextDocument || !contextDocument->allowExecutingScripts(m_element))
266 return false; 287 return false;
267 288
289 // 10. "If scripting is disabled for the script element, then abort these
290 // steps at this point. The script is not executed."
291 if (!contextDocument->allowExecutingScripts(m_element))
292 return false;
293
294 // 13.
268 if (!isScriptForEventSupported()) 295 if (!isScriptForEventSupported())
269 return false; 296 return false;
270 297
298 // 14. "If the script element has a charset attribute,
299 // then let encoding be the result of
300 // getting an encoding from the value of the charset attribute."
301 // "If the script element does not have a charset attribute,
302 // or if getting an encoding failed, let encoding
303 // be the same as the encoding of the script element's node document."
304 // TODO(hiroshige): Should we handle failure in getting an encoding?
271 String encoding; 305 String encoding;
272 if (!client->charsetAttributeValue().isEmpty()) 306 if (!client->charsetAttributeValue().isEmpty())
273 encoding = client->charsetAttributeValue(); 307 encoding = client->charsetAttributeValue();
274 else 308 else
275 encoding = elementDocument.characterSet(); 309 encoding = elementDocument.characterSet();
276 310
311 // Steps 15--20 are handled in fetchScript().
312
313 // 21. "If the element has a src content attribute, run these substeps:"
277 if (client->hasSourceAttribute()) { 314 if (client->hasSourceAttribute()) {
278 FetchRequest::DeferOption defer = FetchRequest::NoDefer; 315 FetchRequest::DeferOption defer = FetchRequest::NoDefer;
279 if (!m_parserInserted || client->asyncAttributeValue() || 316 if (!m_parserInserted || client->asyncAttributeValue() ||
280 client->deferAttributeValue()) 317 client->deferAttributeValue())
281 defer = FetchRequest::LazyLoad; 318 defer = FetchRequest::LazyLoad;
282 if (m_documentWriteIntervention == 319 if (m_documentWriteIntervention ==
283 DocumentWriteIntervention::FetchDocWrittenScriptDeferIdle) 320 DocumentWriteIntervention::FetchDocWrittenScriptDeferIdle)
284 defer = FetchRequest::IdleLoad; 321 defer = FetchRequest::IdleLoad;
285 if (!fetchScript(client->sourceAttributeValue(), encoding, defer)) 322 if (!fetchScript(client->sourceAttributeValue(), encoding, defer))
286 return false; 323 return false;
287 } 324 }
288 325
326 // [Intervention]
289 // Since the asynchronous, low priority fetch for doc.written blocked 327 // Since the asynchronous, low priority fetch for doc.written blocked
290 // script is not for execution, return early from here. Watch for its 328 // script is not for execution, return early from here. Watch for its
291 // completion to be able to remove it from the memory cache. 329 // completion to be able to remove it from the memory cache.
292 if (m_documentWriteIntervention == 330 if (m_documentWriteIntervention ==
293 DocumentWriteIntervention::FetchDocWrittenScriptDeferIdle) { 331 DocumentWriteIntervention::FetchDocWrittenScriptDeferIdle) {
294 m_pendingScript = PendingScript::create(m_element, m_resource.get()); 332 m_pendingScript = PendingScript::create(m_element, m_resource.get());
295 m_pendingScript->watchForLoad(this); 333 m_pendingScript->watchForLoad(this);
296 return true; 334 return true;
297 } 335 }
298 336
337 // 23. "Then, follow the first of the following options that describes the
338 // situation:"
339
340 // Three flags are used to instruct the caller of prepareScript() to execute
341 // a part of Step 23, when |m_willBeParserExecuted| is true:
342 // - |m_willBeParserExecuted|
343 // - |m_willExecuteWhenDocumentFinishedParsing|
344 // - |m_readyToBeParserExecuted|
345 // TODO(hiroshige): Clean up the dependency.
346
347 // 1st Clause:
348 // - "If the script's type is "classic", and
349 // the element has a src attribute, and the element has a defer attribute,
350 // and the element has been flagged as "parser-inserted",
351 // and the element does not have an async attribute"
352 // TODO(hiroshige): Check the script's type and implement "module" case.
299 if (client->hasSourceAttribute() && client->deferAttributeValue() && 353 if (client->hasSourceAttribute() && client->deferAttributeValue() &&
300 m_parserInserted && !client->asyncAttributeValue()) { 354 m_parserInserted && !client->asyncAttributeValue()) {
355 // This clause is implemented by the caller-side of prepareScript():
356 // - HTMLParserScriptRunner::requestDeferredScript(), and
357 // - TODO(hiroshige): Investigate XMLDocumentParser::endElementNs()
301 m_willExecuteWhenDocumentFinishedParsing = true; 358 m_willExecuteWhenDocumentFinishedParsing = true;
302 m_willBeParserExecuted = true; 359 m_willBeParserExecuted = true;
303 } else if (client->hasSourceAttribute() && m_parserInserted && 360
304 !client->asyncAttributeValue()) { 361 return true;
hiroshige 2017/02/13 22:59:54 I replaced if-then-else-if-else-if-... block with
362 }
363
364 // 2nd Clause:
365 // - "If the script's type is "classic",
366 // and the element has a src attribute,
367 // and the element has been flagged as "parser-inserted",
368 // and the element does not have an async attribute"
369 // TODO(hiroshige): Check the script's type.
370 if (client->hasSourceAttribute() && m_parserInserted &&
371 !client->asyncAttributeValue()) {
372 // This clause is implemented by the caller-side of prepareScript():
373 // - HTMLParserScriptRunner::requestParsingBlockingScript()
374 // - TODO(hiroshige): Investigate XMLDocumentParser::endElementNs()
305 m_willBeParserExecuted = true; 375 m_willBeParserExecuted = true;
306 } else if (!client->hasSourceAttribute() && m_parserInserted && 376
307 !elementDocument.isScriptExecutionReady()) { 377 return true;
378 }
379
380 // 5th Clause:
381 // TODO(hiroshige): Reorder the clauses to match the spec.
382 // - "If the element does not have a src attribute,
383 // and the element has been flagged as "parser-inserted",
384 // and either the parser that created the script is an XML parser
385 // or it's an HTML parser whose script nesting level is not greater than
386 // one,
387 // and the Document of the HTML parser or XML parser that created
388 // the script element has a style sheet that is blocking scripts"
389 // The last part "... has a style sheet that is blocking scripts"
390 // is implemented in Document::isScriptExecutionReady().
391 // Part of the condition check is done in
392 // HTMLParserScriptRunner::processScriptElementInternal().
393 // TODO(hiroshige): Clean up the split condition check.
394 if (!client->hasSourceAttribute() && m_parserInserted &&
395 !elementDocument.isScriptExecutionReady()) {
396 // The former part of this clause is
397 // implemented by the caller-side of prepareScript():
398 // - HTMLParserScriptRunner::requestParsingBlockingScript()
399 // - TODO(hiroshige): Investigate XMLDocumentParser::endElementNs()
308 m_willBeParserExecuted = true; 400 m_willBeParserExecuted = true;
401 // "Set the element's "ready to be parser-executed" flag."
309 m_readyToBeParserExecuted = true; 402 m_readyToBeParserExecuted = true;
310 } else if (client->hasSourceAttribute() && !client->asyncAttributeValue() && 403
311 !m_nonBlocking) { 404 return true;
405 }
406
407 // 3rd Clause:
408 // - "If the script's type is "classic",
409 // and the element has a src attribute,
410 // and the element does not have an async attribute,
411 // and the element does not have the "non-blocking" flag set"
412 // TODO(hiroshige): Check the script's type and implement "module" case.
413 if (client->hasSourceAttribute() && !client->asyncAttributeValue() &&
414 !m_nonBlocking) {
415 // "Add the element to the end of the list of scripts that will execute
416 // in order as soon as possible associated with the node document of the
417 // script element at the time the prepare a script algorithm started."
312 m_pendingScript = PendingScript::create(m_element, m_resource.get()); 418 m_pendingScript = PendingScript::create(m_element, m_resource.get());
313 m_asyncExecType = ScriptRunner::InOrder; 419 m_asyncExecType = ScriptRunner::InOrder;
420 // TODO(hiroshige): Here |contextDocument| is used as "node document"
421 // while Step 14 uses |elementDocument| as "node document". Fix this.
314 contextDocument->scriptRunner()->queueScriptForExecution(this, 422 contextDocument->scriptRunner()->queueScriptForExecution(this,
315 m_asyncExecType); 423 m_asyncExecType);
316 // Note that watchForLoad can immediately call pendingScriptFinished. 424 // Note that watchForLoad can immediately call pendingScriptFinished.
317 m_pendingScript->watchForLoad(this); 425 m_pendingScript->watchForLoad(this);
318 } else if (client->hasSourceAttribute()) { 426 // The part "When the script is ready..." is implemented in
427 // ScriptRunner::notifyScriptReady().
428 // TODO(hiroshige): Annotate it.
429
430 return true;
431 }
432
433 // 4th Clause:
434 // - "If the script's type is "classic", and the element has a src attribute"
435 // TODO(hiroshige): Check the script's type and implement "module" case.
436 if (client->hasSourceAttribute()) {
437 // "The element must be added to the set of scripts that will execute
438 // as soon as possible of the node document of the script element at the
439 // time the prepare a script algorithm started."
319 m_pendingScript = PendingScript::create(m_element, m_resource.get()); 440 m_pendingScript = PendingScript::create(m_element, m_resource.get());
320 m_asyncExecType = ScriptRunner::Async; 441 m_asyncExecType = ScriptRunner::Async;
321 LocalFrame* frame = m_element->document().frame(); 442 LocalFrame* frame = m_element->document().frame();
322 if (frame) { 443 if (frame) {
323 ScriptState* scriptState = ScriptState::forMainWorld(frame); 444 ScriptState* scriptState = ScriptState::forMainWorld(frame);
324 if (scriptState) 445 if (scriptState)
325 ScriptStreamer::startStreaming( 446 ScriptStreamer::startStreaming(
326 m_pendingScript.get(), ScriptStreamer::Async, frame->settings(), 447 m_pendingScript.get(), ScriptStreamer::Async, frame->settings(),
327 scriptState, frame->frameScheduler()->loadingTaskRunner()); 448 scriptState, frame->frameScheduler()->loadingTaskRunner());
328 } 449 }
450 // TODO(hiroshige): Here |contextDocument| is used as "node document"
451 // while Step 14 uses |elementDocument| as "node document". Fix this.
329 contextDocument->scriptRunner()->queueScriptForExecution(this, 452 contextDocument->scriptRunner()->queueScriptForExecution(this,
330 m_asyncExecType); 453 m_asyncExecType);
331 // Note that watchForLoad can immediately call pendingScriptFinished. 454 // Note that watchForLoad can immediately call pendingScriptFinished.
332 m_pendingScript->watchForLoad(this); 455 m_pendingScript->watchForLoad(this);
333 } else { 456 // The part "When the script is ready..." is implemented in
334 // Reset line numbering for nested writes. 457 // ScriptRunner::notifyScriptReady().
335 TextPosition position = elementDocument.isInDocumentWrite() 458 // TODO(hiroshige): Annotate it.
336 ? TextPosition() 459
337 : scriptStartPosition; 460 return true;
338 KURL scriptURL = (!elementDocument.isInDocumentWrite() && m_parserInserted) 461 }
339 ? elementDocument.url() 462
340 : KURL(); 463 // 6th Clause:
341 if (!executeScript( 464 // - "Otherwise"
342 ScriptSourceCode(scriptContent(), scriptURL, position))) { 465 // "Immediately execute the script block,
343 dispatchErrorEvent(); 466 // even if other scripts are already executing."
344 return false; 467 // Note: this block is also duplicated in
345 } 468 // HTMLParserScriptRunner::processScriptElementInternal().
469 // TODO(hiroshige): Merge the duplicated code.
470
471 // Reset line numbering for nested writes.
472 TextPosition position = elementDocument.isInDocumentWrite()
473 ? TextPosition()
474 : scriptStartPosition;
475 KURL scriptURL = (!elementDocument.isInDocumentWrite() && m_parserInserted)
476 ? elementDocument.url()
477 : KURL();
478 if (!executeScript(ScriptSourceCode(scriptContent(), scriptURL, position))) {
479 dispatchErrorEvent();
480 return false;
346 } 481 }
347 482
348 return true; 483 return true;
349 } 484 }
350 485
486 // Steps 15--21 of https://html.spec.whatwg.org/#prepare-a-script
351 bool ScriptLoader::fetchScript(const String& sourceUrl, 487 bool ScriptLoader::fetchScript(const String& sourceUrl,
352 const String& encoding, 488 const String& encoding,
353 FetchRequest::DeferOption defer) { 489 FetchRequest::DeferOption defer) {
354 DCHECK(m_element); 490 DCHECK(m_element);
355 491
356 Document* elementDocument = &(m_element->document()); 492 Document* elementDocument = &(m_element->document());
357 if (!m_element->isConnected() || m_element->document() != elementDocument) 493 if (!m_element->isConnected() || m_element->document() != elementDocument)
358 return false; 494 return false;
359 495
360 DCHECK(!m_resource); 496 DCHECK(!m_resource);
(...skipping 264 matching lines...) Expand 10 before | Expand all | Expand 10 after
625 } 761 }
626 contextDocument->scriptRunner()->notifyScriptReady(this, m_asyncExecType); 762 contextDocument->scriptRunner()->notifyScriptReady(this, m_asyncExecType);
627 m_pendingScript->stopWatchingForLoad(); 763 m_pendingScript->stopWatchingForLoad();
628 } 764 }
629 765
630 bool ScriptLoader::ignoresLoadRequest() const { 766 bool ScriptLoader::ignoresLoadRequest() const {
631 return m_alreadyStarted || m_isExternalScript || m_parserInserted || 767 return m_alreadyStarted || m_isExternalScript || m_parserInserted ||
632 !element() || !element()->isConnected(); 768 !element() || !element()->isConnected();
633 } 769 }
634 770
771 // Step 13 of https://html.spec.whatwg.org/#prepare-a-script
635 bool ScriptLoader::isScriptForEventSupported() const { 772 bool ScriptLoader::isScriptForEventSupported() const {
773 // 1. "Let for be the value of the for attribute."
636 String eventAttribute = client()->eventAttributeValue(); 774 String eventAttribute = client()->eventAttributeValue();
775 // 2. "Let event be the value of the event attribute."
637 String forAttribute = client()->forAttributeValue(); 776 String forAttribute = client()->forAttributeValue();
777
778 // "If the script element has an event attribute and a for attribute, and
779 // the script's type is "classic", then run these substeps:"
780 // TODO(hiroshige): Check the script's type.
638 if (eventAttribute.isNull() || forAttribute.isNull()) 781 if (eventAttribute.isNull() || forAttribute.isNull())
639 return true; 782 return true;
640 783
784 // 3. "Strip leading and trailing ASCII whitespace from event and for."
641 forAttribute = forAttribute.stripWhiteSpace(); 785 forAttribute = forAttribute.stripWhiteSpace();
786 // 4. "If for is not an ASCII case-insensitive match for the string
787 // "window",
788 // then abort these steps at this point. The script is not executed."
642 if (!equalIgnoringCase(forAttribute, "window")) 789 if (!equalIgnoringCase(forAttribute, "window"))
643 return false; 790 return false;
644 eventAttribute = eventAttribute.stripWhiteSpace(); 791 eventAttribute = eventAttribute.stripWhiteSpace();
792 // 5. "If event is not an ASCII case-insensitive match for either the
793 // string "onload" or the string "onload()",
794 // then abort these steps at this point. The script is not executed.
645 return equalIgnoringCase(eventAttribute, "onload") || 795 return equalIgnoringCase(eventAttribute, "onload") ||
646 equalIgnoringCase(eventAttribute, "onload()"); 796 equalIgnoringCase(eventAttribute, "onload()");
647 } 797 }
648 798
649 String ScriptLoader::scriptContent() const { 799 String ScriptLoader::scriptContent() const {
650 return m_element->textFromChildren(); 800 return m_element->textFromChildren();
651 } 801 }
652 802
653 ScriptLoaderClient* ScriptLoader::client() const { 803 ScriptLoaderClient* ScriptLoader::client() const {
654 if (isHTMLScriptLoader(m_element)) 804 if (isHTMLScriptLoader(m_element))
(...skipping 10 matching lines...) Expand all
665 if (isHTMLScriptLoader(element)) 815 if (isHTMLScriptLoader(element))
666 return toHTMLScriptElement(element)->loader(); 816 return toHTMLScriptElement(element)->loader();
667 817
668 if (isSVGScriptLoader(element)) 818 if (isSVGScriptLoader(element))
669 return toSVGScriptElement(element)->loader(); 819 return toSVGScriptElement(element)->loader();
670 820
671 return 0; 821 return 0;
672 } 822 }
673 823
674 } // namespace blink 824 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698