OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "config.h" | 5 #include "config.h" |
6 #include "FetchManager.h" | 6 #include "FetchManager.h" |
7 | 7 |
| 8 #include "bindings/core/v8/ExceptionState.h" |
8 #include "bindings/core/v8/ScriptPromiseResolver.h" | 9 #include "bindings/core/v8/ScriptPromiseResolver.h" |
9 #include "bindings/core/v8/ScriptState.h" | 10 #include "bindings/core/v8/ScriptState.h" |
10 #include "bindings/core/v8/V8ThrowException.h" | 11 #include "bindings/core/v8/V8ThrowException.h" |
11 #include "core/dom/ExceptionCode.h" | 12 #include "core/dom/ExceptionCode.h" |
12 #include "core/fileapi/Blob.h" | 13 #include "core/fileapi/Blob.h" |
13 #include "core/loader/ThreadableLoader.h" | 14 #include "core/loader/ThreadableLoader.h" |
14 #include "core/loader/ThreadableLoaderClient.h" | 15 #include "core/loader/ThreadableLoaderClient.h" |
15 #include "core/xml/XMLHttpRequest.h" | 16 #include "core/xml/XMLHttpRequest.h" |
16 #include "modules/serviceworkers/Response.h" | 17 #include "modules/serviceworkers/Response.h" |
17 #include "modules/serviceworkers/ResponseInit.h" | 18 #include "modules/serviceworkers/ResponseInit.h" |
18 #include "platform/network/ResourceRequest.h" | 19 #include "platform/network/ResourceRequest.h" |
| 20 #include "platform/weborigin/SecurityOrigin.h" |
| 21 #include "public/platform/WebServiceWorkerRequest.h" |
19 #include "wtf/HashSet.h" | 22 #include "wtf/HashSet.h" |
20 | 23 |
21 namespace WebCore { | 24 namespace WebCore { |
22 | 25 |
23 class FetchManager::Loader : public ThreadableLoaderClient { | 26 class FetchManager::Loader : public ThreadableLoaderClient { |
24 public: | 27 public: |
25 Loader(ExecutionContext*, FetchManager*, PassRefPtr<ScriptPromiseResolver>,
PassOwnPtr<ResourceRequest>); | 28 Loader(ExecutionContext*, FetchManager*, PassRefPtr<ScriptPromiseResolver>,
PassRefPtr<FetchRequestData>); |
26 ~Loader(); | 29 ~Loader(); |
27 virtual void didReceiveResponse(unsigned long, const ResourceResponse&); | 30 virtual void didReceiveResponse(unsigned long, const ResourceResponse&); |
28 virtual void didFinishLoading(unsigned long, double); | 31 virtual void didFinishLoading(unsigned long, double); |
29 virtual void didFail(const ResourceError&); | 32 virtual void didFail(const ResourceError&); |
30 virtual void didFailAccessControlCheck(const ResourceError&); | 33 virtual void didFailAccessControlCheck(const ResourceError&); |
31 virtual void didFailRedirectCheck(); | 34 virtual void didFailRedirectCheck(); |
32 virtual void didDownloadData(int); | 35 virtual void didDownloadData(int); |
33 | 36 |
34 void start(); | 37 void start(); |
35 void cleanup(); | 38 void cleanup(); |
36 | 39 |
37 private: | 40 private: |
| 41 void performBasicFetch(); |
| 42 void performNetworkError(); |
| 43 void performeHTTPFetch(bool, bool); |
38 void failed(); | 44 void failed(); |
39 void notifyFinished(); | 45 void notifyFinished(); |
40 | 46 |
41 ExecutionContext* m_executionContext; | 47 ExecutionContext* m_executionContext; |
42 FetchManager* m_fetchManager; | 48 FetchManager* m_fetchManager; |
43 RefPtr<ScriptPromiseResolver> m_resolver; | 49 RefPtr<ScriptPromiseResolver> m_resolver; |
44 OwnPtr<ResourceRequest> m_request; | 50 RefPtr<FetchRequestData> m_request; |
45 RefPtr<ThreadableLoader> m_loader; | 51 RefPtr<ThreadableLoader> m_loader; |
46 ResourceResponse m_response; | 52 ResourceResponse m_response; |
47 long long m_downloadedBlobLength; | 53 long long m_downloadedBlobLength; |
48 bool m_failed; | 54 bool m_failed; |
49 }; | 55 }; |
50 | 56 |
51 FetchManager::Loader::Loader(ExecutionContext* executionContext, FetchManager* f
etchManager, PassRefPtr<ScriptPromiseResolver> resolver, PassOwnPtr<ResourceRequ
est> request) | 57 FetchManager::Loader::Loader(ExecutionContext* executionContext, FetchManager* f
etchManager, PassRefPtr<ScriptPromiseResolver> resolver, PassRefPtr<FetchRequest
Data> request) |
52 : m_executionContext(executionContext) | 58 : m_executionContext(executionContext) |
53 , m_fetchManager(fetchManager) | 59 , m_fetchManager(fetchManager) |
54 , m_resolver(resolver) | 60 , m_resolver(resolver) |
55 , m_request(request) | 61 , m_request(request) |
56 , m_downloadedBlobLength(0) | 62 , m_downloadedBlobLength(0) |
57 , m_failed(false) | 63 , m_failed(false) |
58 { | 64 { |
59 } | 65 } |
60 | 66 |
61 FetchManager::Loader::~Loader() | 67 FetchManager::Loader::~Loader() |
62 { | 68 { |
63 if (m_loader) | 69 if (m_loader) |
64 m_loader->cancel(); | 70 m_loader->cancel(); |
65 } | 71 } |
66 | 72 |
67 void FetchManager::Loader::didReceiveResponse(unsigned long, const ResourceRespo
nse& response) | 73 void FetchManager::Loader::didReceiveResponse(unsigned long, const ResourceRespo
nse& response) |
68 { | 74 { |
69 m_response = response; | 75 m_response = response; |
70 } | 76 } |
71 | 77 |
72 void FetchManager::Loader::didFinishLoading(unsigned long, double) | 78 void FetchManager::Loader::didFinishLoading(unsigned long, double) |
73 { | 79 { |
74 OwnPtr<BlobData> blobData = BlobData::create(); | 80 OwnPtr<BlobData> blobData = BlobData::create(); |
75 String filePath = m_response.downloadedFilePath(); | 81 String filePath = m_response.downloadedFilePath(); |
76 if (!filePath.isEmpty() && m_downloadedBlobLength) { | 82 if (!filePath.isEmpty() && m_downloadedBlobLength) { |
77 blobData->appendFile(filePath); | 83 blobData->appendFile(filePath); |
78 // FIXME: Set the ContentType correctly. | 84 // FIXME: Set the ContentType correctly. |
79 } | 85 } |
80 ResponseInit responseInit; | 86 RefPtr<FetchResponseData> response(FetchResponseData::create()); |
81 // FIXME: We may have to filter the status when we support CORS. | 87 response->setStatus(m_response.httpStatusCode()); |
82 // http://fetch.spec.whatwg.org/#concept-filtered-response-opaque | 88 response->setStatusMessage(m_response.httpStatusText()); |
83 responseInit.status = m_response.httpStatusCode(); | 89 HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end(); |
84 responseInit.statusText = m_response.httpStatusText(); | 90 for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(
); it != end; ++it) { |
85 // FIXME: fill options. | 91 response->headerList()->append(it->key, it->value); |
86 RefPtrWillBeRawPtr<Blob> blob = Blob::create(BlobDataHandle::create(blobData
.release(), m_downloadedBlobLength)); | 92 } |
87 // FIXME: Handle response status correctly. | 93 response->setBlobDataHandle(BlobDataHandle::create(blobData.release(), m_dow
nloadedBlobLength)); |
88 m_resolver->resolve(Response::create(blob.get(), responseInit)); | 94 response->setURL(m_request->url()); |
| 95 |
| 96 switch (m_request->tainting()) { |
| 97 case FetchRequestData::BasicTainting: |
| 98 response = response->createBasicFilteredResponse(); |
| 99 break; |
| 100 case FetchRequestData::CORSTainting: |
| 101 response = response->createCORSFilteredResponse(); |
| 102 break; |
| 103 case FetchRequestData::OpaqueTainting: |
| 104 response = response->createOpaqueFilteredResponse(); |
| 105 break; |
| 106 } |
| 107 m_resolver->resolve(Response::create(response.release())); |
89 notifyFinished(); | 108 notifyFinished(); |
90 } | 109 } |
91 | 110 |
92 void FetchManager::Loader::didFail(const ResourceError& error) | 111 void FetchManager::Loader::didFail(const ResourceError& error) |
93 { | 112 { |
94 failed(); | 113 failed(); |
95 } | 114 } |
96 | 115 |
97 void FetchManager::Loader::didFailAccessControlCheck(const ResourceError& error) | 116 void FetchManager::Loader::didFailAccessControlCheck(const ResourceError& error) |
98 { | 117 { |
99 failed(); | 118 failed(); |
100 } | 119 } |
101 | 120 |
102 void FetchManager::Loader::didFailRedirectCheck() | 121 void FetchManager::Loader::didFailRedirectCheck() |
103 { | 122 { |
104 failed(); | 123 failed(); |
105 } | 124 } |
106 | 125 |
107 void FetchManager::Loader::didDownloadData(int dataLength) | 126 void FetchManager::Loader::didDownloadData(int dataLength) |
108 { | 127 { |
109 m_downloadedBlobLength += dataLength; | 128 m_downloadedBlobLength += dataLength; |
110 } | 129 } |
111 | 130 |
112 void FetchManager::Loader::start() | 131 void FetchManager::Loader::start() |
113 { | 132 { |
114 m_request->setDownloadToFile(true); | 133 |
115 ThreadableLoaderOptions options; | 134 // FIXME: 1. If request's url contains a Known HSTS Host, modify it per the
requirements of the "URI [sic] Loading and Port Mapping" chapter of HTTP Strict
Transport Security. [HSTS] |
116 // FIXME: Fill options. | 135 |
117 ResourceLoaderOptions resourceLoaderOptions; | 136 // FIXME: 2. If request's referrer is not none, set request's referrer to th
e result of invoking determine request's referrer. [REFERRER] |
118 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData; | 137 |
119 // FIXME: Fill resourceLoaderOptions. | 138 // FIXME: 3. If request's synchronous flag is unset and fetch is not invoked
recursively, run the remaining steps asynchronously. |
120 m_loader = ThreadableLoader::create(*m_executionContext, this, *m_request, o
ptions, resourceLoaderOptions); | 139 |
| 140 // FIXME: 4. Let response be the value corresponding to the first matching s
tatement: |
| 141 // - should fetching request be blocked as mixed content returns blocked [MI
X] |
| 142 // - should fetching request be blocked as content security returns blocked
[CSP] |
| 143 // A network error. |
| 144 |
| 145 // - request's url's origin is request's origin and the CORS flag is unset |
| 146 // - request's url's scheme is "data" and request's same-origin data URL fla
g is set |
| 147 // - request's url's scheme is "about" |
| 148 if ((m_request->mode() != FetchRequestData::CORSMode |
| 149 && m_request->mode() != FetchRequestData::CORSWithForcedPreflight |
| 150 && SecurityOrigin::create(m_request->url())->isSameSchemeHostPort(m_requ
est->origin().get())) |
| 151 || (m_request->url().protocol() == "data" && m_request->sameOriginDataUR
LFlag()) |
| 152 || (m_request->url().protocol() == "about")) { |
| 153 // The result of performing a basic fetch using request. |
| 154 performBasicFetch(); |
| 155 return; |
| 156 } |
| 157 |
| 158 // - request's mode is same-origin |
| 159 if (m_request->mode() == FetchRequestData::SameOriginMode) { |
| 160 // A network error. |
| 161 performNetworkError(); |
| 162 return; |
| 163 } |
| 164 |
| 165 // - request's mode is no CORS |
| 166 if (m_request->mode() == FetchRequestData::NoCORSMode) { |
| 167 // Set request's response tainting to opaque. |
| 168 m_request->setResponseTainting(FetchRequestData::OpaqueTainting); |
| 169 // The result of performing a basic fetch using request. |
| 170 performBasicFetch(); |
| 171 return; |
| 172 } |
| 173 |
| 174 // - request's url's scheme is not one of "http" and "https" |
| 175 if (m_request->url().protocol() != "http" && m_request->url().protocol() !=
"https") { |
| 176 // A network error. |
| 177 performNetworkError(); |
| 178 return; |
| 179 } |
| 180 |
| 181 // - request's mode is CORS-with-forced-preflight |
| 182 if (m_request->mode() == FetchRequestData::CORSWithForcedPreflight) { |
| 183 // Set request's response tainting to CORS. |
| 184 m_request->setResponseTainting(FetchRequestData::CORSTainting); |
| 185 // The result of performing an HTTP fetch using request with the CORS fl
ag and CORS preflight flag set. |
| 186 performeHTTPFetch(true, true); |
| 187 return; |
| 188 } |
| 189 |
| 190 // - request's unsafe request flag is set and either request's method is not
a simple method or a header in request's header list is not a simple header |
| 191 if (m_request->unsafeRequestFlag() |
| 192 && (!isSimpleMethod(m_request->method()) |
| 193 || m_request->headerList()->containsNonSimpleHeader())) { |
| 194 // Set request's response tainting to CORS. |
| 195 m_request->setResponseTainting(FetchRequestData::CORSTainting); |
| 196 // The result of performing an HTTP fetch using request with the CORS fl
ag and CORS preflight flag set. |
| 197 performeHTTPFetch(true, true); |
| 198 return; |
| 199 } |
| 200 |
| 201 // - Otherwise |
| 202 // Set request's response tainting to CORS. |
| 203 m_request->setResponseTainting(FetchRequestData::CORSTainting); |
| 204 // The result of performing an HTTP fetch using request with the CORS fl
ag set. |
| 205 performeHTTPFetch(true, false); |
121 } | 206 } |
122 | 207 |
123 void FetchManager::Loader::cleanup() | 208 void FetchManager::Loader::cleanup() |
124 { | 209 { |
125 // Prevent notification | 210 // Prevent notification |
126 m_fetchManager = 0; | 211 m_fetchManager = 0; |
127 | 212 |
128 if (m_loader) { | 213 if (m_loader) { |
129 m_loader->cancel(); | 214 m_loader->cancel(); |
130 m_loader.clear(); | 215 m_loader.clear(); |
131 } | 216 } |
132 } | 217 } |
133 | 218 |
| 219 void FetchManager::Loader::performBasicFetch() |
| 220 { |
| 221 // To perform a basic fetch using request, switch on request's url's scheme,
and run the associated steps: |
| 222 String protocol(m_request->url().protocol()); |
| 223 if (protocol == "about") { |
| 224 // If request's url's scheme data is "blank", return a response whose |
| 225 // header list consist of a single header whose name is `Content-Type` |
| 226 // and value is `text/html;charset=utf-8`, and body is the empty byte |
| 227 // sequence. |
| 228 // FIXME: implement this. |
| 229 performNetworkError(); |
| 230 } else if (protocol == "blob") { |
| 231 // It has been argued this should be handled outside of fetching. |
| 232 performNetworkError(); |
| 233 } else if (protocol == "data") { |
| 234 // If request's method is `GET` and obtaining a resource from request's |
| 235 // url does not return failure, return a response whose header list |
| 236 // consist of a single header whose name is `Content-Type` and value is |
| 237 // the MIME type and parameters returned from obtaining a resource, and |
| 238 // body is the data returned from obtaining a resource. [DATAURL] |
| 239 // Otherwise, return a network error. |
| 240 // FIXME: implement this. |
| 241 performNetworkError(); |
| 242 } else if (protocol == "file" || protocol == "ftp") { |
| 243 // For now, unfortunate as it is, file and ftp URLs are left as an |
| 244 // exercise for the reader. |
| 245 // When in doubt, return a network error. |
| 246 performNetworkError(); |
| 247 } else if (protocol == "http" || protocol == "https") { |
| 248 // Return the result of performing an HTTP fetch using request. |
| 249 performeHTTPFetch(false, false); |
| 250 } else { |
| 251 // Return a network error. |
| 252 performNetworkError(); |
| 253 } |
| 254 } |
| 255 |
| 256 void FetchManager::Loader::performNetworkError() |
| 257 { |
| 258 failed(); |
| 259 } |
| 260 |
| 261 void FetchManager::Loader::performeHTTPFetch(bool CORSFlag, bool CORSPreflightFl
ag) |
| 262 { |
| 263 ResourceRequest request(m_request->url()); |
| 264 request.setDownloadToFile(true); |
| 265 request.setHTTPMethod(m_request->method()); |
| 266 |
| 267 // Append `Referer`/empty byte sequence, if HTTPRequest's referrer is none, |
| 268 // and `Referer`/HTTPRequest's referrer, serialized and utf-8 encoded, |
| 269 // otherwise, to HTTPRequest's header list. |
| 270 // FIXME: Support Referer. |
| 271 const Vector<OwnPtr<FetchHeaderList::Header> >& list = m_request->headerList
()->list(); |
| 272 for (size_t i = 0; i < list.size(); ++i) { |
| 273 request.addHTTPHeaderField(AtomicString(list[i]->first), AtomicString(li
st[i]->second)); |
| 274 } |
| 275 |
| 276 |
| 277 ThreadableLoaderOptions threadableLoaderOptions; |
| 278 if (CORSPreflightFlag) |
| 279 threadableLoaderOptions.preflightPolicy = ForcePreflight; |
| 280 if (CORSFlag) |
| 281 threadableLoaderOptions.crossOriginRequestPolicy = UseAccessControl; |
| 282 else |
| 283 threadableLoaderOptions.crossOriginRequestPolicy = AllowCrossOriginReque
sts; |
| 284 |
| 285 ResourceLoaderOptions resourceLoaderOptions; |
| 286 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData; |
| 287 // Let credentials flag be set if either HTTPRequest's credentials mode is i
nclude, |
| 288 // or HTTPRequest's credentials mode is same-origin and the CORS flag is uns
et, |
| 289 // and unset otherwise. |
| 290 if (m_request->credentials() == FetchRequestData::IncludeCredentials |
| 291 || (m_request->credentials() == FetchRequestData::SameOriginCredentials
&& !CORSFlag)) { |
| 292 resourceLoaderOptions.allowCredentials = AllowStoredCredentials; |
| 293 } |
| 294 if (m_request->credentials() == FetchRequestData::IncludeCredentials) |
| 295 resourceLoaderOptions.credentialsRequested = ClientRequestedCredentials; |
| 296 |
| 297 m_loader = ThreadableLoader::create(*m_executionContext, this, request, thre
adableLoaderOptions, resourceLoaderOptions); |
| 298 } |
| 299 |
134 void FetchManager::Loader::failed() | 300 void FetchManager::Loader::failed() |
135 { | 301 { |
136 if (m_failed) | 302 if (m_failed) |
137 return; | 303 return; |
138 m_failed = true; | 304 m_failed = true; |
139 ScriptState* state = m_resolver->scriptState(); | 305 ScriptState* state = m_resolver->scriptState(); |
140 ScriptState::Scope scope(state); | 306 ScriptState::Scope scope(state); |
141 m_resolver->reject(V8ThrowException::createTypeError("Failed to fetch", stat
e->isolate())); | 307 m_resolver->reject(V8ThrowException::createTypeError("Failed to fetch", stat
e->isolate())); |
142 notifyFinished(); | 308 notifyFinished(); |
143 } | 309 } |
144 | 310 |
145 void FetchManager::Loader::notifyFinished() | 311 void FetchManager::Loader::notifyFinished() |
146 { | 312 { |
147 if (m_fetchManager) | 313 if (m_fetchManager) |
148 m_fetchManager->onLoaderFinished(this); | 314 m_fetchManager->onLoaderFinished(this); |
149 } | 315 } |
150 | 316 |
151 FetchManager::FetchManager(ExecutionContext* executionContext) | 317 FetchManager::FetchManager(ExecutionContext* executionContext) |
152 : m_executionContext(executionContext) | 318 : m_executionContext(executionContext) |
153 { | 319 { |
154 } | 320 } |
155 | 321 |
156 FetchManager::~FetchManager() | 322 FetchManager::~FetchManager() |
157 { | 323 { |
158 for (HashSet<OwnPtr<Loader> >::iterator it = m_loaders.begin(); it != m_load
ers.end(); ++it) { | 324 for (HashSet<OwnPtr<Loader> >::iterator it = m_loaders.begin(); it != m_load
ers.end(); ++it) { |
159 (*it)->cleanup(); | 325 (*it)->cleanup(); |
160 } | 326 } |
161 } | 327 } |
162 | 328 |
163 ScriptPromise FetchManager::fetch(ScriptState* scriptState, PassOwnPtr<ResourceR
equest> request) | 329 ScriptPromise FetchManager::fetch(ScriptState* scriptState, PassRefPtr<FetchRequ
estData> request) |
164 { | 330 { |
165 RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scrip
tState); | 331 RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scrip
tState); |
166 ScriptPromise promise = resolver->promise(); | 332 ScriptPromise promise = resolver->promise(); |
167 | 333 |
168 OwnPtr<Loader> loader(adoptPtr(new Loader(m_executionContext, this, resolver
.release(), request))); | 334 OwnPtr<Loader> ownLoader(adoptPtr(new Loader(m_executionContext, this, resol
ver.release(), request))); |
169 (*m_loaders.add(loader.release()).storedValue)->start(); | 335 Loader* loader = m_loaders.add(ownLoader.release()).storedValue->get(); |
| 336 loader->start(); |
170 return promise; | 337 return promise; |
171 } | 338 } |
172 | 339 |
| 340 bool FetchManager::isSimpleMethod(const String& method) |
| 341 { |
| 342 // A simple method is a method that is `GET`, `HEAD`, or `POST`. |
| 343 return isOnAccessControlSimpleRequestMethodWhitelist(method); |
| 344 } |
| 345 |
| 346 bool FetchManager::isForbiddenMethod(const String& method) |
| 347 { |
| 348 // A forbidden method is a method that is a byte case-insensitive match for
one of `CONNECT`, `TRACE`, and `TRACK`. |
| 349 return !XMLHttpRequest::isAllowedHTTPMethod(method); |
| 350 } |
| 351 |
| 352 bool FetchManager::isUsefulMethod(const String& method) |
| 353 { |
| 354 // A useful method is a method that is not a forbidden method. |
| 355 // A forbidden method is a method that is a byte case-insensitive match for
one of `CONNECT`, `TRACE`, and `TRACK`. |
| 356 return XMLHttpRequest::isAllowedHTTPMethod(method); |
| 357 } |
| 358 |
173 void FetchManager::onLoaderFinished(Loader* loader) | 359 void FetchManager::onLoaderFinished(Loader* loader) |
174 { | 360 { |
175 m_loaders.remove(loader); | 361 m_loaders.remove(loader); |
176 } | 362 } |
177 | 363 |
178 bool FetchManager::isSimpleMethod(const String& method) | 364 bool FetchManager::isSimpleMethod(const String& method) |
179 { | 365 { |
180 // "A simple method is a method that is `GET`, `HEAD`, or `POST`." | 366 // "A simple method is a method that is `GET`, `HEAD`, or `POST`." |
181 return isOnAccessControlSimpleRequestMethodWhitelist(method); | 367 return isOnAccessControlSimpleRequestMethodWhitelist(method); |
182 } | 368 } |
183 | 369 |
184 bool FetchManager::isForbiddenMethod(const String& method) | 370 bool FetchManager::isForbiddenMethod(const String& method) |
185 { | 371 { |
186 // "A forbidden method is a method that is a byte case-insensitive match for
one of `CONNECT`, `TRACE`, and `TRACK`." | 372 // "A forbidden method is a method that is a byte case-insensitive match for
one of `CONNECT`, `TRACE`, and `TRACK`." |
187 return !XMLHttpRequest::isAllowedHTTPMethod(method); | 373 return !XMLHttpRequest::isAllowedHTTPMethod(method); |
188 } | 374 } |
189 | 375 |
190 bool FetchManager::isUsefulMethod(const String& method) | 376 bool FetchManager::isUsefulMethod(const String& method) |
191 { | 377 { |
192 // "A useful method is a method that is not a forbidden method." | 378 // "A useful method is a method that is not a forbidden method." |
193 // "A forbidden method is a method that is a byte case-insensitive match for
one of `CONNECT`, `TRACE`, and `TRACK`." | 379 // "A forbidden method is a method that is a byte case-insensitive match for
one of `CONNECT`, `TRACE`, and `TRACK`." |
194 return XMLHttpRequest::isAllowedHTTPMethod(method); | 380 return XMLHttpRequest::isAllowedHTTPMethod(method); |
195 } | 381 } |
196 | 382 |
197 } // namespace WebCore | 383 } // namespace WebCore |
OLD | NEW |