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