OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |