1/*
2 * Copyright (C) 2016-2017 Apple, Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "LoadableClassicScript.h"
28
29#include "FetchIdioms.h"
30#include "ScriptElement.h"
31#include "ScriptSourceCode.h"
32#include "SubresourceIntegrity.h"
33#include <wtf/NeverDestroyed.h>
34#include <wtf/text/StringImpl.h>
35
36namespace WebCore {
37
38Ref<LoadableClassicScript> LoadableClassicScript::create(const String& nonce, const String& integrityMetadata, const String& crossOriginMode, const String& charset, const AtomString& initiatorName, bool isInUserAgentShadowTree)
39{
40 return adoptRef(*new LoadableClassicScript(nonce, integrityMetadata, crossOriginMode, charset, initiatorName, isInUserAgentShadowTree));
41}
42
43LoadableClassicScript::~LoadableClassicScript()
44{
45 if (m_cachedScript)
46 m_cachedScript->removeClient(*this);
47}
48
49bool LoadableClassicScript::isLoaded() const
50{
51 ASSERT(m_cachedScript);
52 return m_cachedScript->isLoaded();
53}
54
55Optional<LoadableScript::Error> LoadableClassicScript::error() const
56{
57 ASSERT(m_cachedScript);
58 if (m_error)
59 return m_error;
60
61 if (m_cachedScript->errorOccurred())
62 return Error { ErrorType::CachedScript, WTF::nullopt };
63
64 return WTF::nullopt;
65}
66
67bool LoadableClassicScript::wasCanceled() const
68{
69 ASSERT(m_cachedScript);
70 return m_cachedScript->wasCanceled();
71}
72
73void LoadableClassicScript::notifyFinished(CachedResource& resource)
74{
75 ASSERT(m_cachedScript);
76 if (resource.resourceError().isAccessControl()) {
77 static NeverDestroyed<String> consoleMessage(MAKE_STATIC_STRING_IMPL("Cross-origin script load denied by Cross-Origin Resource Sharing policy."));
78 m_error = Error {
79 ErrorType::CrossOriginLoad,
80 ConsoleMessage {
81 MessageSource::JS,
82 MessageLevel::Error,
83 consoleMessage
84 }
85 };
86 }
87
88 if (!m_error && !isScriptAllowedByNosniff(m_cachedScript->response())) {
89 m_error = Error {
90 ErrorType::Nosniff,
91 ConsoleMessage {
92 MessageSource::Security,
93 MessageLevel::Error,
94 makeString("Refused to execute ", m_cachedScript->url().stringCenterEllipsizedToLength(), " as script because \"X-Content-Type: nosniff\" was given and its Content-Type is not a script MIME type.")
95 }
96 };
97 }
98
99 if (!m_error && shouldBlockResponseDueToMIMEType(m_cachedScript->response(), m_cachedScript->options().destination)) {
100 m_error = Error {
101 ErrorType::MIMEType,
102 ConsoleMessage {
103 MessageSource::Security,
104 MessageLevel::Error,
105 makeString("Refused to execute ", m_cachedScript->url().stringCenterEllipsizedToLength(), " as script because ", m_cachedScript->response().mimeType(), " is not a script MIME type.")
106 }
107 };
108 }
109
110 if (!m_error && !resource.errorOccurred() && !matchIntegrityMetadata(resource, m_integrity)) {
111 m_error = Error {
112 ErrorType::FailedIntegrityCheck,
113 ConsoleMessage { MessageSource::Security, MessageLevel::Error, makeString("Cannot load script ", m_cachedScript->url().stringCenterEllipsizedToLength(), ". Failed integrity metadata check.") }
114 };
115 }
116
117 notifyClientFinished();
118}
119
120void LoadableClassicScript::execute(ScriptElement& scriptElement)
121{
122 ASSERT(!error());
123 scriptElement.executeClassicScript(ScriptSourceCode(m_cachedScript.get(), JSC::SourceProviderSourceType::Program, *this));
124}
125
126bool LoadableClassicScript::load(Document& document, const URL& sourceURL)
127{
128 ASSERT(!m_cachedScript);
129 m_cachedScript = requestScriptWithCache(document, sourceURL, crossOriginMode(), String { m_integrity });
130 if (!m_cachedScript)
131 return false;
132 m_cachedScript->addClient(*this);
133 return true;
134}
135
136}
137