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