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

Side by Side Diff: Source/core/dom/ScriptElement.cpp

Issue 18261015: Rename ScriptElement to ScriptLoader (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 7 years, 5 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 | Annotate | Revision Log
« no previous file with comments | « Source/core/dom/ScriptElement.h ('k') | Source/core/dom/ScriptLoader.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserv ed.
6 * Copyright (C) 2008 Nikolas Zimmermann <zimmermann@kde.org>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #include "config.h"
25 #include "core/dom/ScriptElement.h"
26
27 #include "HTMLNames.h"
28 #include "SVGNames.h"
29 #include "bindings/v8/ScriptController.h"
30 #include "bindings/v8/ScriptSourceCode.h"
31 #include "core/dom/Document.h"
32 #include "core/dom/Event.h"
33 #include "core/dom/IgnoreDestructiveWriteCountIncrementer.h"
34 #include "core/dom/ScriptRunner.h"
35 #include "core/dom/ScriptableDocumentParser.h"
36 #include "core/dom/Text.h"
37 #include "core/html/HTMLScriptElement.h"
38 #include "core/html/parser/HTMLParserIdioms.h"
39 #include "core/loader/cache/CachedResourceLoader.h"
40 #include "core/loader/cache/CachedResourceRequest.h"
41 #include "core/loader/cache/CachedScript.h"
42 #include "core/page/ContentSecurityPolicy.h"
43 #include "core/page/Frame.h"
44 #include "core/platform/MIMETypeRegistry.h"
45 #include "core/svg/SVGScriptElement.h"
46 #include "weborigin/SecurityOrigin.h"
47 #include "wtf/StdLibExtras.h"
48 #include "wtf/text/StringBuilder.h"
49 #include "wtf/text/StringHash.h"
50 #include "wtf/text/TextPosition.h"
51
52 namespace WebCore {
53
54 ScriptElement::ScriptElement(Element* element, bool parserInserted, bool already Started)
55 : m_element(element)
56 , m_cachedScript(0)
57 , m_startLineNumber(WTF::OrdinalNumber::beforeFirst())
58 , m_parserInserted(parserInserted)
59 , m_isExternalScript(false)
60 , m_alreadyStarted(alreadyStarted)
61 , m_haveFiredLoad(false)
62 , m_willBeParserExecuted(false)
63 , m_readyToBeParserExecuted(false)
64 , m_willExecuteWhenDocumentFinishedParsing(false)
65 , m_forceAsync(!parserInserted)
66 , m_willExecuteInOrder(false)
67 {
68 ASSERT(m_element);
69 if (parserInserted && element->document()->scriptableDocumentParser() && !el ement->document()->isInDocumentWrite())
70 m_startLineNumber = element->document()->scriptableDocumentParser()->lin eNumber();
71 }
72
73 ScriptElement::~ScriptElement()
74 {
75 stopLoadRequest();
76 }
77
78 void ScriptElement::insertedInto(ContainerNode* insertionPoint)
79 {
80 if (insertionPoint->inDocument() && !m_parserInserted)
81 prepareScript(); // FIXME: Provide a real starting line number here.
82 }
83
84 void ScriptElement::childrenChanged()
85 {
86 if (!m_parserInserted && m_element->inDocument())
87 prepareScript(); // FIXME: Provide a real starting line number here.
88 }
89
90 void ScriptElement::handleSourceAttribute(const String& sourceUrl)
91 {
92 if (ignoresLoadRequest() || sourceUrl.isEmpty())
93 return;
94
95 prepareScript(); // FIXME: Provide a real starting line number here.
96 }
97
98 void ScriptElement::handleAsyncAttribute()
99 {
100 m_forceAsync = false;
101 }
102
103 // Helper function
104 static bool isLegacySupportedJavaScriptLanguage(const String& language)
105 {
106 // Mozilla 1.8 accepts javascript1.0 - javascript1.7, but WinIE 7 accepts on ly javascript1.1 - javascript1.3.
107 // Mozilla 1.8 and WinIE 7 both accept javascript and livescript.
108 // WinIE 7 accepts ecmascript and jscript, but Mozilla 1.8 doesn't.
109 // Neither Mozilla 1.8 nor WinIE 7 accept leading or trailing whitespace.
110 // We want to accept all the values that either of these browsers accept, bu t not other values.
111
112 // FIXME: This function is not HTML5 compliant. These belong in the MIME reg istry as "text/javascript<version>" entries.
113 typedef HashSet<String, CaseFoldingHash> LanguageSet;
114 DEFINE_STATIC_LOCAL(LanguageSet, languages, ());
115 if (languages.isEmpty()) {
116 languages.add("javascript");
117 languages.add("javascript");
118 languages.add("javascript1.0");
119 languages.add("javascript1.1");
120 languages.add("javascript1.2");
121 languages.add("javascript1.3");
122 languages.add("javascript1.4");
123 languages.add("javascript1.5");
124 languages.add("javascript1.6");
125 languages.add("javascript1.7");
126 languages.add("livescript");
127 languages.add("ecmascript");
128 languages.add("jscript");
129 }
130
131 return languages.contains(language);
132 }
133
134 void ScriptElement::dispatchErrorEvent()
135 {
136 m_element->dispatchEvent(Event::create(eventNames().errorEvent, false, false ));
137 }
138
139 void ScriptElement::dispatchLoadEvent()
140 {
141 ASSERT(!haveFiredLoadEvent());
142 if (ScriptElementClient* client = this->client())
143 client->dispatchLoadEvent();
144 setHaveFiredLoadEvent(true);
145 }
146
147 bool ScriptElement::isScriptTypeSupported(LegacyTypeSupport supportLegacyTypes) const
148 {
149 // FIXME: isLegacySupportedJavaScriptLanguage() is not valid HTML5. It is us ed here to maintain backwards compatibility with existing layout tests. The spec ific violations are:
150 // - Allowing type=javascript. type= should only support MIME types, such as text/javascript.
151 // - Allowing a different set of languages for language= and type=. language = supports Javascript 1.1 and 1.4-1.6, but type= does not.
152
153 String type = client()->typeAttributeValue();
154 String language = client()->languageAttributeValue();
155 if (type.isEmpty() && language.isEmpty())
156 return true; // Assume text/javascript.
157 if (type.isEmpty()) {
158 type = "text/" + language.lower();
159 if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(type) || isLegacySup portedJavaScriptLanguage(language))
160 return true;
161 } else if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(type.stripWhiteSp ace().lower()) || (supportLegacyTypes == AllowLegacyTypeInTypeAttribute && isLeg acySupportedJavaScriptLanguage(type)))
162 return true;
163 return false;
164 }
165
166 // http://dev.w3.org/html5/spec/Overview.html#prepare-a-script
167 bool ScriptElement::prepareScript(const TextPosition& scriptStartPosition, Legac yTypeSupport supportLegacyTypes)
168 {
169 if (m_alreadyStarted)
170 return false;
171
172 ScriptElementClient* client = this->client();
173
174 bool wasParserInserted;
175 if (m_parserInserted) {
176 wasParserInserted = true;
177 m_parserInserted = false;
178 } else
179 wasParserInserted = false;
180
181 if (wasParserInserted && !client->asyncAttributeValue())
182 m_forceAsync = true;
183
184 // FIXME: HTML5 spec says we should check that all children are either comme nts or empty text nodes.
185 if (!client->hasSourceAttribute() && !m_element->firstChild())
186 return false;
187
188 if (!m_element->inDocument())
189 return false;
190
191 if (!isScriptTypeSupported(supportLegacyTypes))
192 return false;
193
194 if (wasParserInserted) {
195 m_parserInserted = true;
196 m_forceAsync = false;
197 }
198
199 m_alreadyStarted = true;
200
201 // FIXME: If script is parser inserted, verify it's still in the original do cument.
202 Document* document = m_element->document();
203
204 // FIXME: Eventually we'd like to evaluate scripts which are inserted into a
205 // viewless document but this'll do for now.
206 // See http://bugs.webkit.org/show_bug.cgi?id=5727
207 if (!document->frame())
208 return false;
209
210 if (!document->frame()->script()->canExecuteScripts(AboutToExecuteScript))
211 return false;
212
213 if (!isScriptForEventSupported())
214 return false;
215
216 if (!client->charsetAttributeValue().isEmpty())
217 m_characterEncoding = client->charsetAttributeValue();
218 else
219 m_characterEncoding = document->charset();
220
221 if (client->hasSourceAttribute())
222 if (!requestScript(client->sourceAttributeValue()))
223 return false;
224
225 if (client->hasSourceAttribute() && client->deferAttributeValue() && m_parse rInserted && !client->asyncAttributeValue()) {
226 m_willExecuteWhenDocumentFinishedParsing = true;
227 m_willBeParserExecuted = true;
228 } else if (client->hasSourceAttribute() && m_parserInserted && !client->asyn cAttributeValue())
229 m_willBeParserExecuted = true;
230 else if (!client->hasSourceAttribute() && m_parserInserted && !document->hav eStylesheetsAndImportsLoaded()) {
231 m_willBeParserExecuted = true;
232 m_readyToBeParserExecuted = true;
233 } else if (client->hasSourceAttribute() && !client->asyncAttributeValue() && !m_forceAsync) {
234 m_willExecuteInOrder = true;
235 document->scriptRunner()->queueScriptForExecution(this, m_cachedScript, ScriptRunner::IN_ORDER_EXECUTION);
236 m_cachedScript->addClient(this);
237 } else if (client->hasSourceAttribute()) {
238 document->scriptRunner()->queueScriptForExecution(this, m_cachedScript, ScriptRunner::ASYNC_EXECUTION);
239 m_cachedScript->addClient(this);
240 } else {
241 // Reset line numbering for nested writes.
242 TextPosition position = document->isInDocumentWrite() ? TextPosition() : scriptStartPosition;
243 KURL scriptURL = (!document->isInDocumentWrite() && m_parserInserted) ? document->url() : KURL();
244 executeScript(ScriptSourceCode(scriptContent(), scriptURL, position));
245 }
246
247 return true;
248 }
249
250 bool ScriptElement::requestScript(const String& sourceUrl)
251 {
252 ASSERT(m_element);
253
254 RefPtr<Document> originalDocument = m_element->document();
255 if (!m_element->dispatchBeforeLoadEvent(sourceUrl))
256 return false;
257 if (!m_element->inDocument() || m_element->document() != originalDocument)
258 return false;
259
260 ASSERT(!m_cachedScript);
261 if (!stripLeadingAndTrailingHTMLSpaces(sourceUrl).isEmpty()) {
262 CachedResourceRequest request(ResourceRequest(m_element->document()->com pleteURL(sourceUrl)), m_element->localName());
263
264 String crossOriginMode = m_element->fastGetAttribute(HTMLNames::crossori ginAttr);
265 if (!crossOriginMode.isNull()) {
266 StoredCredentials allowCredentials = equalIgnoringCase(crossOriginMo de, "use-credentials") ? AllowStoredCredentials : DoNotAllowStoredCredentials;
267 request.setPotentiallyCrossOriginEnabled(m_element->document()->secu rityOrigin(), allowCredentials);
268 }
269 request.setCharset(scriptCharset());
270
271 bool isValidScriptNonce = m_element->document()->contentSecurityPolicy() ->allowScriptNonce(m_element->fastGetAttribute(HTMLNames::nonceAttr));
272 if (isValidScriptNonce)
273 request.setContentSecurityCheck(DoNotCheckContentSecurityPolicy);
274
275 m_cachedScript = m_element->document()->cachedResourceLoader()->requestS cript(request);
276 m_isExternalScript = true;
277 }
278
279 if (m_cachedScript) {
280 return true;
281 }
282
283 dispatchErrorEvent();
284 return false;
285 }
286
287 bool isHTMLScriptElement(Element* element)
288 {
289 return element->hasTagName(HTMLNames::scriptTag);
290 }
291
292 bool isSVGScriptElement(Element* element)
293 {
294 return element->hasTagName(SVGNames::scriptTag);
295 }
296
297 void ScriptElement::executeScript(const ScriptSourceCode& sourceCode)
298 {
299 ASSERT(m_alreadyStarted);
300
301 if (sourceCode.isEmpty())
302 return;
303
304 RefPtr<Document> document = m_element->document();
305 Frame* frame = document->frame();
306
307 bool shouldBypassMainWorldContentSecurityPolicy = (frame && frame->script()- >shouldBypassMainWorldContentSecurityPolicy()) || document->contentSecurityPolic y()->allowScriptNonce(m_element->fastGetAttribute(HTMLNames::nonceAttr));
308
309 if (!m_isExternalScript && (!shouldBypassMainWorldContentSecurityPolicy && ! document->contentSecurityPolicy()->allowInlineScript(document->url(), m_startLin eNumber)))
310 return;
311
312 if (m_isExternalScript && m_cachedScript && !m_cachedScript->mimeTypeAllowed ByNosniff()) {
313 document->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "R efused to execute script from '" + m_cachedScript->url().elidedString() + "' bec ause its MIME type ('" + m_cachedScript->mimeType() + "') is not executable, and strict MIME type checking is enabled.");
314 return;
315 }
316
317 if (frame) {
318 {
319 IgnoreDestructiveWriteCountIncrementer ignoreDesctructiveWriteCountI ncrementer(m_isExternalScript ? document.get() : 0);
320
321 if (isHTMLScriptElement(m_element))
322 document->pushCurrentScript(toHTMLScriptElement(m_element));
323
324 // Create a script from the script element node, using the script
325 // block's source and the script block's type.
326 // Note: This is where the script is compiled and actually executed.
327 frame->script()->executeScriptInMainWorld(sourceCode);
328
329 if (isHTMLScriptElement(m_element)) {
330 ASSERT(document->currentScript() == m_element);
331 document->popCurrentScript();
332 }
333 }
334 }
335 }
336
337 void ScriptElement::stopLoadRequest()
338 {
339 if (m_cachedScript) {
340 if (!m_willBeParserExecuted)
341 m_cachedScript->removeClient(this);
342 m_cachedScript = 0;
343 }
344 }
345
346 void ScriptElement::execute(CachedScript* cachedScript)
347 {
348 ASSERT(!m_willBeParserExecuted);
349 ASSERT(cachedScript);
350 if (cachedScript->errorOccurred())
351 dispatchErrorEvent();
352 else if (!cachedScript->wasCanceled()) {
353 executeScript(ScriptSourceCode(cachedScript));
354 dispatchLoadEvent();
355 }
356 cachedScript->removeClient(this);
357 }
358
359 void ScriptElement::notifyFinished(CachedResource* resource)
360 {
361 ASSERT(!m_willBeParserExecuted);
362
363 // CachedResource possibly invokes this notifyFinished() more than
364 // once because ScriptElement doesn't unsubscribe itself from
365 // CachedResource here and does it in execute() instead.
366 // We use m_cachedScript to check if this function is already called.
367 ASSERT_UNUSED(resource, resource == m_cachedScript);
368 if (!m_cachedScript)
369 return;
370 if (!m_element->document()->cachedResourceLoader()->canAccess(m_cachedScript .get())) {
371 dispatchErrorEvent();
372 return;
373 }
374
375 if (m_willExecuteInOrder)
376 m_element->document()->scriptRunner()->notifyScriptReady(this, ScriptRun ner::IN_ORDER_EXECUTION);
377 else
378 m_element->document()->scriptRunner()->notifyScriptReady(this, ScriptRun ner::ASYNC_EXECUTION);
379
380 m_cachedScript = 0;
381 }
382
383 bool ScriptElement::ignoresLoadRequest() const
384 {
385 return m_alreadyStarted || m_isExternalScript || m_parserInserted || !elemen t() || !element()->inDocument();
386 }
387
388 bool ScriptElement::isScriptForEventSupported() const
389 {
390 String eventAttribute = client()->eventAttributeValue();
391 String forAttribute = client()->forAttributeValue();
392 if (!eventAttribute.isEmpty() && !forAttribute.isEmpty()) {
393 forAttribute = forAttribute.stripWhiteSpace();
394 if (!equalIgnoringCase(forAttribute, "window"))
395 return false;
396
397 eventAttribute = eventAttribute.stripWhiteSpace();
398 if (!equalIgnoringCase(eventAttribute, "onload") && !equalIgnoringCase(e ventAttribute, "onload()"))
399 return false;
400 }
401 return true;
402 }
403
404 String ScriptElement::scriptContent() const
405 {
406 return m_element->textFromChildren();
407 }
408
409 ScriptElementClient* ScriptElement::client() const
410 {
411 if (isHTMLScriptElement(m_element))
412 return toHTMLScriptElement(m_element);
413
414 if (isSVGScriptElement(m_element))
415 return toSVGScriptElement(m_element);
416
417 ASSERT_NOT_REACHED();
418 return 0;
419 }
420
421 ScriptElement* toScriptElementIfPossible(Element* element)
422 {
423 if (isHTMLScriptElement(element))
424 return toHTMLScriptElement(element)->scriptElement();
425
426 if (isSVGScriptElement(element))
427 return toSVGScriptElement(element)->scriptElement();
428
429 return 0;
430 }
431
432 }
OLDNEW
« no previous file with comments | « Source/core/dom/ScriptElement.h ('k') | Source/core/dom/ScriptLoader.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698