OSDN Git Service

Merge "Fix (or partial fix) for 3355185, crash on broken-ideograph-small-caps.html...
[android-x86/external-webkit.git] / WebKit / android / WebCoreSupport / WebUrlLoaderClient.cpp
1 /*
2  * Copyright 2010, The Android Open Source Project
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  *  * Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  *  * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 #define LOG_TAG "WebUrlLoaderClient"
27
28 #include "config.h"
29 #include "WebUrlLoaderClient.h"
30
31 #include "ChromiumIncludes.h"
32 #include "OwnPtr.h"
33 #include "ResourceHandle.h"
34 #include "ResourceHandleClient.h"
35 #include "ResourceResponse.h"
36 #include "WebCoreFrameBridge.h"
37 #include "WebRequest.h"
38 #include "WebResourceRequest.h"
39
40 #include <wtf/text/CString.h>
41
42 namespace android {
43
44 base::Thread* WebUrlLoaderClient::ioThread()
45 {
46     static base::Thread* networkThread = 0;
47     static Lock networkThreadLock;
48
49     // Multiple threads appear to access the ioThread so we must ensure the
50     // critical section ordering.
51     AutoLock lock(networkThreadLock);
52
53     if (!networkThread)
54         networkThread = new base::Thread("network");
55
56     if (!networkThread)
57         return 0;
58
59     if (networkThread->IsRunning())
60         return networkThread;
61
62     base::Thread::Options options;
63     options.message_loop_type = MessageLoop::TYPE_IO;
64     if (!networkThread->StartWithOptions(options)) {
65         delete networkThread;
66         networkThread = 0;
67     }
68
69     return networkThread;
70 }
71
72 Lock* WebUrlLoaderClient::syncLock() {
73     static Lock s_syncLock;
74     return &s_syncLock;
75 }
76
77 ConditionVariable* WebUrlLoaderClient::syncCondition() {
78     static ConditionVariable s_syncCondition(syncLock());
79     return &s_syncCondition;
80 }
81
82 WebUrlLoaderClient::~WebUrlLoaderClient()
83 {
84 }
85
86 bool WebUrlLoaderClient::isActive() const
87 {
88     if (m_cancelling)
89         return false;
90     if (!m_resourceHandle->client())
91         return false;
92
93     return true;
94 }
95
96 WebUrlLoaderClient::WebUrlLoaderClient(WebFrame* webFrame, WebCore::ResourceHandle* resourceHandle, const WebCore::ResourceRequest& resourceRequest)
97     : m_webFrame(webFrame)
98     , m_resourceHandle(resourceHandle)
99     , m_cancelling(false)
100     , m_sync(false)
101     , m_finished(false)
102 {
103     WebResourceRequest webResourceRequest(resourceRequest);
104     UrlInterceptResponse* intercept = webFrame->shouldInterceptRequest(resourceRequest.url().string());
105     if (intercept) {
106         m_request = new WebRequest(this, webResourceRequest, intercept);
107         return;
108     }
109
110     m_request = new WebRequest(this, webResourceRequest);
111
112     // Set uploads before start is called on the request
113     if (resourceRequest.httpBody() && !(webResourceRequest.method() == "GET" || webResourceRequest.method() == "HEAD")) {
114         Vector<FormDataElement>::iterator iter;
115         Vector<FormDataElement> elements = resourceRequest.httpBody()->elements();
116         for (iter = elements.begin(); iter != elements.end(); iter++) {
117             FormDataElement element = *iter;
118
119             switch (element.m_type) {
120             case FormDataElement::data:
121                 if (!element.m_data.isEmpty()) {
122                     // WebKit sometimes gives up empty data to append. These aren't
123                     // necessary so we just optimize those out here.
124                     base::Thread* thread = ioThread();
125                     if (thread) {
126                         Vector<char>* data = new Vector<char>(element.m_data);
127                         thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::appendBytesToUpload, data));
128                     }
129                 }
130                 break;
131             case FormDataElement::encodedFile:
132                 {
133                     // Chromium check if it is a directory by checking
134                     // element.m_fileLength, that doesn't work in Android
135                     std::string filename = element.m_filename.utf8().data();
136                     if (filename.size()) {
137                         // Change from a url string to a filename
138                         if (filename.find("file://") == 0) // Found at pos 0
139                             filename.erase(0, 7);
140                         base::Thread* thread = ioThread();
141                         if (thread)
142                             thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::appendFileToUpload, filename));
143                     }
144                 }
145                 break;
146 #if ENABLE(BLOB)
147             case FormDataElement::encodedBlob:
148                 LOG_ASSERT(false, "Unexpected use of FormDataElement::encodedBlob");
149                 break;
150 #endif // ENABLE(BLOB)
151             default:
152                 LOG_ASSERT(false, "Unexpected default case in WebUrlLoaderClient.cpp");
153                 break;
154             }
155         }
156     }
157 }
158
159 bool WebUrlLoaderClient::start(bool sync, WebRequestContext* context)
160 {
161     base::Thread* thread = ioThread();
162     if (!thread) {
163         return false;
164     }
165
166     m_sync = sync;
167     if (m_sync) {
168         AutoLock autoLock(*syncLock());
169         m_request->setRequestContext(context);
170         thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::start));
171
172         // Run callbacks until the queue is exhausted and m_finished is true.
173         while(!m_finished) {
174             while (!m_queue.empty()) {
175                 OwnPtr<Task> task(m_queue.front());
176                 m_queue.pop_front();
177                 task->Run();
178             }
179             if (m_queue.empty() && !m_finished) {
180                 syncCondition()->Wait();
181             }
182         }
183
184         // This may be the last reference to us, so we may be deleted now.
185         // Don't access any more member variables after releasing this reference.
186         m_resourceHandle = 0;
187     } else {
188         // Asynchronous start.
189         // Important to set this before the thread starts so it has a reference and can't be deleted
190         // before the task starts running on the IO thread.
191         m_request->setRequestContext(context);
192         thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::start));
193     }
194     return true;
195 }
196
197 void WebUrlLoaderClient::downloadFile()
198 {
199     if (m_response) {
200         std::string contentDisposition;
201         m_response->getHeader("content-disposition", &contentDisposition);
202         m_webFrame->downloadStart(m_request->getUrl(), m_request->getUserAgent(), contentDisposition, m_response->getMimeType(), m_response->getExpectedSize());
203     } else {
204         LOGE("Unexpected call to downloadFile() before didReceiveResponse(). URL: %s", m_request->getUrl().c_str());
205         // TODO: Turn off asserts crashing before release
206         // http://b/issue?id=2951985
207         CRASH();
208     }
209 }
210
211 void WebUrlLoaderClient::cancel()
212 {
213     m_cancelling = true;
214
215     base::Thread* thread = ioThread();
216     if (thread)
217         thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancel));
218 }
219
220 void WebUrlLoaderClient::setAuth(const std::string& username, const std::string& password)
221 {
222     base::Thread* thread = ioThread();
223     if (!thread) {
224         return;
225     }
226     string16 username16 = ASCIIToUTF16(username);
227     string16 password16 = ASCIIToUTF16(password);
228     thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::setAuth, username16, password16));
229 }
230
231 void WebUrlLoaderClient::cancelAuth()
232 {
233     base::Thread* thread = ioThread();
234     if (!thread) {
235         return;
236     }
237     thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancelAuth));
238 }
239
240 void WebUrlLoaderClient::proceedSslCertError()
241 {
242     base::Thread* thread = ioThread();
243     if (!thread) {
244         return;
245     }
246     thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::proceedSslCertError));
247 }
248
249 void WebUrlLoaderClient::cancelSslCertError(int cert_error)
250 {
251     base::Thread* thread = ioThread();
252     if (!thread) {
253         return;
254     }
255     thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancelSslCertError, cert_error));
256 }
257
258
259 void WebUrlLoaderClient::finish()
260 {
261     m_finished = true;
262     if (!m_sync) {
263         // This is the last reference to us, so we will be deleted now.
264         // We only release the reference here if start() was called asynchronously!
265         m_resourceHandle = 0;
266     }
267     m_request = 0;
268 }
269
270 namespace {
271 // Trampoline to wrap a Chromium Task* in a WebKit-style static function + void*.
272 static void RunTask(void* v) {
273     OwnPtr<Task> task(static_cast<Task*>(v));
274     task->Run();
275 }
276 }
277
278 // This is called from the IO thread, and dispatches the callback to the main thread.
279 void WebUrlLoaderClient::maybeCallOnMainThread(Task* task)
280 {
281     if (m_sync) {
282         AutoLock autoLock(*syncLock());
283         if (m_queue.empty()) {
284             syncCondition()->Broadcast();
285         }
286         m_queue.push_back(task);
287     } else {
288         // Let WebKit handle it.
289         callOnMainThread(RunTask, task);
290     }
291 }
292
293 // Response methods
294 void WebUrlLoaderClient::didReceiveResponse(PassOwnPtr<WebResponse> webResponse)
295 {
296     if (!isActive())
297         return;
298
299     m_response = webResponse;
300     m_resourceHandle->client()->didReceiveResponse(m_resourceHandle.get(), m_response->createResourceResponse());
301 }
302
303 void WebUrlLoaderClient::didReceiveData(scoped_refptr<net::IOBuffer> buf, int size)
304 {
305     if (!isActive() || !size)
306         return;
307
308     // didReceiveData will take a copy of the data
309     if (m_resourceHandle && m_resourceHandle->client())
310         m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), buf->data(), size, size);
311 }
312
313 // For data url's
314 void WebUrlLoaderClient::didReceiveDataUrl(PassOwnPtr<std::string> str)
315 {
316     if (!isActive() || !str->size())
317         return;
318
319     // didReceiveData will take a copy of the data
320     m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), str->data(), str->size(), str->size());
321 }
322
323 // For special android files
324 void WebUrlLoaderClient::didReceiveAndroidFileData(PassOwnPtr<std::vector<char> > vector)
325 {
326     if (!isActive() || !vector->size())
327         return;
328
329     // didReceiveData will take a copy of the data
330     m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), vector->begin(), vector->size(), vector->size());
331 }
332
333 void WebUrlLoaderClient::didFail(PassOwnPtr<WebResponse> webResponse)
334 {
335     if (isActive())
336         m_resourceHandle->client()->didFail(m_resourceHandle.get(), webResponse->createResourceError());
337
338     // Always finish a request, if not it will leak
339     finish();
340 }
341
342 void WebUrlLoaderClient::willSendRequest(PassOwnPtr<WebResponse> webResponse)
343 {
344     if (!isActive())
345         return;
346
347     KURL url = webResponse->createKurl();
348     OwnPtr<WebCore::ResourceRequest> resourceRequest(new WebCore::ResourceRequest(url));
349     m_resourceHandle->client()->willSendRequest(m_resourceHandle.get(), *resourceRequest, webResponse->createResourceResponse());
350
351     // WebKit may have killed the request.
352     if (!isActive())
353         return;
354
355     // Like Chrome, we only follow the redirect if WebKit left the URL unmodified.
356     if (url == resourceRequest->url()) {
357         ioThread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::followDeferredRedirect));
358     } else {
359         cancel();
360     }
361 }
362
363 void WebUrlLoaderClient::didFinishLoading()
364 {
365     if (isActive())
366         m_resourceHandle->client()->didFinishLoading(m_resourceHandle.get(), 0);
367
368     // Always finish a request, if not it will leak
369     finish();
370 }
371
372 void WebUrlLoaderClient::authRequired(scoped_refptr<net::AuthChallengeInfo> authChallengeInfo, bool firstTime)
373 {
374     if (!isActive()) {
375         return;
376     }
377
378     std::string host = base::SysWideToUTF8(authChallengeInfo->host_and_port);
379     std::string realm = base::SysWideToUTF8(authChallengeInfo->realm);
380
381     m_webFrame->didReceiveAuthenticationChallenge(this, host, realm, firstTime);
382 }
383
384 void WebUrlLoaderClient::reportSslCertError(int cert_error, net::X509Certificate* cert)
385 {
386     if (!isActive()) return;
387     std::vector<std::string> chain_bytes;
388     cert->GetChainDEREncodedBytes(&chain_bytes);
389     m_webFrame->reportSslCertError(this, cert_error, chain_bytes[0]);
390 }
391
392 } // namespace android