1/*
2 * Copyright 2007, 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 "WebCore"
27
28#include "config.h"
29#include "FrameLoaderClientAndroid.h"
30
31#include "BackForwardList.h"
32#include "CachedFrame.h"
33#include "CachedFramePlatformDataAndroid.h"
34#include "Chrome.h"
35#include "ChromeClientAndroid.h"
36#include "DOMImplementation.h"
37#include "Document.h"
38#include "DocumentLoader.h"
39#include "EditorClientAndroid.h"
40#include "Frame.h"
41#include "FrameLoader.h"
42#include "FrameNetworkingContextAndroid.h"
43#include "FrameTree.h"
44#include "FrameView.h"
45#include "GraphicsContext.h"
46#include "HTMLFrameOwnerElement.h"
47#include "HTMLPlugInElement.h"
48#include "HistoryItem.h"
49#include "IconDatabase.h"
50#include "MIMETypeRegistry.h"
51#include "NotImplemented.h"
52#include "PackageNotifier.h"
53#include "Page.h"
54#include "PlatformBridge.h"
55#include "PlatformGraphicsContext.h"
56#include "PlatformString.h"
57#include "PluginDatabase.h"
58#include "PluginView.h"
59#include "PluginViewBase.h"
60#include "ProgressTracker.h"
61#include "RenderPart.h"
62#include "RenderView.h"
63#include "RenderWidget.h"
64#include "ResourceError.h"
65#include "ResourceHandle.h"
66#include "ResourceHandleInternal.h"
67#include "SelectionController.h"
68#include "Settings.h"
69#include "SkCanvas.h"
70#include "SkRect.h"
71#include "TextEncoding.h"
72#include "WebCoreFrameBridge.h"
73#include "WebHistory.h"
74#include "WebIconDatabase.h"
75#include "WebFrameView.h"
76#include "WebViewClientError.h"
77#include "WebViewCore.h"
78#include "autofill/WebAutofill.h"
79#include "android_graphics.h"
80
81#include <androidfw/AssetManager.h>
82#include <wtf/text/CString.h>
83
84#define verifiedOk() // Verified that we don't need to implement this.
85
86extern android::AssetManager* globalAssetManager();
87
88namespace android {
89
90static const int EXTRA_LAYOUT_DELAY = 1000;
91
92FrameLoaderClientAndroid::FrameLoaderClientAndroid(WebFrame* webframe)
93    : m_frame(NULL)
94    , m_webFrame(webframe)
95    , m_manualLoader(NULL)
96    , m_hasSentResponseToPlugin(false)
97    , m_onDemandPluginsEnabled(false)
98    , m_didReceiveServerRedirect(false) {
99    Retain(m_webFrame);
100}
101
102FrameLoaderClientAndroid* FrameLoaderClientAndroid::get(const WebCore::Frame* frame)
103{
104    return static_cast<FrameLoaderClientAndroid*> (frame->loader()->client());
105}
106
107void FrameLoaderClientAndroid::frameLoaderDestroyed() {
108    registerForIconNotification(false);
109    m_frame = 0;
110    Release(m_webFrame);
111    delete this;
112}
113
114bool FrameLoaderClientAndroid::hasWebView() const {
115    // FIXME,
116    // there is one web view per page, or top frame.
117    // as android's view is created from Java side, it is always there.
118    return true;
119}
120
121void FrameLoaderClientAndroid::makeRepresentation(DocumentLoader*) {
122    m_onDemandPluginsEnabled = false;
123    // don't use representation
124    verifiedOk();
125}
126
127void FrameLoaderClientAndroid::forceLayout() {
128    ASSERT(m_frame);
129    m_frame->view()->forceLayout();
130    // FIXME, should we adjust view size here?
131    m_frame->view()->adjustViewSize();
132}
133
134void FrameLoaderClientAndroid::forceLayoutForNonHTML() {
135    notImplemented();
136}
137
138void FrameLoaderClientAndroid::setCopiesOnScroll() {
139    // this is a hint about whether we need to force redraws, or can
140    // just copy the scrolled content. Since we always force a redraw
141    // anyways, we can ignore this call.
142    verifiedOk();
143}
144
145void FrameLoaderClientAndroid::detachedFromParent2() {
146    // FIXME, ready to detach frame from view
147}
148
149void FrameLoaderClientAndroid::detachedFromParent3() {
150    // FIXME, ready to release view
151    notImplemented();
152}
153
154// This function is responsible for associating the "id" with a given
155// subresource load.  The following functions that accept an "id" are
156// called for each subresource, so they should not be dispatched to the m_frame.
157void FrameLoaderClientAndroid::assignIdentifierToInitialRequest(unsigned long id,
158                            DocumentLoader*, const ResourceRequest&) {
159    notImplemented();
160}
161
162void FrameLoaderClientAndroid::dispatchWillSendRequest(DocumentLoader*, unsigned long id,
163                            ResourceRequest&, const ResourceResponse&) {
164    notImplemented();
165}
166
167bool FrameLoaderClientAndroid::shouldUseCredentialStorage(DocumentLoader*, unsigned long  identifier)
168{
169    notImplemented();
170    return false;
171}
172
173void FrameLoaderClientAndroid::dispatchDidReceiveAuthenticationChallenge(DocumentLoader*,
174                            unsigned long id, const AuthenticationChallenge&) {
175    notImplemented();
176}
177
178void FrameLoaderClientAndroid::dispatchDidCancelAuthenticationChallenge(DocumentLoader*,
179                            unsigned long id, const AuthenticationChallenge&) {
180    notImplemented();
181}
182
183void FrameLoaderClientAndroid::dispatchDidReceiveResponse(DocumentLoader*,
184                            unsigned long id, const ResourceResponse&) {
185    notImplemented();
186}
187
188void FrameLoaderClientAndroid::dispatchDidReceiveContentLength(DocumentLoader*,
189                            unsigned long id, int lengthReceived) {
190    notImplemented();
191}
192
193void FrameLoaderClientAndroid::dispatchDidFinishLoading(DocumentLoader*,
194                            unsigned long id) {
195    notImplemented();
196}
197
198void FrameLoaderClientAndroid::dispatchDidFailLoading(DocumentLoader* docLoader,
199                            unsigned long id, const ResourceError&) {
200    notImplemented();
201}
202
203bool FrameLoaderClientAndroid::dispatchDidLoadResourceFromMemoryCache(DocumentLoader*,
204                            const ResourceRequest&, const ResourceResponse&, int length) {
205    notImplemented();
206    return false;
207}
208
209void FrameLoaderClientAndroid::dispatchDidHandleOnloadEvents() {
210}
211
212void FrameLoaderClientAndroid::dispatchDidReceiveServerRedirectForProvisionalLoad() {
213    ASSERT(!m_didReceiveServerRedirect);
214    m_didReceiveServerRedirect = true;
215}
216
217void FrameLoaderClientAndroid::dispatchDidCancelClientRedirect() {
218    notImplemented();
219}
220
221void FrameLoaderClientAndroid::dispatchWillPerformClientRedirect(const KURL&,
222                                double interval, double fireDate) {
223    notImplemented();
224}
225
226void FrameLoaderClientAndroid::dispatchDidChangeLocationWithinPage() {
227    notImplemented();
228}
229
230void FrameLoaderClientAndroid::dispatchDidPushStateWithinPage()
231{
232    notImplemented();
233}
234
235void FrameLoaderClientAndroid::dispatchDidReplaceStateWithinPage()
236{
237    notImplemented();
238}
239
240void FrameLoaderClientAndroid::dispatchDidPopStateWithinPage()
241{
242    notImplemented();
243}
244
245void FrameLoaderClientAndroid::dispatchWillClose() {
246    notImplemented();
247}
248
249void FrameLoaderClientAndroid::dispatchDidReceiveIcon() {
250    ASSERT(m_frame);
251    if (m_frame->tree() && m_frame->tree()->parent())
252        return;
253    WTF::String url(m_frame->document()->url().string());
254    // Try to obtain the icon image.
255    // FIXME: This method should not be used from outside WebCore and will be removed.
256    // http://trac.webkit.org/changeset/81484
257    WebCore::Image* icon = WebCore::iconDatabase().synchronousIconForPageURL(url, WebCore::IntSize(16, 16));
258    // If the request fails, try the original request url.
259    if (!icon) {
260        DocumentLoader* docLoader = m_frame->loader()->activeDocumentLoader();
261        KURL originalURL = docLoader->originalRequest().url();
262        // FIXME: This method should not be used from outside WebCore and will be removed.
263        // http://trac.webkit.org/changeset/81484
264        icon = WebCore::iconDatabase().synchronousIconForPageURL(originalURL, WebCore::IntSize(16, 16));
265    }
266    // There is a bug in webkit where cancelling an icon load is treated as a
267    // failure. When this is fixed, we can ASSERT again that we have an icon.
268    if (icon) {
269        ALOGV("Received icon (%p) for %s", icon,
270                url.utf8().data());
271        m_webFrame->didReceiveIcon(icon);
272    } else {
273        ALOGV("Icon data for %s unavailable, registering for notification...",
274                url.utf8().data());
275        registerForIconNotification();
276    }
277}
278
279void FrameLoaderClientAndroid::dispatchDidReceiveTouchIconURL(const String& url, bool precomposed) {
280    ASSERT(m_frame);
281    // Do not report sub frame touch icons
282    if (m_frame->tree() && m_frame->tree()->parent())
283        return;
284    m_webFrame->didReceiveTouchIconURL(url, precomposed);
285}
286
287void FrameLoaderClientAndroid::dispatchDidStartProvisionalLoad() {
288    notImplemented();
289}
290
291void FrameLoaderClientAndroid::dispatchDidReceiveTitle(const StringWithDirection& title) {
292    ASSERT(m_frame);
293    // Used to check for FrameLoadTypeStandard but we only want to send the title for
294    // the top frame and not sub-frames.
295    // FIXME: Use direction of title.
296    if (!m_frame->tree() || !m_frame->tree()->parent()) {
297        m_webFrame->setTitle(title.string());
298    }
299}
300
301void FrameLoaderClientAndroid::dispatchDidCommitLoad() {
302#if ENABLE(WEB_AUTOFILL)
303    if (m_frame == m_frame->page()->mainFrame()) {
304        EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(m_frame->page()->editorClient());
305        WebAutofill* autoFill = editorC->getAutofill();
306        autoFill->reset();
307    }
308#endif
309    verifiedOk();
310}
311
312static void loadDataIntoFrame(Frame* frame, KURL baseUrl, const String& url,
313        const String& data) {
314    if (baseUrl.isEmpty()) {
315        baseUrl = blankURL();
316    }
317    ResourceRequest request(baseUrl);
318    CString cstr = data.utf8();
319    RefPtr<WebCore::SharedBuffer> buf = WebCore::SharedBuffer::create(cstr.data(), cstr.length());
320    SubstituteData subData(buf, String("text/html"), String("utf-8"),
321            KURL(KURL(), url));
322    frame->loader()->load(request, subData, false);
323}
324
325void FrameLoaderClientAndroid::dispatchDidFailProvisionalLoad(const ResourceError& error) {
326    ASSERT(m_frame);
327    // Ignore ErrorInterrupted since it is due to a policy interruption. This
328    // is caused by a decision to download the main resource rather than
329    // display it.
330    if (error.errorCode() == InternalErrorInterrupted
331            || error.errorCode() == InternalErrorCancelled) {
332        // If we decided to download the main resource or if the user cancelled
333        // it, make sure we report that the load is done.
334        didFinishLoad();
335        return;
336    }
337
338    AssetManager* am = globalAssetManager();
339
340    // Check to see if the error code was not generated internally
341    WebCore::PlatformBridge::rawResId id = WebCore::PlatformBridge::NoDomain;
342    if ((error.errorCode() == ErrorFile ||
343            error.errorCode() == ErrorFileNotFound) &&
344            (!error.localizedDescription().isEmpty())) {
345        id = WebCore::PlatformBridge::LoadError;
346    }
347    String filename = m_webFrame->getRawResourceFilename(id);
348    if (filename.isEmpty())
349        return;
350
351    // Grab the error page from the asset manager
352    Asset* a = am->openNonAsset(
353            filename.utf8().data(), Asset::ACCESS_BUFFER);
354    if (!a)
355        return;
356
357    // Take the failing url and encode html entities so javascript urls are not
358    // executed.
359    CString failingUrl = error.failingURL().utf8();
360    WTF::Vector<char> url;
361    int len = failingUrl.length();
362    const char* data = failingUrl.data();
363    for (int i = 0; i < len; i++) {
364        char c = data[i];
365        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
366                || (c >= '0' && c <= '9'))
367            url.append(c);
368        else {
369            char buf[16];
370            int res = sprintf(buf, "&#%d;", c);
371            buf[res] = 0;
372            url.append(buf, res);
373        }
374    }
375    // Vector sets up its data buffer lazilly, so if failingUrl is the empty
376    // string, the data buffer will be null. This will result in sanitizedUrl
377    // being null, and the string substitution below will be a no-op.
378    // FIXME: Ideally we'd always have a non-empty URL, or at least improve the
379    // wording of the error page in this case. See http://b/5293782.
380    String sanitizedUrl = url.data() ? String(url.data(), url.size()) : "";
381
382    // Replace all occurances of %s with the failing url.
383    String s = UTF8Encoding().decode((const char*)a->getBuffer(false), a->getLength());
384    s = s.replace("%s", sanitizedUrl);
385
386    // Replace all occurances of %e with the error text
387    s = s.replace("%e", error.localizedDescription());
388
389    // Create the request and the substitute data and tell the FrameLoader to
390    // load with the replacement data.
391    // use KURL(const char*) as KURL(const String& url) can trigger ASSERT for
392    // invalidate URL string.
393    loadDataIntoFrame(m_frame, KURL(ParsedURLString, data), error.failingURL(), s);
394
395    // Delete the asset.
396    delete a;
397
398    // Report that the load is finished, since it failed.
399    didFinishLoad();
400}
401
402void FrameLoaderClientAndroid::dispatchDidFailLoad(const ResourceError&) {
403    // called when page is completed with error
404    didFinishLoad();
405}
406
407void FrameLoaderClientAndroid::dispatchDidFinishDocumentLoad() {
408    // called when finishedParsing
409    notImplemented();
410}
411
412void FrameLoaderClientAndroid::dispatchDidFinishLoad() {
413    didFinishLoad();
414}
415
416void FrameLoaderClientAndroid::dispatchDidFirstLayout() {
417    ASSERT(m_frame);
418    // set EXTRA_LAYOUT_DELAY if the loader is not completed yet
419    if (!m_frame->loader()->isComplete())
420        m_frame->document()->setExtraLayoutDelay(EXTRA_LAYOUT_DELAY);
421    // we need to do this here instead of dispatchDidFirstVisuallyNonEmptyLayout
422    // so that about:blank will update the screen.
423    if (!m_frame->tree()->parent()) {
424        // Only need to notify Java side for the top frame
425        WebViewCore* core = WebViewCore::getWebViewCore(m_frame->view());
426        if (core)
427            core->didFirstLayout();
428    }
429}
430
431void FrameLoaderClientAndroid::dispatchDidFirstVisuallyNonEmptyLayout()
432{
433    notImplemented();
434}
435
436Frame* FrameLoaderClientAndroid::dispatchCreatePage(const NavigationAction&) {
437    ASSERT(m_frame);
438#ifdef ANDROID_MULTIPLE_WINDOWS
439    if (m_frame->settings() && m_frame->settings()->supportMultipleWindows())
440        // Always a user gesture since window.open maps to
441        // ChromeClientAndroid::createWindow
442        return m_webFrame->createWindow(false, true);
443    else
444#endif
445        // If the client doesn't support multiple windows, just replace the
446        // current frame's contents.
447        return m_frame;
448}
449
450void FrameLoaderClientAndroid::dispatchShow() {
451    ASSERT(m_frame);
452    m_frame->view()->invalidate();
453}
454
455
456static bool TreatAsAttachment(const String& content_disposition) {
457    // Some broken sites just send
458    // Content-Disposition: ; filename="file"
459    // screen those out here.
460    if (content_disposition.startsWith(";"))
461        return false;
462
463    if (content_disposition.startsWith("inline", false))
464        return false;
465
466    // Some broken sites just send
467    // Content-Disposition: filename="file"
468    // without a disposition token... screen those out.
469    if (content_disposition.startsWith("filename", false))
470        return false;
471
472    // Also in use is Content-Disposition: name="file"
473    if (content_disposition.startsWith("name", false))
474        return false;
475
476    // We have a content-disposition of "attachment" or unknown.
477    // RFC 2183, section 2.8 says that an unknown disposition
478    // value should be treated as "attachment"
479    return true;
480}
481
482void FrameLoaderClientAndroid::dispatchDecidePolicyForResponse(FramePolicyFunction func,
483                                const ResourceResponse& response, const ResourceRequest& request) {
484    ASSERT(m_frame);
485    ASSERT(func);
486    if (!func)
487        return;
488
489    PolicyChecker* policy = m_frame->loader()->policyChecker();
490
491    if (request.isNull()) {
492        (policy->*func)(PolicyIgnore);
493        return;
494    }
495    // Default to Use (display internally).
496    PolicyAction action = PolicyUse;
497    // Check if we should Download instead.
498    const String& content_disposition = response.httpHeaderField("Content-Disposition");
499    if (!content_disposition.isEmpty() &&
500            TreatAsAttachment(content_disposition)) {
501        // Server wants to override our normal policy.
502        // Check to see if we are a sub frame (main frame has no owner element)
503        if (m_frame->ownerElement() != 0)
504            action = PolicyIgnore;
505        else
506            action = PolicyDownload;
507        (policy->*func)(action);
508        return;
509    }
510
511    // Ask if it can be handled internally.
512    if (!canShowMIMEType(response.mimeType())) {
513        // Check to see if we are a sub frame (main frame has no owner element)
514        if (m_frame->ownerElement() != 0)
515            action = PolicyIgnore;
516        else
517            action = PolicyDownload;
518        (policy->*func)(action);
519        return;
520    }
521    // A status code of 204 indicates no content change. Ignore the result.
522    WebCore::DocumentLoader* docLoader = m_frame->loader()->activeDocumentLoader();
523    if (docLoader->response().httpStatusCode() == 204)
524        action = PolicyIgnore;
525    (policy->*func)(action);
526}
527
528void FrameLoaderClientAndroid::dispatchDecidePolicyForNewWindowAction(FramePolicyFunction func,
529                                const NavigationAction& action, const ResourceRequest& request,
530                                PassRefPtr<FormState> formState, const String& frameName) {
531    ASSERT(m_frame);
532    ASSERT(func);
533    if (!func)
534        return;
535
536    if (request.isNull()) {
537        (m_frame->loader()->policyChecker()->*func)(PolicyIgnore);
538        return;
539    }
540
541    if (action.type() == NavigationTypeFormSubmitted || action.type() == NavigationTypeFormResubmitted)
542        m_frame->loader()->resetMultipleFormSubmissionProtection();
543
544    // If we get to this point it means that a link has a target that was not
545    // found by the frame tree. Instead of creating a new frame, return the
546    // current frame in dispatchCreatePage.
547    if (canHandleRequest(request))
548        (m_frame->loader()->policyChecker()->*func)(PolicyUse);
549    else
550        (m_frame->loader()->policyChecker()->*func)(PolicyIgnore);
551}
552
553void FrameLoaderClientAndroid::cancelPolicyCheck() {
554    notImplemented();
555}
556
557void FrameLoaderClientAndroid::dispatchUnableToImplementPolicy(const ResourceError&) {
558    notImplemented();
559}
560
561void FrameLoaderClientAndroid::dispatchDecidePolicyForNavigationAction(FramePolicyFunction func,
562                                const NavigationAction& action, const ResourceRequest& request,
563                                PassRefPtr<FormState> formState) {
564    ASSERT(m_frame);
565    ASSERT(func);
566    if (!func)
567        return;
568    if (request.isNull()) {
569        (m_frame->loader()->policyChecker()->*func)(PolicyIgnore);
570        return;
571    }
572
573    // Reset multiple form submission protection. If this is a resubmission, we check with the
574    // user and reset the protection if they choose to resubmit the form (see WebCoreFrameBridge.cpp)
575    if (action.type() == NavigationTypeFormSubmitted)
576        m_frame->loader()->resetMultipleFormSubmissionProtection();
577
578    if (action.type() == NavigationTypeFormResubmitted) {
579        m_webFrame->decidePolicyForFormResubmission(func);
580        return;
581    } else
582        (m_frame->loader()->policyChecker()->*func)(PolicyUse);
583}
584
585void FrameLoaderClientAndroid::dispatchWillSubmitForm(FramePolicyFunction func, PassRefPtr<FormState>) {
586    ASSERT(m_frame);
587    ASSERT(func);
588    (m_frame->loader()->policyChecker()->*func)(PolicyUse);
589}
590
591void FrameLoaderClientAndroid::dispatchWillSendSubmitEvent(HTMLFormElement* form)
592{
593    if (m_webFrame->shouldSaveFormData())
594        m_webFrame->saveFormData(form);
595}
596
597void FrameLoaderClientAndroid::dispatchDidLoadMainResource(DocumentLoader*) {
598    notImplemented();
599}
600
601void FrameLoaderClientAndroid::revertToProvisionalState(DocumentLoader*) {
602    notImplemented();
603}
604
605void FrameLoaderClientAndroid::setMainDocumentError(DocumentLoader* docLoader, const ResourceError& error) {
606    ASSERT(m_frame);
607    if (m_manualLoader) {
608        m_manualLoader->didFail(error);
609        m_manualLoader = NULL;
610        m_hasSentResponseToPlugin = false;
611    } else {
612        if (!error.isNull() && error.errorCode() >= InternalErrorLast && error.errorCode() != ERROR_OK)
613            m_webFrame->reportError(error.errorCode(),
614                    error.localizedDescription(), error.failingURL());
615    }
616}
617
618// This function is called right before the progress is updated.
619void FrameLoaderClientAndroid::willChangeEstimatedProgress() {
620    verifiedOk();
621}
622
623// This function is called after the progress has been updated. The bad part
624// about this is that when a page is completed, this function is called after
625// the progress has been reset to 0.
626void FrameLoaderClientAndroid::didChangeEstimatedProgress() {
627    verifiedOk();
628}
629
630// This will give us the initial estimate when the page first starts to load.
631void FrameLoaderClientAndroid::postProgressStartedNotification() {
632    ASSERT(m_frame);
633    if (m_frame->page())
634        m_webFrame->setProgress(m_frame->page()->progress()->estimatedProgress());
635}
636
637// This will give us any updated progress including the final progress.
638void FrameLoaderClientAndroid::postProgressEstimateChangedNotification() {
639    ASSERT(m_frame);
640    if (m_frame->page())
641        m_webFrame->setProgress(m_frame->page()->progress()->estimatedProgress());
642}
643
644// This is just a notification that the progress has finished. Don't call
645// setProgress(1) because postProgressEstimateChangedNotification will do so.
646void FrameLoaderClientAndroid::postProgressFinishedNotification() {
647    WebViewCore* core =  WebViewCore::getWebViewCore(m_frame->view());
648    if (!m_frame->tree()->parent()) {
649        // only need to notify Java for the top frame
650        core->notifyProgressFinished();
651    }
652    // notify plugins that the frame has loaded
653    core->notifyPluginsOnFrameLoad(m_frame);
654}
655
656void FrameLoaderClientAndroid::setMainFrameDocumentReady(bool) {
657    // this is only interesting once we provide an external API for the DOM
658    notImplemented();
659}
660
661void FrameLoaderClientAndroid::startDownload(const ResourceRequest&) {
662    notImplemented();
663}
664
665void FrameLoaderClientAndroid::willChangeTitle(DocumentLoader*) {
666    verifiedOk();
667}
668
669void FrameLoaderClientAndroid::didChangeTitle(DocumentLoader* loader) {
670    verifiedOk();
671}
672
673void FrameLoaderClientAndroid::finishedLoading(DocumentLoader* docLoader) {
674    // Telling the frame we received some data and passing 0 as the data is our
675    // way to get work done that is normally done when the first bit of data is
676    // received, even for the case of a document with no data (like about:blank)
677    if (!m_manualLoader) {
678        committedLoad(docLoader, 0, 0);
679        return;
680    }
681
682    m_manualLoader->didFinishLoading();
683    m_manualLoader = NULL;
684    m_hasSentResponseToPlugin = false;
685}
686
687void FrameLoaderClientAndroid::updateGlobalHistory() {
688    ASSERT(m_frame);
689
690    DocumentLoader* docLoader = m_frame->loader()->documentLoader();
691    ASSERT(docLoader);
692
693    // Code copied from FrameLoader.cpp:createHistoryItem
694    // Only add this URL to the database if it is a valid page
695    if (docLoader->unreachableURL().isEmpty()
696            && docLoader->response().httpStatusCode() < 400) {
697        m_webFrame->updateVisitedHistory(docLoader->urlForHistory(), false);
698        if (!docLoader->serverRedirectSourceForHistory().isNull())
699            m_webFrame->updateVisitedHistory(KURL(ParsedURLString, docLoader->serverRedirectDestinationForHistory()), false);
700    }
701}
702
703void FrameLoaderClientAndroid::updateGlobalHistoryRedirectLinks() {
704    // Note, do we need to do anything where there is no HistoryItem? If we call
705    // updateGlobalHistory(), we will add bunch of "data:xxx" urls for gmail.com
706    // which is not what we want. Opt to do nothing now.
707}
708
709bool FrameLoaderClientAndroid::shouldGoToHistoryItem(HistoryItem* item) const {
710    // hmmm, seems like we might do a more thoughtful check
711    ASSERT(m_frame);
712    return item != NULL;
713}
714
715bool FrameLoaderClientAndroid::shouldStopLoadingForHistoryItem(HistoryItem* item) const
716{
717    return true;
718}
719
720void FrameLoaderClientAndroid::didDisplayInsecureContent()
721{
722    notImplemented();
723}
724
725void FrameLoaderClientAndroid::didRunInsecureContent(SecurityOrigin*, const KURL&)
726{
727    notImplemented();
728}
729
730void FrameLoaderClientAndroid::committedLoad(DocumentLoader* loader, const char* data, int length) {
731    if (!m_manualLoader)
732        loader->commitData(data, length);
733
734    // commit data may have created a manual plugin loader
735    if (m_manualLoader) {
736        if (!m_hasSentResponseToPlugin) {
737            m_manualLoader->didReceiveResponse(loader->response());
738            // Failure could cause the main document to have an error causing
739            // the manual loader to be reset.
740            if (!m_manualLoader)
741                return;
742            m_hasSentResponseToPlugin = true;
743        }
744        m_manualLoader->didReceiveData(data, length);
745    }
746}
747
748ResourceError FrameLoaderClientAndroid::cancelledError(const ResourceRequest& request) {
749    return ResourceError(String(), InternalErrorCancelled, request.url(), String());
750}
751
752ResourceError FrameLoaderClientAndroid::cannotShowURLError(const ResourceRequest& request) {
753    return ResourceError(String(), InternalErrorCannotShowUrl, request.url(), String());
754}
755
756ResourceError FrameLoaderClientAndroid::interruptForPolicyChangeError(const ResourceRequest& request) {
757    return ResourceError(String(), InternalErrorInterrupted, request.url(), String());
758}
759
760ResourceError FrameLoaderClientAndroid::cannotShowMIMETypeError(const ResourceResponse& request) {
761    return ResourceError(String(), InternalErrorCannotShowMimeType, request.url(), String());
762}
763
764ResourceError FrameLoaderClientAndroid::fileDoesNotExistError(const ResourceResponse& request) {
765    return ResourceError(String(), InternalErrorFileDoesNotExist, request.url(), String());
766}
767
768ResourceError FrameLoaderClientAndroid::pluginWillHandleLoadError(const ResourceResponse& request) {
769    return ResourceError(String(), InternalErrorPluginWillHandleLoadError, request.url(), String());
770}
771
772bool FrameLoaderClientAndroid::shouldFallBack(const ResourceError&) {
773    notImplemented();
774    return false;
775}
776
777bool FrameLoaderClientAndroid::canHandleRequest(const ResourceRequest& request) const {
778    // This is called by WebCore to determine if this load can be handled by the
779    // WebView. In general, we delegate to the WebFrame, which may ask the
780    // embedding application whether it wishes to hijack the load. However, we
781    // don't allow this if the load is ...
782    // - An intrapage navigation
783    // - An iframe with a HTTP or HTTPS scheme URL
784    bool canHandle = WebCore::equalIgnoringFragmentIdentifier(request.url(), m_frame->document()->url()) ||
785            (request.url().protocol().startsWith("http", false) && m_frame->tree() && m_frame->tree()->parent()) ||
786            m_webFrame->canHandleRequest(request);
787
788    // If this is a server-side redirect and the WebView will handle loading it,
789    // notify the WebFrame, which may notify the embedding application that
790    // we're loading a new URL.
791    if (m_didReceiveServerRedirect && canHandle)
792        m_webFrame->loadStarted(m_frame);
793    m_didReceiveServerRedirect = false;
794
795    return canHandle;
796}
797
798bool FrameLoaderClientAndroid::canShowMIMEType(const String& mimeType) const {
799    // FIXME: This looks like it has to do with whether or not a type can be
800    // shown "internally" (i.e. inside the browser) regardless of whether
801    // or not the browser is doing the rendering, e.g. a full page plugin.
802    if (MIMETypeRegistry::isSupportedImageResourceMIMEType(mimeType) ||
803            MIMETypeRegistry::isSupportedNonImageMIMEType(mimeType) ||
804            MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimeType) ||
805            (m_frame && m_frame->settings()
806                    && m_frame->settings()->arePluginsEnabled()
807                    && PluginDatabase::installedPlugins()->isMIMETypeRegistered(
808                            mimeType)) ||
809            (DOMImplementation::isTextMIMEType(mimeType) &&
810             !mimeType.startsWith("text/vnd")) ||
811            DOMImplementation::isXMLMIMEType(mimeType))
812        return true;
813    return false;
814}
815
816bool FrameLoaderClientAndroid::canShowMIMETypeAsHTML(const String& mimeType) const {
817    return false;
818}
819
820bool FrameLoaderClientAndroid::representationExistsForURLScheme(const String&) const {
821    // don't use representation
822    verifiedOk();
823    return false;
824}
825
826String FrameLoaderClientAndroid::generatedMIMETypeForURLScheme(const String& URLScheme) const {
827    // FIXME, copy from Apple's port
828    String mimetype("x-apple-web-kit/");
829    mimetype.append(URLScheme.lower());
830    return mimetype;
831}
832
833void FrameLoaderClientAndroid::frameLoadCompleted() {
834    // copied from Apple port, without this back with sub-frame will trigger ASSERT
835    ASSERT(m_frame);
836}
837
838void FrameLoaderClientAndroid::saveViewStateToItem(HistoryItem* item) {
839    ASSERT(m_frame);
840    ASSERT(item);
841    // store the current scale (only) for the top frame
842    if (!m_frame->tree()->parent()) {
843        // We should have added a bridge when the child item was added to its
844        // parent.
845        AndroidWebHistoryBridge* bridge = item->bridge();
846        ASSERT(bridge);
847        WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view());
848        bridge->setScale(webViewCore->scale());
849        bridge->setTextWrapScale(webViewCore->textWrapScale());
850    }
851
852    WebCore::notifyHistoryItemChanged(item);
853}
854
855void FrameLoaderClientAndroid::restoreViewState() {
856    WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view());
857    HistoryItem* item = m_frame->loader()->history()->currentItem();
858    AndroidWebHistoryBridge* bridge = item->bridge();
859    // restore the scale (only) for the top frame
860    if (!m_frame->tree()->parent()) {
861        webViewCore->restoreScale(bridge->scale(), bridge->textWrapScale());
862    }
863}
864
865void FrameLoaderClientAndroid::dispatchDidAddBackForwardItem(HistoryItem* item) const {
866    ASSERT(m_frame);
867    m_webFrame->addHistoryItem(item);
868}
869
870void FrameLoaderClientAndroid::dispatchDidRemoveBackForwardItem(HistoryItem* item) const {
871    ASSERT(m_frame);
872    m_webFrame->removeHistoryItem(0);
873}
874
875void FrameLoaderClientAndroid::dispatchDidChangeBackForwardIndex() const {
876    ASSERT(m_frame);
877    BackForwardList* list = m_frame->page()->backForwardList();
878    ASSERT(list);
879    m_webFrame->updateHistoryIndex(list->backListCount());
880}
881
882void FrameLoaderClientAndroid::provisionalLoadStarted() {
883    ASSERT(m_frame);
884    m_webFrame->loadStarted(m_frame);
885}
886
887void FrameLoaderClientAndroid::didFinishLoad() {
888    ASSERT(m_frame);
889    m_frame->document()->setExtraLayoutDelay(0);
890    m_webFrame->didFinishLoad(m_frame);
891}
892
893void FrameLoaderClientAndroid::prepareForDataSourceReplacement() {
894    verifiedOk();
895}
896
897PassRefPtr<DocumentLoader> FrameLoaderClientAndroid::createDocumentLoader(
898                    const ResourceRequest& request, const SubstituteData& data) {
899    RefPtr<DocumentLoader> loader = DocumentLoader::create(request, data);
900    return loader.release();
901}
902
903void FrameLoaderClientAndroid::setTitle(const StringWithDirection& title, const KURL& url) {
904    // Not needed. dispatchDidReceiveTitle is called immediately after this.
905    // url is used to update the Apple port history items.
906    verifiedOk();
907}
908
909String FrameLoaderClientAndroid::userAgent(const KURL& u) {
910    return m_webFrame->userAgentForURL(&u);
911}
912
913void FrameLoaderClientAndroid::savePlatformDataToCachedFrame(WebCore::CachedFrame* cachedFrame) {
914    CachedFramePlatformDataAndroid* platformData = new CachedFramePlatformDataAndroid(m_frame->settings());
915    cachedFrame->setCachedFramePlatformData(platformData);
916}
917
918void FrameLoaderClientAndroid::transitionToCommittedFromCachedFrame(WebCore::CachedFrame* cachedFrame) {
919    CachedFramePlatformDataAndroid* platformData = reinterpret_cast<CachedFramePlatformDataAndroid*>(cachedFrame->cachedFramePlatformData());
920#ifdef ANDROID_META_SUPPORT
921   platformData->restoreMetadata(m_frame->settings());
922#endif
923
924#if ENABLE(ANDROID_OVERFLOW_SCROLL)
925#else
926   WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view());
927
928   webViewCore->clearContent();
929#endif
930
931   m_webFrame->transitionToCommitted(m_frame);
932}
933
934void FrameLoaderClientAndroid::transitionToCommittedForNewPage() {
935    ASSERT(m_frame);
936
937#ifdef ANDROID_META_SUPPORT
938    // reset metadata settings for the main frame as they are not preserved cross page
939    if (m_frame == m_frame->page()->mainFrame() && m_frame->settings())
940        m_frame->settings()->resetMetadataSettings();
941#endif
942
943    // Save the old WebViewCore before creating a new FrameView. There is one
944    // WebViewCore per page. Each frame, including the main frame and sub frame,
945    // has a 1:1 FrameView and WebFrameView.
946    WebViewCore* webViewCore = WebViewCore::getWebViewCore(m_frame->view());
947    Retain(webViewCore);
948
949    // Save the old WebFrameView's bounds and apply them to the new WebFrameView
950    RefPtr<WebCore::FrameView> oldFrameView = m_frame->view();
951    WebFrameView* oldWebFrameView = static_cast<WebFrameView*> (oldFrameView->platformWidget());
952    IntRect bounds;
953    if (oldWebFrameView)
954        bounds = oldWebFrameView->getBounds();
955    const float oldZoomFactor = oldFrameView->frame()->textZoomFactor();
956    m_frame->createView(bounds.size(), oldFrameView->baseBackgroundColor(), oldFrameView->isTransparent(),
957            oldFrameView->fixedLayoutSize(), oldFrameView->useFixedLayout());
958    if (oldZoomFactor != 1.0f && oldZoomFactor != m_frame->textZoomFactor()) {
959        m_frame->setTextZoomFactor(oldZoomFactor);
960    }
961
962    if (oldWebFrameView) {
963        IntRect visBounds = oldWebFrameView->getVisibleBounds();
964        IntRect windowBounds = oldWebFrameView->getWindowBounds();
965        // Create a new WebFrameView for the new FrameView
966        WebFrameView* newFrameView = new WebFrameView(m_frame->view(), webViewCore);
967        newFrameView->setLocation(bounds.x(), bounds.y());
968        newFrameView->setSize(bounds.width(), bounds.height());
969        newFrameView->setVisibleSize(visBounds.width(), visBounds.height());
970        newFrameView->setWindowBounds(windowBounds.x(), windowBounds.y(), windowBounds.width(), windowBounds.height());
971        // newFrameView attaches itself to FrameView which Retains the reference, so
972        // call Release for newFrameView
973        Release(newFrameView);
974    }
975    // WebFrameView Retains webViewCore, so call Release for webViewCore
976    Release(webViewCore);
977
978    m_webFrame->transitionToCommitted(m_frame);
979}
980
981void FrameLoaderClientAndroid::dispatchDidBecomeFrameset(bool)
982{
983}
984
985bool FrameLoaderClientAndroid::canCachePage() const {
986    return true;
987}
988
989void FrameLoaderClientAndroid::download(ResourceHandle* handle, const ResourceRequest&,
990                                const ResourceRequest&, const ResourceResponse&) {
991    // Get the C++ side of the load listener and tell it to handle the download
992    handle->getInternal()->m_loader->downloadFile();
993}
994
995WTF::PassRefPtr<WebCore::Frame> FrameLoaderClientAndroid::createFrame(const KURL& url, const String& name,
996                        HTMLFrameOwnerElement* ownerElement, const String& referrer,
997                        bool allowsScrolling, int marginWidth, int marginHeight)
998{
999    Frame* parent = ownerElement->document()->frame();
1000    FrameLoaderClientAndroid* loaderC = new FrameLoaderClientAndroid(m_webFrame);
1001    RefPtr<Frame> pFrame = Frame::create(parent->page(), ownerElement, loaderC);
1002    Frame* newFrame = pFrame.get();
1003    loaderC->setFrame(newFrame);
1004    // Append the subframe to the parent and set the name of the subframe. The name must be set after
1005    // appending the child so that the name becomes unique.
1006    parent->tree()->appendChild(newFrame);
1007    newFrame->tree()->setName(name);
1008    // Create a new FrameView and WebFrameView for the child frame to draw into.
1009    RefPtr<FrameView> frameView = FrameView::create(newFrame);
1010    // Attach the frameView to the newFrame.
1011    newFrame->setView(frameView);
1012    newFrame->init();
1013    newFrame->selection()->setFocused(true);
1014    ALOGV("::WebCore:: createSubFrame returning %p", newFrame);
1015
1016    // The creation of the frame may have run arbitrary JavaScript that removed it from the page already.
1017    if (!pFrame->page())
1018        return 0;
1019
1020    parent->loader()->loadURLIntoChildFrame(url, referrer, pFrame.get());
1021
1022    // onLoad may cuase the frame to be removed from the document. Allow the RefPtr to delete the child frame.
1023    if (!pFrame->tree()->parent())
1024        return NULL;
1025
1026    return pFrame.release();
1027}
1028
1029// YouTube flash url path starts with /v/
1030static const char slash_v_slash[] = { '/', 'v', '/' };
1031static const char slash_e_slash[] = { '/', 'e', '/' };
1032
1033static bool isValidYouTubeVideo(const String& path)
1034{
1035    if (!charactersAreAllASCII(path.characters(), path.length()))
1036        return false;
1037    unsigned int len = path.length();
1038    if (len <= sizeof(slash_v_slash)) // check for more than just /v/
1039        return false;
1040    CString str = path.lower().utf8();
1041    const char* data = str.data();
1042    // Youtube flash url can start with /v/ or /e/
1043    if (memcmp(data, slash_v_slash, sizeof(slash_v_slash)) != 0)
1044        if (memcmp(data, slash_e_slash, sizeof(slash_e_slash)) != 0)
1045            return false;
1046    // Start after /v/
1047    for (unsigned int i = sizeof(slash_v_slash); i < len; i++) {
1048        char c = data[i];
1049        // Check for alpha-numeric characters only.
1050        if (WTF::isASCIIAlphanumeric(c) || c == '_' || c == '-')
1051            continue;
1052        // The url can have more parameters such as &hl=en after the video id.
1053        // Once we start seeing extra parameters we can return true.
1054        return c == '&' && i > sizeof(slash_v_slash);
1055    }
1056    return true;
1057}
1058
1059static bool isYouTubeUrl(const KURL& url, const String& mimeType)
1060{
1061    String host = url.host();
1062    bool youtube = host.endsWith("youtube.com")
1063            || host.endsWith("youtube-nocookie.com");
1064    return youtube && isValidYouTubeVideo(url.path())
1065            && equalIgnoringCase(mimeType, "application/x-shockwave-flash");
1066}
1067
1068static bool isYouTubeInstalled() {
1069    return WebCore::packageNotifier().isPackageInstalled("com.google.android.youtube");
1070}
1071
1072// Use PluginViewBase rather than an Android specific sub class as we do not require any
1073// Android specific functionality; this just renders a placeholder which will later
1074// activate the real plugin.
1075class PluginToggleWidget : public PluginViewBase {
1076public:
1077    PluginToggleWidget(Frame* parent, const IntSize& size,
1078            HTMLPlugInElement* elem, const KURL& url,
1079            const WTF::Vector<String>& paramNames,
1080            const WTF::Vector<String>& paramValues, const String& mimeType,
1081            bool loadManually)
1082        : PluginViewBase(0)
1083        , m_parent(parent)
1084        , m_size(size)
1085        , m_element(elem)
1086        , m_url(url)
1087        , m_paramNames(paramNames)
1088        , m_paramValues(paramValues)
1089        , m_mimeType(mimeType)
1090        , m_loadManually(loadManually)
1091    {
1092        resize(size);
1093    }
1094
1095    virtual void paint(GraphicsContext* ctx, const IntRect& rect)
1096    {
1097        // Most of this code is copied from PluginView::paintMissingPluginIcon
1098        // with slight modification.
1099
1100        static RefPtr<Image> image;
1101        if (!image) {
1102            image = Image::loadPlatformResource("togglePlugin");
1103        }
1104
1105        IntRect imageRect(x(), y(), image->width(), image->height());
1106
1107        int xOffset = (width() - imageRect.width()) >> 1;
1108        int yOffset = (height() - imageRect.height()) >> 1;
1109
1110        imageRect.move(xOffset, yOffset);
1111
1112        if (!rect.intersects(imageRect))
1113            return;
1114
1115        // FIXME: We need to clip similarly to paintMissingPluginIcon but it is
1116        // way screwed up right now. It has something to do with how we tell
1117        // webkit the scroll position and it causes the placeholder to get
1118        // clipped very badly. http://b/issue?id=2533303
1119
1120        ctx->save();
1121        ctx->clip(frameRect());
1122
1123        ctx->setFillColor(Color::white, ColorSpaceDeviceRGB);
1124        ctx->fillRect(frameRect());
1125        if (frameRect().contains(imageRect)) {
1126            // Leave a 2 pixel padding.
1127            const int pixelWidth = 2;
1128            IntRect innerRect = frameRect();
1129            innerRect.inflate(-pixelWidth);
1130            // Draw a 2 pixel light gray border.
1131            ctx->setStrokeColor(Color::lightGray, ColorSpaceDeviceRGB);
1132            ctx->strokeRect(innerRect, pixelWidth);
1133        }
1134
1135        // Draw the image in the center
1136        ctx->drawImage(image.get(), ColorSpaceDeviceRGB, imageRect.location());
1137        ctx->restore();
1138    }
1139
1140    virtual void handleEvent(Event* event)
1141    {
1142        if (event->type() != eventNames().clickEvent)
1143            return;
1144
1145        Frame* frame = m_parent->page()->mainFrame();
1146        while (frame) {
1147            RenderView* view = frame->contentRenderer();
1148            const HashSet<RenderWidget*> widgets = view->widgets();
1149            HashSet<RenderWidget*>::const_iterator it = widgets.begin();
1150            HashSet<RenderWidget*>::const_iterator end = widgets.end();
1151            for (; it != end; ++it) {
1152                Widget* widget = (*it)->widget();
1153                // PluginWidget is used only with PluginToggleWidget
1154                if (widget && widget->isPluginViewBase()) {
1155                    PluginToggleWidget* ptw =
1156                            static_cast<PluginToggleWidget*>(widget);
1157                    ptw->swapPlugin(*it);
1158                }
1159            }
1160            frame = frame->tree()->traverseNext();
1161        }
1162    }
1163
1164    void swapPlugin(RenderWidget* renderer) {
1165        typedef FrameLoaderClientAndroid FLCA;
1166        FLCA* client = static_cast<FLCA*>(m_parent->loader()->client());
1167        client->enableOnDemandPlugins();
1168        WTF::PassRefPtr<PluginView> prpWidget =
1169                PluginView::create(m_parent.get(),
1170                                   m_size,
1171                                   m_element,
1172                                   m_url,
1173                                   m_paramNames,
1174                                   m_paramValues,
1175                                   m_mimeType,
1176                                   m_loadManually);
1177        RefPtr<Widget> myProtector(this);
1178        prpWidget->focusPluginElement();
1179        renderer->setWidget(prpWidget);
1180    }
1181
1182private:
1183    void invalidateRect(const IntRect& rect) { }
1184
1185    RefPtr<Frame>       m_parent;
1186    IntSize             m_size;
1187    HTMLPlugInElement*  m_element;
1188    KURL                m_url;
1189    WTF::Vector<String> m_paramNames;
1190    WTF::Vector<String> m_paramValues;
1191    String              m_mimeType;
1192    bool                m_loadManually;
1193};
1194
1195WTF::PassRefPtr<Widget> FrameLoaderClientAndroid::createPlugin(
1196        const IntSize& size,
1197        HTMLPlugInElement* element,
1198        const KURL& url,
1199        const WTF::Vector<String>& names,
1200        const WTF::Vector<String>& values,
1201        const String& mimeType,
1202        bool loadManually) {
1203    WTF::PassRefPtr<PluginView> prpWidget = 0;
1204#ifdef ANDROID_PLUGINS
1205    // This is copied from PluginView.cpp. We need to determine if a plugin
1206    // will be found before doing some of the work in PluginView.
1207    String mimeTypeCopy = mimeType;
1208    PluginPackage* plugin =
1209            PluginDatabase::installedPlugins()->findPlugin(url, mimeTypeCopy);
1210    if (!plugin && PluginDatabase::installedPlugins()->refresh()) {
1211        mimeTypeCopy = mimeType;
1212        plugin = PluginDatabase::installedPlugins()->findPlugin(url,
1213                                                                mimeTypeCopy);
1214    }
1215    Settings* settings = m_frame->settings();
1216    // Do the placeholder if plugins are on-demand and there is a plugin for the
1217    // given mime type.
1218    if (settings && settings->arePluginsOnDemand() && plugin &&
1219            !m_onDemandPluginsEnabled) {
1220        return adoptRef(new PluginToggleWidget(m_frame, size, element, url,
1221                    names, values, mimeType, loadManually));
1222    }
1223    prpWidget = PluginView::create(m_frame,
1224                                   size,
1225                                   element,
1226                                   url,
1227                                   names,
1228                                   values,
1229                                   mimeType,
1230                                   loadManually);
1231    // Return the plugin if it was loaded successfully. Otherwise, fallback to
1232    // the youtube placeholder if possible. No need to check prpWidget as
1233    // PluginView::create will create a PluginView for missing plugins.
1234    // Note: this check really only checks if the plugin was found and not if
1235    // the plugin was loaded.
1236    if (prpWidget->status() == PluginStatusLoadedSuccessfully)
1237        return prpWidget;
1238#endif
1239    // Create an iframe for youtube urls.
1240    if (isYouTubeUrl(url, mimeType) && isYouTubeInstalled()) {
1241        WTF::RefPtr<Frame> frame = createFrame(blankURL(), String(), element,
1242                String(), false, 0, 0);
1243        if (frame) {
1244            // grab everything after /v/
1245            String videoId = url.path().substring(sizeof(slash_v_slash));
1246            // Extract just the video id
1247            unsigned videoIdEnd = 0;
1248            for (; videoIdEnd < videoId.length(); videoIdEnd++) {
1249                if (videoId[videoIdEnd] == '&') {
1250                    videoId = videoId.left(videoIdEnd);
1251                    break;
1252                }
1253            }
1254            AssetManager* am = globalAssetManager();
1255            Asset* a = am->open("webkit/youtube.html",
1256                    Asset::ACCESS_BUFFER);
1257            if (!a)
1258                return NULL;
1259            String s = String((const char*)a->getBuffer(false), a->getLength());
1260            s = s.replace("VIDEO_ID", videoId);
1261            delete a;
1262            loadDataIntoFrame(frame.get(),
1263                    KURL(ParsedURLString, "file:///android_asset/webkit/"), String(), s);
1264            // Transfer ownership to a local refptr.
1265            WTF::RefPtr<Widget> widget(frame->view());
1266            return widget.release();
1267        }
1268    }
1269    return prpWidget;
1270}
1271
1272void FrameLoaderClientAndroid::redirectDataToPlugin(Widget* pluginWidget) {
1273    // Do not redirect data if the Widget is our plugin placeholder.
1274    if (pluginWidget->isPluginView()) {
1275        m_manualLoader = static_cast<PluginView*>(pluginWidget);
1276    }
1277}
1278
1279WTF::PassRefPtr<Widget> FrameLoaderClientAndroid::createJavaAppletWidget(const IntSize&, HTMLAppletElement*,
1280                                        const KURL& baseURL, const WTF::Vector<String>& paramNames,
1281                                        const WTF::Vector<String>& paramValues) {
1282    // don't support widget yet
1283    notImplemented();
1284    return 0;
1285}
1286
1287void FrameLoaderClientAndroid::didTransferChildFrameToNewDocument(WebCore::Page*)
1288{
1289    ASSERT(m_frame);
1290    // m_webFrame points to the WebFrame for the page that our frame previously
1291    // belonged to. If the frame now belongs to a new page, we need to update
1292    // m_webFrame to point to the WebFrame for the new page.
1293    Page* newPage = m_frame->page();
1294    if (newPage != m_webFrame->page()) {
1295        ChromeClientAndroid* chromeClient = static_cast<ChromeClientAndroid*>(newPage->chrome()->client());
1296        Release(m_webFrame);
1297        m_webFrame = chromeClient->webFrame();
1298        Retain(m_webFrame);
1299    }
1300}
1301
1302void FrameLoaderClientAndroid::transferLoadingResourceFromPage(unsigned long, DocumentLoader*, const ResourceRequest&, Page*)
1303{
1304    notImplemented();
1305}
1306
1307// This function is used by the <OBJECT> element to determine the type of
1308// the contents and work out if it can render it.
1309ObjectContentType FrameLoaderClientAndroid::objectContentType(const KURL& url, const String& mimeType, bool shouldPreferPlugInsForImages) {
1310    return FrameLoader::defaultObjectContentType(url, mimeType, shouldPreferPlugInsForImages);
1311}
1312
1313// This function allows the application to set the correct CSS media
1314// style. Android could use it to set the media style 'handheld'. Safari
1315// may use it to set the media style to 'print' when the user wants to print
1316// a particular web page.
1317String FrameLoaderClientAndroid::overrideMediaType() const {
1318    notImplemented();
1319    return String();
1320}
1321
1322// This function is used to re-attach Javascript<->native code classes.
1323void FrameLoaderClientAndroid::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld* world)
1324{
1325    if (world != mainThreadNormalWorld())
1326        return;
1327
1328    ASSERT(m_frame);
1329    ALOGV("::WebCore:: windowObjectCleared called on frame %p for %s\n",
1330            m_frame, m_frame->document()->url().string().ascii().data());
1331    m_webFrame->windowObjectCleared(m_frame);
1332}
1333
1334void FrameLoaderClientAndroid::documentElementAvailable() {
1335}
1336
1337// functions new to Jun-07 tip of tree merge:
1338ResourceError FrameLoaderClientAndroid::blockedError(ResourceRequest const& request) {
1339    return ResourceError(String(), InternalErrorFileDoesNotExist, String(), String());
1340}
1341
1342// functions new to Nov-07 tip of tree merge:
1343void FrameLoaderClientAndroid::didPerformFirstNavigation() const {
1344    // This seems to be just a notification that the UI can listen to, to
1345    // know if the user has performed first navigation action.
1346    // It is called from
1347    // void FrameLoader::addBackForwardItemClippedAtTarget(bool doClip)
1348    // "Navigation" here means a transition from one page to another that
1349    // ends up in the back/forward list.
1350}
1351
1352void FrameLoaderClientAndroid::registerForIconNotification(bool listen) {
1353    if (listen)
1354        WebIconDatabase::RegisterForIconNotification(this);
1355    else
1356        WebIconDatabase::UnregisterForIconNotification(this);
1357}
1358
1359// This is the WebIconDatabaseClient method for receiving a notification when we
1360// get the icon for the page.
1361void FrameLoaderClientAndroid::didAddIconForPageUrl(const String& pageUrl) {
1362    // This call must happen before dispatchDidReceiveIcon since that method
1363    // may register for icon notifications again since the icon data may have
1364    // to be read from disk.
1365    registerForIconNotification(false);
1366    KURL u(ParsedURLString, pageUrl);
1367    if (equalIgnoringFragmentIdentifier(u, m_frame->document()->url())) {
1368        dispatchDidReceiveIcon();
1369    }
1370}
1371
1372void FrameLoaderClientAndroid::dispatchDidChangeIcons() {
1373    notImplemented();
1374}
1375
1376PassRefPtr<FrameNetworkingContext> FrameLoaderClientAndroid::createNetworkingContext()
1377{
1378    return FrameNetworkingContextAndroid::create(getFrame());
1379}
1380
1381}
1382