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 <utils/Log.h>
41#include <wtf/text/CString.h>
42
43using base::Lock;
44using base::AutoLock;
45
46namespace android {
47
48base::Thread* WebUrlLoaderClient::ioThread()
49{
50    static base::Thread* networkThread = 0;
51    static Lock networkThreadLock;
52
53    // Multiple threads appear to access the ioThread so we must ensure the
54    // critical section ordering.
55    AutoLock lock(networkThreadLock);
56
57    if (!networkThread)
58        networkThread = new base::Thread("network");
59
60    if (!networkThread)
61        return 0;
62
63    if (networkThread->IsRunning())
64        return networkThread;
65
66    base::Thread::Options options;
67    options.message_loop_type = MessageLoop::TYPE_IO;
68    if (!networkThread->StartWithOptions(options)) {
69        delete networkThread;
70        networkThread = 0;
71    }
72
73    return networkThread;
74}
75
76base::Lock* WebUrlLoaderClient::syncLock() {
77    static Lock s_syncLock;
78    return &s_syncLock;
79}
80
81base::ConditionVariable* WebUrlLoaderClient::syncCondition() {
82    static base::ConditionVariable s_syncCondition(syncLock());
83    return &s_syncCondition;
84}
85
86WebUrlLoaderClient::~WebUrlLoaderClient()
87{
88}
89
90bool WebUrlLoaderClient::isActive() const
91{
92    if (m_cancelling)
93        return false;
94    if (!m_resourceHandle)
95        return false;
96    if (!m_resourceHandle->client())
97        return false;
98    if (m_finished)
99        return false;
100
101    return true;
102}
103
104WebUrlLoaderClient::WebUrlLoaderClient(WebFrame* webFrame, WebCore::ResourceHandle* resourceHandle, const WebCore::ResourceRequest& resourceRequest)
105    : m_webFrame(webFrame)
106    , m_resourceHandle(resourceHandle)
107    , m_isMainResource(false)
108    , m_isMainFrame(false)
109    , m_isCertMimeType(false)
110    , m_cancelling(false)
111    , m_sync(false)
112    , m_finished(false)
113{
114    bool block = webFrame->blockNetworkLoads() && (resourceRequest.url().protocolIs("http") || resourceRequest.url().protocolIs("https"));
115    WebResourceRequest webResourceRequest(resourceRequest, block);
116    UrlInterceptResponse* intercept = webFrame->shouldInterceptRequest(resourceRequest.url().string());
117    if (intercept) {
118        m_request = new WebRequest(this, webResourceRequest, intercept);
119        return;
120    }
121
122    m_request = new WebRequest(this, webResourceRequest);
123
124    // Set uploads before start is called on the request
125    if (resourceRequest.httpBody() && !(webResourceRequest.method() == "GET" || webResourceRequest.method() == "HEAD")) {
126        Vector<FormDataElement>::iterator iter;
127        Vector<FormDataElement> elements = resourceRequest.httpBody()->elements();
128        for (iter = elements.begin(); iter != elements.end(); iter++) {
129            FormDataElement element = *iter;
130
131            switch (element.m_type) {
132            case FormDataElement::data:
133                if (!element.m_data.isEmpty()) {
134                    // WebKit sometimes gives up empty data to append. These aren't
135                    // necessary so we just optimize those out here.
136                    base::Thread* thread = ioThread();
137                    if (thread) {
138                        Vector<char>* data = new Vector<char>(element.m_data);
139                        thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::appendBytesToUpload, data));
140                    }
141                }
142                break;
143            case FormDataElement::encodedFile:
144                {
145                    // Chromium check if it is a directory by checking
146                    // element.m_fileLength, that doesn't work in Android
147                    std::string filename = element.m_filename.utf8().data();
148                    if (filename.size()) {
149                        // Change from a url string to a filename
150                        if (filename.find("file://") == 0) // Found at pos 0
151                            filename.erase(0, 7);
152                        base::Thread* thread = ioThread();
153                        if (thread)
154                            thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::appendFileToUpload, filename));
155                    }
156                }
157                break;
158#if ENABLE(BLOB)
159            case FormDataElement::encodedBlob:
160                ALOG_ASSERT(false, "Unexpected use of FormDataElement::encodedBlob");
161                break;
162#endif // ENABLE(BLOB)
163            default:
164                ALOG_ASSERT(false, "Unexpected default case in WebUrlLoaderClient.cpp");
165                break;
166            }
167        }
168    }
169}
170
171bool WebUrlLoaderClient::start(bool isMainResource, bool isMainFrame, bool sync, WebRequestContext* context)
172{
173    base::Thread* thread = ioThread();
174    if (!thread) {
175        return false;
176    }
177
178    m_isMainResource = isMainResource;
179    m_isMainFrame = isMainFrame;
180    m_sync = sync;
181    if (m_sync) {
182        AutoLock autoLock(*syncLock());
183        m_request->setSync(sync);
184        m_request->setRequestContext(context);
185        thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::start));
186
187        // Run callbacks until the queue is exhausted and m_finished is true.
188        // Sometimes, a sync load can wait forever and lock up the WebCore thread,
189        // here we use TimedWait() with multiple tries to avoid locking.
190        const int kMaxNumTimeout = 3;
191        const int kCallbackWaitingTime = 10;
192        int num_timeout = 0;
193        while(!m_finished) {
194            while (!m_queue.empty()) {
195                OwnPtr<Task> task(m_queue.front());
196                m_queue.pop_front();
197                task->Run();
198            }
199            if (m_finished) break;
200
201            syncCondition()->TimedWait(base::TimeDelta::FromSeconds(kCallbackWaitingTime));
202            if (m_queue.empty()) {
203                ALOGE("Synchronous request timed out after %d seconds for the %dth try, URL: %s",
204                     kCallbackWaitingTime, num_timeout, m_request->getUrl().c_str());
205                num_timeout++;
206                if (num_timeout >= kMaxNumTimeout) {
207                    cancel();
208                    m_resourceHandle = 0;
209                    return false;
210                }
211            }
212        }
213
214        // This may be the last reference to us, so we may be deleted now.
215        // Don't access any more member variables after releasing this reference.
216        m_resourceHandle = 0;
217    } else {
218        // Asynchronous start.
219        // Important to set this before the thread starts so it has a reference and can't be deleted
220        // before the task starts running on the IO thread.
221        m_request->setRequestContext(context);
222        thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::start));
223    }
224    return true;
225}
226
227namespace {
228// Check if the mime type is for certificate installation.
229// The items must be consistent with the sCertificateTypeMap
230// in frameworks/base/core/java/android/webkit/CertTool.java.
231bool isMimeTypeForCert(const std::string& mimeType)
232{
233    static std::hash_set<std::string> sCertificateTypeSet;
234    if (sCertificateTypeSet.empty()) {
235        sCertificateTypeSet.insert("application/x-x509-ca-cert");
236        sCertificateTypeSet.insert("application/x-x509-user-cert");
237        sCertificateTypeSet.insert("application/x-pkcs12");
238    }
239    return sCertificateTypeSet.find(mimeType) != sCertificateTypeSet.end();
240}
241}
242
243void WebUrlLoaderClient::downloadFile()
244{
245    if (m_response) {
246        std::string contentDisposition;
247        m_response->getHeader("content-disposition", &contentDisposition);
248        m_webFrame->downloadStart(m_response->getUrl(), m_request->getUserAgent(), contentDisposition, m_response->getMimeType(), m_request->getReferer(), m_response->getExpectedSize());
249
250        m_isCertMimeType = isMimeTypeForCert(m_response->getMimeType());
251        // Currently, only certificate mime type needs to receive the data.
252        // Other mime type, e.g. wav, will send the url to other application
253        // which will load the data by url.
254        if (!m_isCertMimeType)
255            cancel();
256    } else {
257        ALOGE("Unexpected call to downloadFile() before didReceiveResponse(). URL: %s", m_request->getUrl().c_str());
258        // TODO: Turn off asserts crashing before release
259        // http://b/issue?id=2951985
260        CRASH();
261    }
262}
263
264void WebUrlLoaderClient::cancel()
265{
266    if (!isActive())
267        return;
268
269    m_cancelling = true;
270
271    base::Thread* thread = ioThread();
272    if (thread)
273        thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancel));
274}
275
276void WebUrlLoaderClient::pauseLoad(bool pause)
277{
278    if (!isActive())
279        return;
280
281    base::Thread* thread = ioThread();
282    if (thread)
283        thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::pauseLoad, pause));
284}
285
286void WebUrlLoaderClient::setAuth(const std::string& username, const std::string& password)
287{
288    if (!isActive())
289        return;
290
291    base::Thread* thread = ioThread();
292    if (!thread) {
293        return;
294    }
295    string16 username16 = ASCIIToUTF16(username);
296    string16 password16 = ASCIIToUTF16(password);
297    thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::setAuth, username16, password16));
298}
299
300void WebUrlLoaderClient::cancelAuth()
301{
302    if (!isActive())
303        return;
304
305    base::Thread* thread = ioThread();
306    if (!thread) {
307        return;
308    }
309    thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancelAuth));
310}
311
312void WebUrlLoaderClient::proceedSslCertError()
313{
314    base::Thread* thread = ioThread();
315    if (isActive() && thread)
316        thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::proceedSslCertError));
317    this->Release();
318}
319
320void WebUrlLoaderClient::cancelSslCertError(int cert_error)
321{
322    base::Thread* thread = ioThread();
323    if (isActive() && thread)
324        thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::cancelSslCertError, cert_error));
325    this->Release();
326}
327
328void WebUrlLoaderClient::finish()
329{
330    m_finished = true;
331    if (!m_sync) {
332        // This is the last reference to us, so we will be deleted now.
333        // We only release the reference here if start() was called asynchronously!
334        m_resourceHandle = 0;
335    }
336    m_request = 0;
337}
338
339namespace {
340// Trampoline to wrap a Chromium Task* in a WebKit-style static function + void*.
341static void RunTask(void* v) {
342    OwnPtr<Task> task(static_cast<Task*>(v));
343    task->Run();
344}
345}
346
347// This is called from the IO thread, and dispatches the callback to the main thread.
348void WebUrlLoaderClient::maybeCallOnMainThread(Task* task)
349{
350    if (m_sync) {
351        AutoLock autoLock(*syncLock());
352        if (m_queue.empty()) {
353            syncCondition()->Broadcast();
354        }
355        m_queue.push_back(task);
356    } else {
357        // Let WebKit handle it.
358        callOnMainThread(RunTask, task);
359    }
360}
361
362// Response methods
363void WebUrlLoaderClient::didReceiveResponse(PassOwnPtr<WebResponse> webResponse)
364{
365    if (!isActive())
366        return;
367
368    m_response = webResponse;
369    m_resourceHandle->client()->didReceiveResponse(m_resourceHandle.get(), m_response->createResourceResponse());
370
371    // Set the main page's certificate to WebView.
372    if (m_isMainResource && m_isMainFrame) {
373        const net::SSLInfo& ssl_info = m_response->getSslInfo();
374        if (ssl_info.is_valid()) {
375            std::vector<std::string> chain_bytes;
376            ssl_info.cert->GetChainDEREncodedBytes(&chain_bytes);
377            m_webFrame->setCertificate(chain_bytes[0]);
378        }
379
380        // Look for X-Auto-Login on the main resource to log in the user.
381        std::string login;
382        if (m_response->getHeader("x-auto-login", &login))
383            m_webFrame->autoLogin(login);
384    }
385}
386
387void WebUrlLoaderClient::didReceiveData(scoped_refptr<net::IOBuffer> buf, int size)
388{
389    if (m_isMainResource && m_isCertMimeType) {
390        m_webFrame->didReceiveData(buf->data(), size);
391    }
392
393    if (!isActive() || !size)
394        return;
395
396    // didReceiveData will take a copy of the data
397    if (m_resourceHandle && m_resourceHandle->client())
398        m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), buf->data(), size, size);
399}
400
401// For data url's
402void WebUrlLoaderClient::didReceiveDataUrl(PassOwnPtr<std::string> str)
403{
404    if (!isActive() || !str->size())
405        return;
406
407    // didReceiveData will take a copy of the data
408    m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), str->data(), str->size(), str->size());
409}
410
411// For special android files
412void WebUrlLoaderClient::didReceiveAndroidFileData(PassOwnPtr<std::vector<char> > vector)
413{
414    if (!isActive() || !vector->size())
415        return;
416
417    // didReceiveData will take a copy of the data
418    m_resourceHandle->client()->didReceiveData(m_resourceHandle.get(), vector->begin(), vector->size(), vector->size());
419}
420
421void WebUrlLoaderClient::didFail(PassOwnPtr<WebResponse> webResponse)
422{
423    if (isActive())
424        m_resourceHandle->client()->didFail(m_resourceHandle.get(), webResponse->createResourceError());
425
426    // Always finish a request, if not it will leak
427    finish();
428}
429
430void WebUrlLoaderClient::willSendRequest(PassOwnPtr<WebResponse> webResponse)
431{
432    if (!isActive())
433        return;
434
435    KURL url = webResponse->createKurl();
436    OwnPtr<WebCore::ResourceRequest> resourceRequest(new WebCore::ResourceRequest(url));
437    m_resourceHandle->client()->willSendRequest(m_resourceHandle.get(), *resourceRequest, webResponse->createResourceResponse());
438
439    // WebKit may have killed the request.
440    if (!isActive())
441        return;
442
443    // Like Chrome, we only follow the redirect if WebKit left the URL unmodified.
444    if (url == resourceRequest->url()) {
445        ioThread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::followDeferredRedirect));
446    } else {
447        cancel();
448    }
449}
450
451void WebUrlLoaderClient::didFinishLoading()
452{
453    if (isActive())
454        m_resourceHandle->client()->didFinishLoading(m_resourceHandle.get(), 0);
455
456    if (m_isMainResource && m_isCertMimeType) {
457        m_webFrame->didFinishLoading();
458    }
459
460    // Always finish a request, if not it will leak
461    finish();
462}
463
464void WebUrlLoaderClient::authRequired(scoped_refptr<net::AuthChallengeInfo> authChallengeInfo, bool firstTime, bool suppressDialog)
465{
466    if (!isActive())
467        return;
468
469    std::string host = base::SysWideToUTF8(authChallengeInfo->host_and_port);
470    std::string realm = base::SysWideToUTF8(authChallengeInfo->realm);
471
472    m_webFrame->didReceiveAuthenticationChallenge(this, host, realm, firstTime, suppressDialog);
473}
474
475void WebUrlLoaderClient::reportSslCertError(int cert_error, net::X509Certificate* cert)
476{
477    if (!isActive())
478        return;
479
480    std::vector<std::string> chain_bytes;
481    cert->GetChainDEREncodedBytes(&chain_bytes);
482    this->AddRef();
483    m_webFrame->reportSslCertError(this, cert_error, chain_bytes[0], m_request->getUrl());
484}
485
486void WebUrlLoaderClient::sslClientCert(EVP_PKEY* pkey, net::X509Certificate* chain)
487{
488    base::Thread* thread = ioThread();
489    scoped_refptr<net::X509Certificate> scopedChain(chain);
490    if (isActive() && thread)
491        thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(m_request.get(), &WebRequest::sslClientCert, pkey, scopedChain));
492    this->Release();
493}
494
495void WebUrlLoaderClient::requestClientCert(net::SSLCertRequestInfo* cert_request_info)
496{
497    if (!isActive())
498        return;
499
500    std::string host_and_port = cert_request_info->host_and_port;
501    this->AddRef();
502    m_webFrame->requestClientCert(this, host_and_port);
503}
504
505} // namespace android
506