/*
 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#include "config.h"
#include "WebKitDLL.h"
#include "WebFrame.h"

#include "CFDictionaryPropertyBag.h"
#include "COMPtr.h"
#include "DefaultPolicyDelegate.h"
#include "DOMCoreClasses.h"
#include "FormValuesPropertyBag.h"
#include "MarshallingHelpers.h"
#include "WebActionPropertyBag.h"
#include "WebCachedPagePlatformData.h"
#include "WebChromeClient.h"
#include "WebDocumentLoader.h"
#include "WebDownload.h"
#include "WebError.h"
#include "WebMutableURLRequest.h"
#include "WebEditorClient.h"
#include "WebFramePolicyListener.h"
#include "WebHistory.h"
#include "WebKit.h"
#include "WebKitStatisticsPrivate.h"
#include "WebNotificationCenter.h"
#include "WebView.h"
#include "WebDataSource.h"
#include "WebHistoryItem.h"
#include "WebScriptDebugger.h"
#include "WebScriptDebugServer.h"
#include "WebURLAuthenticationChallenge.h"
#include "WebURLResponse.h"
#pragma warning( push, 0 )
#include <WebCore/AuthenticationChallenge.h>
#include <WebCore/BString.h>
#include <WebCore/Cache.h>
#include <WebCore/Document.h>
#include <WebCore/DocumentLoader.h>
#include <WebCore/DOMImplementation.h>
#include <WebCore/DOMWindow.h>
#include <WebCore/Event.h>
#include <WebCore/FormState.h>
#include <WebCore/FrameLoader.h>
#include <WebCore/FrameLoadRequest.h>
#include <WebCore/FrameTree.h>
#include <WebCore/FrameView.h>
#include <WebCore/FrameWin.h>
#include <WebCore/GDIObjectCounter.h>
#include <WebCore/GraphicsContext.h>
#include <WebCore/HistoryItem.h>
#include <WebCore/HTMLFormElement.h>
#include <WebCore/HTMLGenericFormElement.h>
#include <WebCore/HTMLInputElement.h>
#include <WebCore/HTMLNames.h>
#include <WebCore/KeyboardEvent.h>
#include <WebCore/MIMETypeRegistry.h>
#include <WebCore/MouseRelatedEvent.h>
#include <WebCore/NotImplemented.h>
#include <WebCore/Page.h>
#include <WebCore/PlatformKeyboardEvent.h>
#include <WebCore/PluginInfoStore.h>
#include <WebCore/PluginDatabase.h>
#include <WebCore/PluginView.h>
#include <WebCore/ResourceHandle.h>
#include <WebCore/ResourceHandleWin.h>
#include <WebCore/ResourceRequest.h>
#include <WebCore/RenderFrame.h>
#include <WebCore/RenderTreeAsText.h>
#include <WebCore/Settings.h>
#include <WebCore/TextIterator.h>
#include <WebCore/kjs_binding.h>
#include <WebCore/kjs_proxy.h>
#include <WebCore/kjs_window.h>
#include <JavaScriptCore/APICast.h>
#include <wtf/MathExtras.h>
#pragma warning(pop)

#include <CoreGraphics/CoreGraphics.h>

// CG SPI used for printing
extern "C" {
    CGAffineTransform CGContextGetBaseCTM(CGContextRef c); 
    void CGContextSetBaseCTM(CGContextRef c, CGAffineTransform m); 
}

using namespace WebCore;
using namespace HTMLNames;

#define FLASH_REDRAW 0


// By imaging to a width a little wider than the available pixels,
// thin pages will be scaled down a little, matching the way they
// print in IE and Camino. This lets them use fewer sheets than they
// would otherwise, which is presumably why other browsers do this.
// Wide pages will be scaled down more than this.
const float PrintingMinimumShrinkFactor = 1.25f;

// This number determines how small we are willing to reduce the page content
// in order to accommodate the widest line. If the page would have to be
// reduced smaller to make the widest line fit, we just clip instead (this
// behavior matches MacIE and Mozilla, at least)
const float PrintingMaximumShrinkFactor = 2.0f;


// {A3676398-4485-4a9d-87DC-CB5A40E6351D}
const GUID IID_WebFrame = 
{ 0xa3676398, 0x4485, 0x4a9d, { 0x87, 0xdc, 0xcb, 0x5a, 0x40, 0xe6, 0x35, 0x1d } };


//-----------------------------------------------------------------------------
// Helpers to convert from WebCore to WebKit type
WebFrame* kit(Frame* frame)
{
    if (!frame)
        return 0;

    FrameLoaderClient* frameLoaderClient = frame->loader()->client();
    if (frameLoaderClient)
        return static_cast<WebFrame*>(frameLoaderClient);  // eek, is there a better way than static cast?
    return 0;
}

Frame* core(WebFrame* webFrame)
{
    if (!webFrame)
        return 0;
    return webFrame->impl();
}

// This function is not in WebFrame.h because we don't want to advertise the ability to get a non-const Frame from a const WebFrame
Frame* core(const WebFrame* webFrame)
{
    if (!webFrame)
        return 0;
    return const_cast<WebFrame*>(webFrame)->impl();
}

WebView* kit(Page* page)
{
    return page ? static_cast<WebChromeClient*>(page->chrome()->client())->webView() : 0;
}

//-----------------------------------------------------------------------------

static Element *elementFromDOMElement(IDOMElement *element)
{
    if (!element)
        return 0;

    COMPtr<IDOMElementPrivate> elePriv;
    HRESULT hr = element->QueryInterface(IID_IDOMElementPrivate, (void**) &elePriv);
    if (SUCCEEDED(hr)) {
        Element* ele;
        hr = elePriv->coreElement((void**)&ele);
        if (SUCCEEDED(hr))
            return ele;
    }
    return 0;
}

static HTMLFormElement *formElementFromDOMElement(IDOMElement *element)
{
    if (!element)
        return 0;

    IDOMElementPrivate* elePriv;
    HRESULT hr = element->QueryInterface(IID_IDOMElementPrivate, (void**) &elePriv);
    if (SUCCEEDED(hr)) {
        Element* ele;
        hr = elePriv->coreElement((void**)&ele);
        elePriv->Release();
        if (SUCCEEDED(hr) && ele && ele->hasTagName(formTag))
            return static_cast<HTMLFormElement*>(ele);
    }
    return 0;
}

static HTMLInputElement* inputElementFromDOMElement(IDOMElement* element)
{
    if (!element)
        return 0;

    IDOMElementPrivate* elePriv;
    HRESULT hr = element->QueryInterface(IID_IDOMElementPrivate, (void**) &elePriv);
    if (SUCCEEDED(hr)) {
        Element* ele;
        hr = elePriv->coreElement((void**)&ele);
        elePriv->Release();
        if (SUCCEEDED(hr) && ele && ele->hasTagName(inputTag))
            return static_cast<HTMLInputElement*>(ele);
    }
    return 0;
}

// WebFramePrivate ------------------------------------------------------------

class WebFrame::WebFramePrivate {
public:
    WebFramePrivate() 
        : frame(0)
        , webView(0)
        , m_policyFunction(0)
        , m_pluginView(0) 
        , m_hasSentResponseToPlugin(false) 
    { 
    }

    ~WebFramePrivate() { }
    FrameView* frameView() { return frame ? frame->view() : 0; }

    Frame* frame;
    WebView* webView;
    FramePolicyFunction m_policyFunction;
    COMPtr<WebFramePolicyListener> m_policyListener;
    
    // Points to the plugin view that data should be redirected to.
    PluginView* m_pluginView;
    bool m_hasSentResponseToPlugin;
};

// WebFrame ----------------------------------------------------------------

WebFrame::WebFrame()
    : WebFrameLoaderClient(this)
    , m_refCount(0)
    , d(new WebFrame::WebFramePrivate)
    , m_quickRedirectComing(false)
    , m_inPrintingMode(false)
    , m_pageHeight(0)
    , m_scriptDebugger(0)
{
    WebFrameCount++;
    gClassCount++;
}

WebFrame::~WebFrame()
{
    delete d;
    WebFrameCount--;
    gClassCount--;
}

WebFrame* WebFrame::createInstance()
{
    WebFrame* instance = new WebFrame();
    instance->AddRef();
    return instance;
}

HRESULT STDMETHODCALLTYPE WebFrame::setAllowsScrolling(
    /* [in] */ BOOL flag)
{
    if (Frame* frame = core(this))
        if (FrameView* view = frame->view())
            view->setAllowsScrolling(!!flag);

    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::allowsScrolling(
    /* [retval][out] */ BOOL *flag)
{
    if (flag)
        if (Frame* frame = core(this))
            if (FrameView* view = frame->view())
                *flag = view->allowsScrolling();

    return S_OK;
}


// IUnknown -------------------------------------------------------------------

HRESULT STDMETHODCALLTYPE WebFrame::QueryInterface(REFIID riid, void** ppvObject)
{
    *ppvObject = 0;
    if (IsEqualGUID(riid, IID_WebFrame))
        *ppvObject = this;
    else if (IsEqualGUID(riid, IID_IUnknown))
        *ppvObject = static_cast<IWebFrame*>(this);
    else if (IsEqualGUID(riid, IID_IWebFrame))
        *ppvObject = static_cast<IWebFrame*>(this);
    else if (IsEqualGUID(riid, IID_IWebFramePrivate))
        *ppvObject = static_cast<IWebFramePrivate*>(this);
    else if (IsEqualGUID(riid, IID_IWebDocumentText))
        *ppvObject = static_cast<IWebDocumentText*>(this);
    else
        return E_NOINTERFACE;

    AddRef();
    return S_OK;
}

ULONG STDMETHODCALLTYPE WebFrame::AddRef(void)
{
    return ++m_refCount;
}

ULONG STDMETHODCALLTYPE WebFrame::Release(void)
{
    ULONG newRef = --m_refCount;
    if (!newRef)
        delete(this);

    return newRef;
}

// IWebFrame -------------------------------------------------------------------

HRESULT STDMETHODCALLTYPE WebFrame::name( 
    /* [retval][out] */ BSTR* frameName)
{
    if (!frameName) {
        ASSERT_NOT_REACHED();
        return E_POINTER;
    }

    *frameName = 0;

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    *frameName = BString(coreFrame->tree()->name()).release();
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::webView( 
    /* [retval][out] */ IWebView** view)
{
    *view = 0;
    if (!d->webView)
        return E_FAIL;
    *view = d->webView;
    (*view)->AddRef();
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::frameView(
    /* [retval][out] */ IWebFrameView** /*view*/)
{
    ASSERT_NOT_REACHED();
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebFrame::DOMDocument( 
    /* [retval][out] */ IDOMDocument** result)
{
    if (!result) {
        ASSERT_NOT_REACHED();
        return E_POINTER;
    }

    *result = 0;

    if (Frame* coreFrame = core(this))
        if (Document* document = coreFrame->document())
            *result = DOMDocument::createInstance(document);

    return *result ? S_OK : E_FAIL;
}

HRESULT STDMETHODCALLTYPE WebFrame::frameElement( 
    /* [retval][out] */ IDOMHTMLElement** /*frameElement*/)
{
    ASSERT_NOT_REACHED();
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebFrame::currentForm( 
        /* [retval][out] */ IDOMElement **currentForm)
{
    if (!currentForm) {
        ASSERT_NOT_REACHED();
        return E_POINTER;
    }

    *currentForm = 0;

    if (Frame* coreFrame = core(this))
        if (HTMLFormElement* formElement = coreFrame->currentForm())
            *currentForm = DOMElement::createInstance(formElement);

    return *currentForm ? S_OK : E_FAIL;
}

JSGlobalContextRef STDMETHODCALLTYPE WebFrame::globalContext()
{
    Frame* coreFrame = core(this);
    if (!coreFrame)
        return 0;

    return toGlobalRef(coreFrame->scriptProxy()->globalObject()->globalExec());
}

HRESULT STDMETHODCALLTYPE WebFrame::loadRequest( 
    /* [in] */ IWebURLRequest* request)
{
    COMPtr<WebMutableURLRequest> requestImpl;

    HRESULT hr = request->QueryInterface(&requestImpl);
    if (FAILED(hr))
        return hr;
 
    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    coreFrame->loader()->load(requestImpl->resourceRequest());
    return S_OK;
}

void WebFrame::loadData(PassRefPtr<WebCore::SharedBuffer> data, BSTR mimeType, BSTR textEncodingName, BSTR baseURL, BSTR failingURL)
{
    String mimeTypeString(mimeType, SysStringLen(mimeType));
    if (!mimeType)
        mimeTypeString = "text/html";

    String encodingString(textEncodingName, SysStringLen(textEncodingName));
    KURL baseKURL(String(baseURL ? baseURL : L"", SysStringLen(baseURL)));
    KURL failingKURL(String(failingURL, SysStringLen(failingURL)));

    ResourceRequest request(baseKURL);
    SubstituteData substituteData(data, mimeTypeString, encodingString, failingKURL);

    // This method is only called from IWebFrame methods, so don't ASSERT that the Frame pointer isn't null.
    if (Frame* coreFrame = core(this))
        coreFrame->loader()->load(request, substituteData);
}


HRESULT STDMETHODCALLTYPE WebFrame::loadData( 
    /* [in] */ IStream* data,
    /* [in] */ BSTR mimeType,
    /* [in] */ BSTR textEncodingName,
    /* [in] */ BSTR url)
{
    RefPtr<SharedBuffer> sharedBuffer = new SharedBuffer();

    STATSTG stat;
    if (SUCCEEDED(data->Stat(&stat, STATFLAG_NONAME))) {
        if (!stat.cbSize.HighPart && stat.cbSize.LowPart) {
            Vector<char> dataBuffer(stat.cbSize.LowPart);
            ULONG read;
            // FIXME: this does a needless copy, would be better to read right into the SharedBuffer
            // or adopt the Vector or something.
            if (SUCCEEDED(data->Read(dataBuffer.data(), static_cast<ULONG>(dataBuffer.size()), &read)))
                sharedBuffer->append(dataBuffer.data(), static_cast<int>(dataBuffer.size()));
        }
    }

    loadData(sharedBuffer, mimeType, textEncodingName, url, 0);
    return S_OK;
}

void WebFrame::loadHTMLString(BSTR string, BSTR baseURL, BSTR unreachableURL)
{
    RefPtr<SharedBuffer> sharedBuffer = new SharedBuffer(reinterpret_cast<char*>(string), sizeof(UChar) * SysStringLen(string));
    BString utf16Encoding(TEXT("utf-16"), 6);
    loadData(sharedBuffer.release(), 0, utf16Encoding, baseURL, unreachableURL);
}

HRESULT STDMETHODCALLTYPE WebFrame::loadHTMLString( 
    /* [in] */ BSTR string,
    /* [in] */ BSTR baseURL)
{
    loadHTMLString(string, baseURL, 0);
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::loadAlternateHTMLString( 
    /* [in] */ BSTR str,
    /* [in] */ BSTR baseURL,
    /* [in] */ BSTR unreachableURL)
{
    loadHTMLString(str, baseURL, unreachableURL);
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::loadArchive( 
    /* [in] */ IWebArchive* /*archive*/)
{
    ASSERT_NOT_REACHED();
    return E_NOTIMPL;
}

static inline WebDataSource *getWebDataSource(DocumentLoader* loader)
{
    return loader ? static_cast<WebDocumentLoader*>(loader)->dataSource() : 0;
}

HRESULT STDMETHODCALLTYPE WebFrame::dataSource( 
    /* [retval][out] */ IWebDataSource** source)
{
    if (!source) {
        ASSERT_NOT_REACHED();
        return E_POINTER;
    }

    *source = 0;

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    WebDataSource* webDataSource = getWebDataSource(coreFrame->loader()->documentLoader());

    *source = webDataSource;

    if (webDataSource)
        webDataSource->AddRef(); 

    return *source ? S_OK : E_FAIL;
}

HRESULT STDMETHODCALLTYPE WebFrame::provisionalDataSource( 
    /* [retval][out] */ IWebDataSource** source)
{
    if (!source) {
        ASSERT_NOT_REACHED();
        return E_POINTER;
    }

    *source = 0;

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    WebDataSource* webDataSource = getWebDataSource(coreFrame->loader()->provisionalDocumentLoader());

    *source = webDataSource;

    if (webDataSource)
        webDataSource->AddRef(); 

    return *source ? S_OK : E_FAIL;
}

KURL WebFrame::url() const
{
    Frame* coreFrame = core(this);
    if (!coreFrame)
        return KURL();

    return coreFrame->loader()->url();
}

void WebFrame::attachScriptDebugger()
{
    if (!m_scriptDebugger && core(this)->scriptProxy()->haveGlobalObject())
        m_scriptDebugger.set(new WebScriptDebugger(this));
}

void WebFrame::detachScriptDebugger()
{
    m_scriptDebugger.clear();
}

HRESULT STDMETHODCALLTYPE WebFrame::stopLoading( void)
{
    if (Frame* coreFrame = core(this))
        coreFrame->loader()->stopAllLoaders();
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::reload( void)
{
    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    coreFrame->loader()->reload();
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::findFrameNamed( 
    /* [in] */ BSTR name,
    /* [retval][out] */ IWebFrame** frame)
{
    if (!frame) {
        ASSERT_NOT_REACHED();
        return E_POINTER;
    }

    *frame = 0;

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    Frame* foundFrame = coreFrame->tree()->find(AtomicString(name, SysStringLen(name)));
    if (!foundFrame)
        return S_OK;

    WebFrame* foundWebFrame = kit(foundFrame);
    if (!foundWebFrame)
        return E_FAIL;

    return foundWebFrame->QueryInterface(IID_IWebFrame, (void**)frame);
}

HRESULT STDMETHODCALLTYPE WebFrame::parentFrame( 
    /* [retval][out] */ IWebFrame** frame)
{
    HRESULT hr = S_OK;
    *frame = 0;
    if (Frame* coreFrame = core(this))
        if (WebFrame* webFrame = kit(coreFrame->tree()->parent()))
            hr = webFrame->QueryInterface(IID_IWebFrame, (void**) frame);

    return hr;
}

class EnumChildFrames : public IEnumVARIANT
{
public:
    EnumChildFrames(Frame* f) : m_refCount(1), m_frame(f), m_curChild(f ? f->tree()->firstChild() : 0) { }

    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject)
    {
        *ppvObject = 0;
        if (IsEqualGUID(riid, IID_IUnknown) || IsEqualGUID(riid, IID_IEnumVARIANT))
            *ppvObject = this;
        else
            return E_NOINTERFACE;

        AddRef();
        return S_OK;
    }

    virtual ULONG STDMETHODCALLTYPE AddRef(void)
    {
        return ++m_refCount;
    }

    virtual ULONG STDMETHODCALLTYPE Release(void)
    {
        ULONG newRef = --m_refCount;
        if (!newRef)
            delete(this);
        return newRef;
    }

    virtual HRESULT STDMETHODCALLTYPE Next(ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched)
    {
        if (pCeltFetched)
            *pCeltFetched = 0;
        if (!rgVar)
            return E_POINTER;
        VariantInit(rgVar);
        if (!celt || celt > 1)
            return S_FALSE;
        if (!m_frame || !m_curChild)
            return S_FALSE;

        WebFrame* webFrame = kit(m_curChild);
        IUnknown* unknown;
        HRESULT hr = webFrame->QueryInterface(IID_IUnknown, (void**)&unknown);
        if (FAILED(hr))
            return hr;

        V_VT(rgVar) = VT_UNKNOWN;
        V_UNKNOWN(rgVar) = unknown;

        m_curChild = m_curChild->tree()->nextSibling();
        if (pCeltFetched)
            *pCeltFetched = 1;
        return S_OK;
    }

    virtual HRESULT STDMETHODCALLTYPE Skip(ULONG celt)
    {
        if (!m_frame)
            return S_FALSE;
        for (unsigned i = 0; i < celt && m_curChild; i++)
            m_curChild = m_curChild->tree()->nextSibling();
        return m_curChild ? S_OK : S_FALSE;
    }

    virtual HRESULT STDMETHODCALLTYPE Reset(void)
    {
        if (!m_frame)
            return S_FALSE;
        m_curChild = m_frame->tree()->firstChild();
        return S_OK;
    }

    virtual HRESULT STDMETHODCALLTYPE Clone(IEnumVARIANT**)
    {
        return E_NOTIMPL;
    }

private:
    ULONG m_refCount;
    Frame* m_frame;
    Frame* m_curChild;
};

HRESULT STDMETHODCALLTYPE WebFrame::childFrames( 
    /* [retval][out] */ IEnumVARIANT **enumFrames)
{
    if (!enumFrames)
        return E_POINTER;

    *enumFrames = new EnumChildFrames(core(this));
    return S_OK;
}

// IWebFramePrivate ------------------------------------------------------

HRESULT STDMETHODCALLTYPE WebFrame::renderTreeAsExternalRepresentation(
    /* [retval][out] */ BSTR *result)
{
    if (!result)
        return E_POINTER;

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    *result = BString(externalRepresentation(coreFrame->renderer())).release();
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::scrollOffset(
        /* [retval][out] */ SIZE* offset)
{
    if (!offset) {
        ASSERT_NOT_REACHED();
        return E_POINTER;
    }

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    FrameView* view = coreFrame->view();
    if (!view)
        return E_FAIL;

    *offset = view->scrollOffset();
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::layout()
{
    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    FrameView* view = coreFrame->view();
    if (!view)
        return E_FAIL;

    view->layout();
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::firstLayoutDone(
    /* [retval][out] */ BOOL* result)
{
    if (!result) {
        ASSERT_NOT_REACHED();
        return E_POINTER;
    }

    *result = 0;

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    *result = coreFrame->loader()->firstLayoutDone();
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::loadType( 
    /* [retval][out] */ WebFrameLoadType* type)
{
    if (!type) {
        ASSERT_NOT_REACHED();
        return E_POINTER;
    }

    *type = (WebFrameLoadType)0;

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    *type = (WebFrameLoadType)coreFrame->loader()->loadType();
    return S_OK;
}

// IWebDocumentText -----------------------------------------------------------

HRESULT STDMETHODCALLTYPE WebFrame::supportsTextEncoding( 
    /* [retval][out] */ BOOL* result)
{
    *result = FALSE;
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebFrame::selectedString( 
    /* [retval][out] */ BSTR* result)
{
    *result = 0;

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    String text = coreFrame->selectedText();
    text.replace('\\', coreFrame->backslashAsCurrencySymbol());

    *result = BString(text).release();
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::selectAll()
{
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE WebFrame::deselectAll()
{
    return E_NOTIMPL;
}

// WebFrame ---------------------------------------------------------------

void WebFrame::initWithWebFrameView(IWebFrameView* /*view*/, IWebView* webView, Page* page, HTMLFrameOwnerElement* ownerElement)
{
    if (FAILED(webView->QueryInterface(&d->webView)))
        return;
    d->webView->Release(); // don't hold the extra ref

    HWND viewWindow;
    d->webView->viewWindow((OLE_HANDLE*)&viewWindow);

    this->AddRef(); // We release this ref in frameLoaderDestroyed()
    Frame* frame = new Frame(page, ownerElement, this);
    d->frame = frame;
}

Frame* WebFrame::impl()
{
    return d->frame;
}

void WebFrame::invalidate()
{
    Frame* coreFrame = core(this);
    ASSERT(coreFrame);

    if (Document* document = coreFrame->document())
        document->recalcStyle(Node::Force);
}

void WebFrame::setTextSizeMultiplier(float multiplier)
{
    int newZoomFactor = (int)round(multiplier * 100);
    Frame* coreFrame = core(this);
    ASSERT(coreFrame);

    if (coreFrame->zoomFactor() == newZoomFactor)
        return;

    coreFrame->setZoomFactor(newZoomFactor);
}

HRESULT WebFrame::inViewSourceMode(BOOL* flag)
{
    if (!flag) {
        ASSERT_NOT_REACHED();
        return E_POINTER;
    }

    *flag = FALSE;

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    *flag = coreFrame->inViewSourceMode() ? TRUE : FALSE;
    return S_OK;
}

HRESULT WebFrame::setInViewSourceMode(BOOL flag)
{
    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    coreFrame->setInViewSourceMode(!!flag);
    return S_OK;
}

HRESULT WebFrame::elementWithName(BSTR name, IDOMElement* form, IDOMElement** element)
{
    if (!form)
        return E_INVALIDARG;

    HTMLFormElement *formElement = formElementFromDOMElement(form);
    if (formElement) {
        Vector<HTMLGenericFormElement*>& elements = formElement->formElements;
        AtomicString targetName((UChar*)name, SysStringLen(name));
        for (unsigned int i = 0; i < elements.size(); i++) {
            HTMLGenericFormElement *elt = elements[i];
            // Skip option elements, other duds
            if (elt->name() == targetName) {
                *element = DOMElement::createInstance(elt);
                return S_OK;
            }
        }
    }
    return E_FAIL;
}

HRESULT WebFrame::formForElement(IDOMElement* element, IDOMElement** form)
{
    if (!element)
        return E_INVALIDARG;

    HTMLInputElement *inputElement = inputElementFromDOMElement(element);
    if (!inputElement)
        return E_FAIL;

    HTMLFormElement *formElement = inputElement->form();
    if (!formElement)
        return E_FAIL;

    *form = DOMElement::createInstance(formElement);
    return S_OK;
}

HRESULT WebFrame::elementDoesAutoComplete(IDOMElement *element, bool *result)
{
    *result = false;
    if (!element)
        return E_INVALIDARG;

    HTMLInputElement *inputElement = inputElementFromDOMElement(element);
    if (!inputElement)
        *result = false;
    else
        *result = (inputElement->inputType() == HTMLInputElement::TEXT && inputElement->autoComplete());

    return S_OK;
}

HRESULT WebFrame::controlsInForm(IDOMElement* form, IDOMElement** controls, int* cControls)
{
    if (!form)
        return E_INVALIDARG;

    HTMLFormElement *formElement = formElementFromDOMElement(form);
    if (!formElement)
        return E_FAIL;

    int inCount = *cControls;
    int count = (int) formElement->formElements.size();
    *cControls = count;
    if (!controls)
        return S_OK;
    if (inCount < count)
        return E_FAIL;

    *cControls = 0;
    Vector<HTMLGenericFormElement*>& elements = formElement->formElements;
    for (int i = 0; i < count; i++) {
        if (elements.at(i)->isEnumeratable()) { // Skip option elements, other duds
            controls[*cControls] = DOMElement::createInstance(elements.at(i));
            (*cControls)++;
        }
    }
    return S_OK;
}

HRESULT WebFrame::elementIsPassword(IDOMElement *element, bool *result)
{
    HTMLInputElement *inputElement = inputElementFromDOMElement(element);
    *result = inputElement != 0
        && inputElement->inputType() == HTMLInputElement::PASSWORD;
    return S_OK;
}

HRESULT WebFrame::searchForLabelsBeforeElement(const BSTR* labels, int cLabels, IDOMElement* beforeElement, BSTR* result)
{
    if (!result) {
        ASSERT_NOT_REACHED();
        return E_POINTER;
    }

    *result = 0;

    if (!cLabels)
        return S_OK;
    if (cLabels < 1)
        return E_INVALIDARG;

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    Vector<String> labelStrings(cLabels);
    for (int i=0; i<cLabels; i++)
        labelStrings[i] = String(labels[i], SysStringLen(labels[i]));
    Element *coreElement = elementFromDOMElement(beforeElement);
    if (!coreElement)
        return E_FAIL;

    String label = coreFrame->searchForLabelsBeforeElement(labelStrings, coreElement);
    
    *result = SysAllocStringLen(label.characters(), label.length());
    if (label.length() && !*result)
        return E_OUTOFMEMORY;
    return S_OK;
}

HRESULT WebFrame::matchLabelsAgainstElement(const BSTR* labels, int cLabels, IDOMElement* againstElement, BSTR* result)
{
    if (!result) {
        ASSERT_NOT_REACHED();
        return E_POINTER;
    }

    *result = 0;

    if (!cLabels)
        return S_OK;
    if (cLabels < 1)
        return E_INVALIDARG;

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    Vector<String> labelStrings(cLabels);
    for (int i=0; i<cLabels; i++)
        labelStrings[i] = String(labels[i], SysStringLen(labels[i]));
    Element *coreElement = elementFromDOMElement(againstElement);
    if (!coreElement)
        return E_FAIL;

    String label = coreFrame->matchLabelsAgainstElement(labelStrings, coreElement);
    
    *result = SysAllocStringLen(label.characters(), label.length());
    if (label.length() && !*result)
        return E_OUTOFMEMORY;
    return S_OK;
}

HRESULT WebFrame::canProvideDocumentSource(bool* result)
{
    HRESULT hr = S_OK;
    *result = false;

    COMPtr<IWebDataSource> dataSource;
    hr = WebFrame::dataSource(&dataSource);
    if (FAILED(hr))
        return hr;

    COMPtr<IWebURLResponse> urlResponse;
    hr = dataSource->response(&urlResponse);
    if (SUCCEEDED(hr) && urlResponse) {
        BSTR mimeTypeBStr;
        if (SUCCEEDED(urlResponse->MIMEType(&mimeTypeBStr))) {
            String mimeType(mimeTypeBStr, SysStringLen(mimeTypeBStr));
            *result = mimeType == "text/html" || WebCore::DOMImplementation::isXMLMIMEType(mimeType);
            SysFreeString(mimeTypeBStr);
        }
    }
    return hr;
}

void WebFrame::frameLoaderDestroyed()
{
    // The FrameLoader going away is equivalent to the Frame going away,
    // so we now need to clear our frame pointer.
    d->frame = 0;

    this->Release();
}

PassRefPtr<Frame> WebFrame::createFrame(const KURL& URL, const String& name, HTMLFrameOwnerElement* ownerElement, const String& referrer)
{
    Frame* coreFrame = core(this);
    ASSERT(coreFrame);

    COMPtr<WebFrame> webFrame;
    webFrame.adoptRef(WebFrame::createInstance());

    webFrame->initWithWebFrameView(0, d->webView, coreFrame->page(), ownerElement);

    RefPtr<Frame> childFrame(adoptRef(core(webFrame.get()))); // We have to adopt, because Frames start out with a refcount of 1.
    ASSERT(childFrame);

    coreFrame->tree()->appendChild(childFrame);
    childFrame->tree()->setName(name);
    childFrame->init();

    loadURLIntoChild(URL, referrer, webFrame.get());

    // The frame's onload handler may have removed it from the document.
    if (!childFrame->tree()->parent())
        return 0;

    return childFrame.release();
}

void WebFrame::loadURLIntoChild(const KURL& originalURL, const String& referrer, WebFrame* childFrame)
{
    ASSERT(childFrame);
    ASSERT(core(childFrame));

    Frame* coreFrame = core(this);
    ASSERT(coreFrame);

    HistoryItem* parentItem = coreFrame->loader()->currentHistoryItem();
    FrameLoadType loadType = coreFrame->loader()->loadType();
    FrameLoadType childLoadType = FrameLoadTypeRedirectWithLockedHistory;

    KURL url = originalURL;

    // If we're moving in the backforward list, we might want to replace the content
    // of this child frame with whatever was there at that point.
    // Reload will maintain the frame contents, LoadSame will not.
    if (parentItem && parentItem->children().size() &&
        (isBackForwardLoadType(loadType)
         || loadType == FrameLoadTypeReload
         || loadType == FrameLoadTypeReloadAllowingStaleData))
    {
        if (HistoryItem* childItem = parentItem->childItemWithName(core(childFrame)->tree()->name())) {
            // Use the original URL to ensure we get all the side-effects, such as
            // onLoad handlers, of any redirects that happened. An example of where
            // this is needed is Radar 3213556.
            url = childItem->originalURL();
            // These behaviors implied by these loadTypes should apply to the child frames
            childLoadType = loadType;

            if (isBackForwardLoadType(loadType))
                // For back/forward, remember this item so we can traverse any child items as child frames load
                core(childFrame)->loader()->setProvisionalHistoryItem(childItem);
            else
                // For reload, just reinstall the current item, since a new child frame was created but we won't be creating a new BF item
                core(childFrame)->loader()->setCurrentHistoryItem(childItem);
        }
    }

    // FIXME: Handle loading WebArchives here

    core(childFrame)->loader()->load(url, referrer, childLoadType, String(), 0, 0);
}

void WebFrame::dispatchDidHandleOnloadEvents()
{
    IWebFrameLoadDelegatePrivate* frameLoadDelegatePriv;
    if (SUCCEEDED(d->webView->frameLoadDelegatePrivate(&frameLoadDelegatePriv))  && frameLoadDelegatePriv) {
        frameLoadDelegatePriv->didHandleOnloadEventsForFrame(d->webView, this);
        frameLoadDelegatePriv->Release();
    }
}

WebHistory* WebFrame::webHistory()
{
    if (this != d->webView->topLevelFrame())
        return 0;

    IWebHistoryPrivate* historyInternal = WebHistory::optionalSharedHistoryInternal(); // does not add a ref
    if (!historyInternal)
        return 0;

    WebHistory* webHistory;
    if (FAILED(historyInternal->QueryInterface(&webHistory)))
        return 0;

    return webHistory;
}

bool WebFrame::hasFrameView() const
{
    return !!d->frameView();
}

void WebFrame::makeRepresentation(DocumentLoader*)
{
    notImplemented();
}

void WebFrame::forceLayout()
{
    core(this)->forceLayout(true);
}

void WebFrame::forceLayoutForNonHTML()
{
    notImplemented();
}

void WebFrame::setCopiesOnScroll()
{
    notImplemented();
}

void WebFrame::detachedFromParent1()
{
    notImplemented();
}

void WebFrame::detachedFromParent2()
{
    notImplemented();
}

void WebFrame::detachedFromParent3()
{
    notImplemented();
}

void WebFrame::detachedFromParent4()
{
    notImplemented();
}

void WebFrame::dispatchDidReceiveServerRedirectForProvisionalLoad()
{
    COMPtr<IWebFrameLoadDelegate> frameLoadDelegate;
    if (SUCCEEDED(d->webView->frameLoadDelegate(&frameLoadDelegate)))
        frameLoadDelegate->didReceiveServerRedirectForProvisionalLoadForFrame(d->webView, this);
}

void WebFrame::dispatchDidCancelClientRedirect()
{
    COMPtr<IWebFrameLoadDelegate> frameLoadDelegate;
    if (SUCCEEDED(d->webView->frameLoadDelegate(&frameLoadDelegate)))
        frameLoadDelegate->didCancelClientRedirectForFrame(d->webView, this);
}

void WebFrame::dispatchWillPerformClientRedirect(const KURL& url, double delay, double fireDate)
{
    COMPtr<IWebFrameLoadDelegate> frameLoadDelegate;
    if (SUCCEEDED(d->webView->frameLoadDelegate(&frameLoadDelegate)))
        frameLoadDelegate->willPerformClientRedirectToURL(d->webView, BString(url.string()), delay, MarshallingHelpers::CFAbsoluteTimeToDATE(fireDate), this);
}

void WebFrame::dispatchDidChangeLocationWithinPage()
{
    COMPtr<IWebFrameLoadDelegate> frameLoadDelegate;
    if (SUCCEEDED(d->webView->frameLoadDelegate(&frameLoadDelegate)))
        frameLoadDelegate->didChangeLocationWithinPageForFrame(d->webView, this);
}

void WebFrame::dispatchWillClose()
{
    COMPtr<IWebFrameLoadDelegate> frameLoadDelegate;
    if (SUCCEEDED(d->webView->frameLoadDelegate(&frameLoadDelegate)))
        frameLoadDelegate->willCloseFrame(d->webView, this);
}

void WebFrame::dispatchDidReceiveIcon()
{
    d->webView->dispatchDidReceiveIconFromWebFrame(this);
}

void WebFrame::dispatchDidStartProvisionalLoad()
{
    COMPtr<IWebFrameLoadDelegate> frameLoadDelegate;
    if (SUCCEEDED(d->webView->frameLoadDelegate(&frameLoadDelegate)))
        frameLoadDelegate->didStartProvisionalLoadForFrame(d->webView, this);
}

void WebFrame::dispatchDidReceiveTitle(const String& title)
{
    COMPtr<IWebFrameLoadDelegate> frameLoadDelegate;
    if (SUCCEEDED(d->webView->frameLoadDelegate(&frameLoadDelegate)))
        frameLoadDelegate->didReceiveTitle(d->webView, BString(title), this);
}

void WebFrame::dispatchDidCommitLoad()
{
    COMPtr<IWebFrameLoadDelegate> frameLoadDelegate;
    if (SUCCEEDED(d->webView->frameLoadDelegate(&frameLoadDelegate))) 
        frameLoadDelegate->didCommitLoadForFrame(d->webView, this);
}

void WebFrame::dispatchDidFinishDocumentLoad()
{
    COMPtr<IWebFrameLoadDelegatePrivate> frameLoadDelegatePriv;
    if (SUCCEEDED(d->webView->frameLoadDelegatePrivate(&frameLoadDelegatePriv)) && frameLoadDelegatePriv)
        frameLoadDelegatePriv->didFinishDocumentLoadForFrame(d->webView, this);
}

void WebFrame::dispatchDidFinishLoad()
{
    COMPtr<IWebFrameLoadDelegate> frameLoadDelegate;
    if (SUCCEEDED(d->webView->frameLoadDelegate(&frameLoadDelegate))) 
        frameLoadDelegate->didFinishLoadForFrame(d->webView, this);
}

void WebFrame::dispatchDidFirstLayout()
{
    COMPtr<IWebFrameLoadDelegatePrivate> frameLoadDelegatePriv;
    if (SUCCEEDED(d->webView->frameLoadDelegatePrivate(&frameLoadDelegatePriv)) && frameLoadDelegatePriv)
        frameLoadDelegatePriv->didFirstLayoutInFrame(d->webView, this);
}

void WebFrame::dispatchShow()
{
    COMPtr<IWebUIDelegate> ui;

    if (SUCCEEDED(d->webView->uiDelegate(&ui)))
        ui->webViewShow(d->webView);
}

void WebFrame::cancelPolicyCheck()
{
    if (d->m_policyListener) {
        d->m_policyListener->invalidate();
        d->m_policyListener = 0;
    }

    d->m_policyFunction = 0;
}

void WebFrame::dispatchWillSubmitForm(FramePolicyFunction function, PassRefPtr<FormState> formState)
{
    Frame* coreFrame = core(this);
    ASSERT(coreFrame);

    COMPtr<IWebFormDelegate> formDelegate;

    if (FAILED(d->webView->formDelegate(&formDelegate))) {
        (coreFrame->loader()->*function)(PolicyUse);
        return;
    }

    COMPtr<IDOMElement> formElement(AdoptCOM, DOMElement::createInstance(formState->form()));

    // FIXME: The FormValuesPropertyBag constructor should take a const pointer
    FormValuesPropertyBag formValuesPropBag(const_cast<HashMap<String, String>*>(&formState->values()));

    COMPtr<WebFrame> sourceFrame(kit(formState->sourceFrame()));
    if (SUCCEEDED(formDelegate->willSubmitForm(this, sourceFrame.get(), formElement.get(), &formValuesPropBag, setUpPolicyListener(function).get())))
        return;

    // FIXME: Add a sane default implementation
    (coreFrame->loader()->*function)(PolicyUse);
}

void WebFrame::dispatchDidLoadMainResource(DocumentLoader* loader)
{
    if (WebScriptDebugServer::listenerCount() > 0) {
        Frame* coreFrame = core(this);
        if (!coreFrame)
            return;

        WebScriptDebugServer::sharedWebScriptDebugServer()->didLoadMainResourceForDataSource(
            kit(coreFrame->page()),
            loader ? static_cast<WebDocumentLoader*>(loader)->dataSource() : 0);
    }
}

void WebFrame::revertToProvisionalState(DocumentLoader*)
{
    notImplemented();
}

void WebFrame::clearUnarchivingState(DocumentLoader*)
{
    notImplemented();
}

void WebFrame::setMainFrameDocumentReady(bool)
{
    notImplemented();
}

void WebFrame::willChangeTitle(DocumentLoader*)
{
    notImplemented();
}

void WebFrame::didChangeTitle(DocumentLoader*)
{
    notImplemented();
}

void WebFrame::finishedLoading(DocumentLoader* loader)
{
    // Telling the frame we received some data and passing 0 as the data is our
    // way to get work done that is normally done when the first bit of data is
    // received, even for the case of a document with no data (like about:blank)
    if (!d->m_pluginView)
        committedLoad(loader, 0, 0);
    else {
        if (d->m_pluginView->status() == PluginStatusLoadedSuccessfully)
            d->m_pluginView->didFinishLoading();
        d->m_pluginView = 0;
        d->m_hasSentResponseToPlugin = false;
    }
}

void WebFrame::finalSetupForReplace(DocumentLoader*)
{
    notImplemented();
}

void WebFrame::setDefersLoading(bool)
{
    notImplemented();
}

bool WebFrame::isArchiveLoadPending(ResourceLoader*) const
{
    notImplemented();
    return false;
}

void WebFrame::cancelPendingArchiveLoad(ResourceLoader*)
{
    notImplemented();
}

void WebFrame::clearArchivedResources()
{
    notImplemented();
}

bool WebFrame::canHandleRequest(const ResourceRequest& request) const
{
    return WebView::canHandleRequest(request);
}

bool WebFrame::canShowMIMEType(const String& /*MIMEType*/) const
{
    notImplemented();
    return true;
}

bool WebFrame::representationExistsForURLScheme(const String& /*URLScheme*/) const
{
    notImplemented();
    return false;
}

String WebFrame::generatedMIMETypeForURLScheme(const String& /*URLScheme*/) const
{
    notImplemented();
    ASSERT_NOT_REACHED();
    return String();
}

void WebFrame::frameLoadCompleted()
{
    if (Frame* coreFrame = core(this))
        coreFrame->loader()->setPreviousHistoryItem(0);
}

void WebFrame::restoreViewState()
{
}

void WebFrame::provisionalLoadStarted()
{
    notImplemented();
}

bool WebFrame::shouldTreatURLAsSameAsCurrent(const KURL&) const
{
    notImplemented();
    return false;
}

void WebFrame::addHistoryItemForFragmentScroll()
{
    notImplemented();
}

void WebFrame::didFinishLoad()
{
    notImplemented();
}

void WebFrame::prepareForDataSourceReplacement()
{
    notImplemented();
}

void WebFrame::setTitle(const String& title, const KURL& url)
{
    BOOL privateBrowsingEnabled = FALSE;
    COMPtr<IWebPreferences> preferences;
    if (SUCCEEDED(d->webView->preferences(&preferences)))
        preferences->privateBrowsingEnabled(&privateBrowsingEnabled);
    if (!privateBrowsingEnabled) {
        // update title in global history
        COMPtr<WebHistory> history;
        history.adoptRef(webHistory());
        if (history) {
            COMPtr<IWebHistoryItem> item;
            if (SUCCEEDED(history->itemForURL(BString(url.string()), &item))) {
                COMPtr<IWebHistoryItemPrivate> itemPrivate;
                if (SUCCEEDED(item->QueryInterface(IID_IWebHistoryItemPrivate, (void**)&itemPrivate)))
                    itemPrivate->setTitle(BString(title));
            }
        }
    }
}

String WebFrame::userAgent(const KURL& url)
{
    return d->webView->userAgentForKURL(url);
}

void WebFrame::savePlatformDataToCachedPage(CachedPage* cachedPage)
{
    Frame* coreFrame = core(this);
    if (!coreFrame)
        return;

    ASSERT(coreFrame->loader()->documentLoader() == cachedPage->documentLoader());

    WebCachedPagePlatformData* webPlatformData = new WebCachedPagePlatformData(static_cast<IWebDataSource*>(getWebDataSource(coreFrame->loader()->documentLoader())));
    cachedPage->setCachedPagePlatformData(webPlatformData);
}

void WebFrame::transitionToCommittedFromCachedPage(CachedPage*)
{
}

void WebFrame::transitionToCommittedForNewPage()
{
    Frame* frame = core(this);
    ASSERT(frame);

    Page* page = frame->page();
    ASSERT(page);

    bool isMainFrame = frame == page->mainFrame();

    if (isMainFrame && frame->view())
        frame->view()->detachFromWindow();

    frame->setView(0);

    FrameView* frameView;
    if (isMainFrame) {
        RECT rect;
        d->webView->frameRect(&rect);
        frameView = new FrameView(frame, IntRect(rect).size());
    } else
        frameView = new FrameView(frame);

    frame->setView(frameView);
    frameView->deref(); // FrameViews are created with a ref count of 1. Release this ref since we've assigned it to frame.

    HWND viewWindow;
    if (SUCCEEDED(d->webView->viewWindow(reinterpret_cast<OLE_HANDLE*>(&viewWindow))))
        frameView->setContainingWindow(viewWindow);

    if (isMainFrame)
        frameView->attachToWindow();

    if (frame->ownerRenderer())
        frame->ownerRenderer()->setWidget(frameView);

    if (HTMLFrameOwnerElement* owner = frame->ownerElement())
        frame->view()->setScrollbarsMode(owner->scrollingMode());
}

void WebFrame::updateGlobalHistory(const KURL& url)
{
    COMPtr<WebHistory> history;
    history.adoptRef(webHistory());
    if (!history)
        return;
    history->addItemForURL(BString(url.string()), 0);                 
}

bool WebFrame::shouldGoToHistoryItem(HistoryItem*) const
{
    return true;
}

void WebFrame::saveViewStateToItem(HistoryItem*)
{
}

bool WebFrame::canCachePage() const
{
    return true;
}

void WebFrame::setMainDocumentError(DocumentLoader*, const ResourceError& error)
{
    if (d->m_pluginView) {
        if (d->m_pluginView->status() == PluginStatusLoadedSuccessfully)
            d->m_pluginView->didFail(error);
        d->m_pluginView = 0;
        d->m_hasSentResponseToPlugin = false;
    }
}

ResourceError WebFrame::cancelledError(const ResourceRequest& request)
{
    // FIXME: Need ChickenCat to include CFNetwork/CFURLError.h to get these values
    // Alternatively, we could create our own error domain/codes.
    return ResourceError(String(WebURLErrorDomain), -999, request.url().string(), String());
}

ResourceError WebFrame::blockedError(const ResourceRequest& request)
{
    // FIXME: Need to implement the String descriptions for errors in the WebKitErrorDomain and have them localized
    return ResourceError(String(WebKitErrorDomain), WebKitErrorCannotUseRestrictedPort, request.url().string(), String());
}

ResourceError WebFrame::cannotShowURLError(const ResourceRequest&)
{
    notImplemented();
    return ResourceError();
}

ResourceError WebFrame::interruptForPolicyChangeError(const ResourceRequest& request)
{
    // FIXME: Need to implement the String descriptions for errors in the WebKitErrorDomain and have them localized
    return ResourceError(String(WebKitErrorDomain), WebKitErrorFrameLoadInterruptedByPolicyChange, request.url().string(), String());
}

ResourceError WebFrame::cannotShowMIMETypeError(const ResourceResponse&)
{
    notImplemented();
    return ResourceError();
}

ResourceError WebFrame::fileDoesNotExistError(const ResourceResponse&)
{
    notImplemented();
    return ResourceError();
}

bool WebFrame::shouldFallBack(const ResourceError& error)
{
    return error.errorCode() != WebURLErrorCancelled;
}

void WebFrame::receivedData(const char* data, int length, const String& textEncoding)
{
    Frame* coreFrame = core(this);
    if (!coreFrame)
        return;

    // Set the encoding. This only needs to be done once, but it's harmless to do it again later.
    String encoding = coreFrame->loader()->documentLoader()->overrideEncoding();
    bool userChosen = !encoding.isNull();
    if (encoding.isNull())
        encoding = textEncoding;
    coreFrame->loader()->setEncoding(encoding, userChosen);

    coreFrame->loader()->addData(data, length);
}

COMPtr<WebFramePolicyListener> WebFrame::setUpPolicyListener(WebCore::FramePolicyFunction function)
{
    // FIXME: <rdar://5634381> We need to support multiple active policy listeners.

    if (d->m_policyListener)
        d->m_policyListener->invalidate();

    Frame* coreFrame = core(this);
    ASSERT(coreFrame);

    d->m_policyListener.adoptRef(WebFramePolicyListener::createInstance(coreFrame));
    d->m_policyFunction = function;

    return d->m_policyListener;
}

void WebFrame::receivedPolicyDecision(PolicyAction action)
{
    ASSERT(d->m_policyListener);
    ASSERT(d->m_policyFunction);

    FramePolicyFunction function = d->m_policyFunction;

    d->m_policyListener = 0;
    d->m_policyFunction = 0;

    Frame* coreFrame = core(this);
    ASSERT(coreFrame);

    (coreFrame->loader()->*function)(action);
}

void WebFrame::committedLoad(DocumentLoader* loader, const char* data, int length)
{
    // FIXME: This should probably go through the data source.
    const String& textEncoding = loader->response().textEncodingName();

    if (!d->m_pluginView)
        receivedData(data, length, textEncoding);

    if (d->m_pluginView && d->m_pluginView->status() == PluginStatusLoadedSuccessfully) {
        if (!d->m_hasSentResponseToPlugin) {
            d->m_pluginView->didReceiveResponse(d->frame->loader()->documentLoader()->response());
            // didReceiveResponse sets up a new stream to the plug-in. on a full-page plug-in, a failure in
            // setting up this stream can cause the main document load to be cancelled, setting m_pluginView
            // to null
            if (!d->m_pluginView)
                return;
            d->m_hasSentResponseToPlugin = true;
        }
        d->m_pluginView->didReceiveData(data, length);
    }
}

void WebFrame::dispatchDecidePolicyForMIMEType(FramePolicyFunction function, const String& mimeType, const ResourceRequest& request)
{
    Frame* coreFrame = core(this);
    ASSERT(coreFrame);

    COMPtr<IWebPolicyDelegate> policyDelegate;
    if (FAILED(d->webView->policyDelegate(&policyDelegate)))
        policyDelegate = DefaultPolicyDelegate::sharedInstance();

    COMPtr<IWebURLRequest> urlRequest(AdoptCOM, WebMutableURLRequest::createInstance(request));

    if (SUCCEEDED(policyDelegate->decidePolicyForMIMEType(d->webView, BString(mimeType), urlRequest.get(), this, setUpPolicyListener(function).get())))
        return;

    (coreFrame->loader()->*function)(PolicyUse);
}

void WebFrame::dispatchDecidePolicyForNewWindowAction(FramePolicyFunction function, const NavigationAction& action, const ResourceRequest& request, const String& frameName)
{
    Frame* coreFrame = core(this);
    ASSERT(coreFrame);

    COMPtr<IWebPolicyDelegate> policyDelegate;
    if (FAILED(d->webView->policyDelegate(&policyDelegate)))
        policyDelegate = DefaultPolicyDelegate::sharedInstance();

    COMPtr<IWebURLRequest> urlRequest(AdoptCOM, WebMutableURLRequest::createInstance(request));
    COMPtr<WebActionPropertyBag> actionInformation(AdoptCOM, WebActionPropertyBag::createInstance(action, coreFrame));

    if (SUCCEEDED(policyDelegate->decidePolicyForNewWindowAction(d->webView, actionInformation.get(), urlRequest.get(), BString(frameName), setUpPolicyListener(function).get())))
        return;

    (coreFrame->loader()->*function)(PolicyUse);
}

void WebFrame::dispatchDecidePolicyForNavigationAction(FramePolicyFunction function, const NavigationAction& action, const ResourceRequest& request)
{
    Frame* coreFrame = core(this);
    ASSERT(coreFrame);

    COMPtr<IWebPolicyDelegate> policyDelegate;
    if (FAILED(d->webView->policyDelegate(&policyDelegate)))
        policyDelegate = DefaultPolicyDelegate::sharedInstance();

    COMPtr<IWebURLRequest> urlRequest(AdoptCOM, WebMutableURLRequest::createInstance(request));
    COMPtr<WebActionPropertyBag> actionInformation(AdoptCOM, WebActionPropertyBag::createInstance(action, coreFrame));

    if (SUCCEEDED(policyDelegate->decidePolicyForNavigationAction(d->webView, actionInformation.get(), urlRequest.get(), this, setUpPolicyListener(function).get())))
        return;

    (coreFrame->loader()->*function)(PolicyUse);
}

void WebFrame::dispatchUnableToImplementPolicy(const ResourceError& error)
{
    COMPtr<IWebPolicyDelegate> policyDelegate;
    if (FAILED(d->webView->policyDelegate(&policyDelegate)))
        policyDelegate = DefaultPolicyDelegate::sharedInstance();

    COMPtr<IWebError> webError(AdoptCOM, WebError::createInstance(error));
    policyDelegate->unableToImplementPolicyWithError(d->webView, webError.get(), this);
}

void WebFrame::download(ResourceHandle* handle, const ResourceRequest& request, const ResourceRequest&, const ResourceResponse& response)
{
    COMPtr<IWebDownloadDelegate> downloadDelegate;
    COMPtr<IWebView> webView;
    if (SUCCEEDED(this->webView(&webView))) {
        if (FAILED(webView->downloadDelegate(&downloadDelegate))) {
            // If the WebView doesn't successfully provide a download delegate we'll pass a null one
            // into the WebDownload - which may or may not decide to use a DefaultDownloadDelegate
            LOG_ERROR("Failed to get downloadDelegate from WebView");
            downloadDelegate = 0;
        }
    }

    // Its the delegate's job to ref the WebDownload to keep it alive - otherwise it will be destroyed
    // when this method returns
    COMPtr<WebDownload> download;
    download.adoptRef(WebDownload::createInstance(handle, request, response, downloadDelegate.get()));
}

bool WebFrame::willUseArchive(ResourceLoader*, const ResourceRequest&, const KURL&) const
{
    notImplemented();
    return false;
}

void WebFrame::assignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request)
{
    COMPtr<IWebResourceLoadDelegate> resourceLoadDelegate;
    if (SUCCEEDED(d->webView->resourceLoadDelegate(&resourceLoadDelegate))) {
        COMPtr<IWebURLRequest> webURLRequest;
        webURLRequest.adoptRef(WebMutableURLRequest::createInstance(request));

        resourceLoadDelegate->identifierForInitialRequest(d->webView, webURLRequest.get(), getWebDataSource(loader), identifier);
    }
}

void WebFrame::dispatchWillSendRequest(DocumentLoader* loader, unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse)
{
    COMPtr<IWebResourceLoadDelegate> resourceLoadDelegate;
    if (SUCCEEDED(d->webView->resourceLoadDelegate(&resourceLoadDelegate))) {
        COMPtr<IWebURLRequest> webURLRequest;
        webURLRequest.adoptRef(WebMutableURLRequest::createInstance(request));
        COMPtr<IWebURLResponse> webURLRedirectResponse;
        webURLRedirectResponse.adoptRef(WebURLResponse::createInstance(redirectResponse));
        COMPtr<IWebURLRequest> newWebURLRequest;

        if (FAILED(resourceLoadDelegate->willSendRequest(d->webView, identifier, webURLRequest.get(), webURLRedirectResponse.get(), getWebDataSource(loader), &newWebURLRequest)))
            return;

        if (webURLRequest == newWebURLRequest)
            return;

        COMPtr<WebMutableURLRequest> newWebURLRequestImpl;
        if (FAILED(newWebURLRequest->QueryInterface(&newWebURLRequestImpl)))
            return;

        request = newWebURLRequestImpl->resourceRequest();
    }
}

void WebFrame::dispatchDidReceiveResponse(DocumentLoader* loader, unsigned long identifier, const ResourceResponse& response)
{
    COMPtr<IWebResourceLoadDelegate> resourceLoadDelegate;
    if (SUCCEEDED(d->webView->resourceLoadDelegate(&resourceLoadDelegate))) {
        COMPtr<IWebURLResponse> webURLResponse;
        webURLResponse.adoptRef(WebURLResponse::createInstance(response));

        resourceLoadDelegate->didReceiveResponse(d->webView, identifier, webURLResponse.get(), getWebDataSource(loader));
    }
}

void WebFrame::dispatchDidReceiveContentLength(DocumentLoader* loader, unsigned long identifier, int length)
{
    COMPtr<IWebResourceLoadDelegate> resourceLoadDelegate;
    if (SUCCEEDED(d->webView->resourceLoadDelegate(&resourceLoadDelegate)))
        resourceLoadDelegate->didReceiveContentLength(d->webView, identifier, length, getWebDataSource(loader));
}

void WebFrame::dispatchDidFinishLoading(DocumentLoader* loader, unsigned long identifier)
{
    COMPtr<IWebResourceLoadDelegate> resourceLoadDelegate;
    if (SUCCEEDED(d->webView->resourceLoadDelegate(&resourceLoadDelegate)))
        resourceLoadDelegate->didFinishLoadingFromDataSource(d->webView, identifier, getWebDataSource(loader));
}

void WebFrame::dispatchDidFailLoading(DocumentLoader* loader, unsigned long identifier, const ResourceError& error)
{
    COMPtr<IWebResourceLoadDelegate> resourceLoadDelegate;
    if (SUCCEEDED(d->webView->resourceLoadDelegate(&resourceLoadDelegate))) {
        COMPtr<IWebError> webError;
        webError.adoptRef(WebError::createInstance(error));
        resourceLoadDelegate->didFailLoadingWithError(d->webView, identifier, webError.get(), getWebDataSource(loader));
    }
}

bool WebFrame::dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int /*length*/)
{
    notImplemented();
    return false;
}

void WebFrame::dispatchDidFailProvisionalLoad(const ResourceError& error)
{
    COMPtr<IWebFrameLoadDelegate> frameLoadDelegate;
    if (SUCCEEDED(d->webView->frameLoadDelegate(&frameLoadDelegate))) {
        COMPtr<IWebError> webError;
        webError.adoptRef(WebError::createInstance(error));
        frameLoadDelegate->didFailProvisionalLoadWithError(d->webView, webError.get(), this);
    }
}

void WebFrame::dispatchDidFailLoad(const ResourceError& error)
{
    COMPtr<IWebFrameLoadDelegate> frameLoadDelegate;
    if (SUCCEEDED(d->webView->frameLoadDelegate(&frameLoadDelegate))) {
        COMPtr<IWebError> webError;
        webError.adoptRef(WebError::createInstance(error));
        frameLoadDelegate->didFailLoadWithError(d->webView, webError.get(), this);
    }
}

Frame* WebFrame::dispatchCreatePage()
{
    COMPtr<IWebUIDelegate> ui;

    if (SUCCEEDED(d->webView->uiDelegate(&ui))) {
        COMPtr<IWebView> newWebView;

        if (SUCCEEDED(ui->createWebViewWithRequest(d->webView, 0, &newWebView))) {
            COMPtr<IWebFrame> mainFrame;

            if (SUCCEEDED(newWebView->mainFrame(&mainFrame))) {
                COMPtr<WebFrame> mainFrameImpl;

                if (SUCCEEDED(mainFrame->QueryInterface(IID_WebFrame, (void**)&mainFrameImpl)))
                    return core(mainFrameImpl.get());
            }
        }
    }
    return 0;
}

void WebFrame::postProgressStartedNotification()
{
    static BSTR progressStartedName = SysAllocString(WebViewProgressStartedNotification);
    IWebNotificationCenter* notifyCenter = WebNotificationCenter::defaultCenterInternal();
    notifyCenter->postNotificationName(progressStartedName, static_cast<IWebView*>(d->webView), 0);
}

void WebFrame::postProgressEstimateChangedNotification()
{
    static BSTR progressEstimateChangedName = SysAllocString(WebViewProgressEstimateChangedNotification);
    IWebNotificationCenter* notifyCenter = WebNotificationCenter::defaultCenterInternal();
    notifyCenter->postNotificationName(progressEstimateChangedName, static_cast<IWebView*>(d->webView), 0);
}

void WebFrame::postProgressFinishedNotification()
{
    static BSTR progressFinishedName = SysAllocString(WebViewProgressFinishedNotification);
    IWebNotificationCenter* notifyCenter = WebNotificationCenter::defaultCenterInternal();
    notifyCenter->postNotificationName(progressFinishedName, static_cast<IWebView*>(d->webView), 0);
}

void WebFrame::startDownload(const ResourceRequest&)
{
    notImplemented();
}

void WebFrame::dispatchDidReceiveAuthenticationChallenge(DocumentLoader* loader, unsigned long identifier, const AuthenticationChallenge& challenge)
{
    ASSERT(challenge.sourceHandle());

    COMPtr<IWebResourceLoadDelegate> resourceLoadDelegate;
    if (SUCCEEDED(d->webView->resourceLoadDelegate(&resourceLoadDelegate))) {
        COMPtr<IWebURLAuthenticationChallenge> webChallenge(AdoptCOM, WebURLAuthenticationChallenge::createInstance(challenge));

        if (SUCCEEDED(resourceLoadDelegate->didReceiveAuthenticationChallenge(d->webView, identifier, webChallenge.get(), getWebDataSource(loader))))
            return;
    }

    // If the ResourceLoadDelegate doesn't exist or fails to handle the call, we tell the ResourceHandle
    // to continue without credential - this is the best approximation of Mac behavior
    challenge.sourceHandle()->receivedRequestToContinueWithoutCredential(challenge);
}

void WebFrame::dispatchDidCancelAuthenticationChallenge(DocumentLoader* loader, unsigned long identifier, const AuthenticationChallenge& challenge)
{
    COMPtr<IWebResourceLoadDelegate> resourceLoadDelegate;
    if (SUCCEEDED(d->webView->resourceLoadDelegate(&resourceLoadDelegate))) {
        COMPtr<IWebURLAuthenticationChallenge> webChallenge(AdoptCOM, WebURLAuthenticationChallenge::createInstance(challenge));

        if (SUCCEEDED(resourceLoadDelegate->didCancelAuthenticationChallenge(d->webView, identifier, webChallenge.get(), getWebDataSource(loader))))
            return;
    }
}

PassRefPtr<Frame> WebFrame::createFrame(const KURL& url, const String& name, HTMLFrameOwnerElement* ownerElement,
                            const String& referrer, bool /*allowsScrolling*/, int /*marginWidth*/, int /*marginHeight*/)
{
    RefPtr<Frame> result = createFrame(url, name, ownerElement, referrer);
    if (!result)
        return 0;

    // Propagate the marginwidth/height and scrolling modes to the view.
    if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) {
        HTMLFrameElement* frameElt = static_cast<HTMLFrameElement*>(ownerElement);
        if (frameElt->scrollingMode() == ScrollbarAlwaysOff)
            result->view()->setScrollbarsMode(ScrollbarAlwaysOff);
        int marginWidth = frameElt->getMarginWidth();
        int marginHeight = frameElt->getMarginHeight();
        if (marginWidth != -1)
            result->view()->setMarginWidth(marginWidth);
        if (marginHeight != -1)
            result->view()->setMarginHeight(marginHeight);
    }

    return result.release();
}

Widget* WebFrame::createPlugin(const IntSize& pluginSize, Element* element, const KURL& url, const Vector<String>& paramNames, const Vector<String>& paramValues, const String& mimeType, bool loadManually)
{
    PluginView* pluginView = PluginView::create(core(this), pluginSize, element, url, paramNames, paramValues, mimeType, loadManually);

    if (pluginView->status() == PluginStatusLoadedSuccessfully)
        return pluginView;

    COMPtr<IWebResourceLoadDelegate> resourceLoadDelegate;

    if (FAILED(d->webView->resourceLoadDelegate(&resourceLoadDelegate)))
        return pluginView;

    RetainPtr<CFMutableDictionaryRef> userInfo(AdoptCF, CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));

    unsigned count = (unsigned)paramNames.size();
    for (unsigned i = 0; i < count; i++) {
        if (paramNames[i] == "pluginspage") {
            static CFStringRef key = MarshallingHelpers::LPCOLESTRToCFStringRef(WebKitErrorPlugInPageURLStringKey);
            RetainPtr<CFStringRef> str(AdoptCF, paramValues[i].createCFString());
            CFDictionarySetValue(userInfo.get(), key, str.get());
            break;
        }
    }

    if (!mimeType.isNull()) {
        static CFStringRef key = MarshallingHelpers::LPCOLESTRToCFStringRef(WebKitErrorMIMETypeKey);

        RetainPtr<CFStringRef> str(AdoptCF, mimeType.createCFString());
        CFDictionarySetValue(userInfo.get(), key, str.get());
    }

    String pluginName;
    if (pluginView->plugin())
        pluginName = pluginView->plugin()->name();
    if (!pluginName.isNull()) {
        static CFStringRef key = MarshallingHelpers::LPCOLESTRToCFStringRef(WebKitErrorPlugInNameKey);
        RetainPtr<CFStringRef> str(AdoptCF, pluginName.createCFString());
        CFDictionarySetValue(userInfo.get(), key, str.get());
    }

    COMPtr<CFDictionaryPropertyBag> userInfoBag(AdoptCOM, CFDictionaryPropertyBag::createInstance());
    userInfoBag->setDictionary(userInfo.get());
 
    int errorCode = 0;
    switch (pluginView->status()) {
        case PluginStatusCanNotFindPlugin:
            errorCode = WebKitErrorCannotFindPlugIn;
            break;
        case PluginStatusCanNotLoadPlugin:
            errorCode = WebKitErrorCannotLoadPlugIn;
            break;
        default:
            ASSERT_NOT_REACHED();
    }

    ResourceError resourceError(String(WebKitErrorDomain), errorCode, url.string(), String());
    COMPtr<IWebError> error(AdoptCOM, WebError::createInstance(resourceError, userInfoBag.get()));
     
    resourceLoadDelegate->plugInFailedWithError(d->webView, error.get(), getWebDataSource(d->frame->loader()->documentLoader()));

    return pluginView;
}

void WebFrame::redirectDataToPlugin(Widget* pluginWidget)
{
    // Ideally, this function shouldn't be necessary, see <rdar://problem/4852889>

    d->m_pluginView = static_cast<PluginView*>(pluginWidget);
}

Widget* WebFrame::createJavaAppletWidget(const IntSize& pluginSize, Element* element, const KURL& /*baseURL*/, const Vector<String>& paramNames, const Vector<String>& paramValues)
{
    PluginView* pluginView = PluginView::create(core(this), pluginSize, element, KURL(), paramNames, paramValues, "application/x-java-applet", false);

    // Check if the plugin can be loaded successfully
    if (pluginView->plugin() && pluginView->plugin()->load())
        return pluginView;

    COMPtr<IWebResourceLoadDelegate> resourceLoadDelegate;
    if (FAILED(d->webView->resourceLoadDelegate(&resourceLoadDelegate)))
        return pluginView;

    COMPtr<CFDictionaryPropertyBag> userInfoBag(AdoptCOM, CFDictionaryPropertyBag::createInstance());

    ResourceError resourceError(String(WebKitErrorDomain), WebKitErrorJavaUnavailable, String(), String());
    COMPtr<IWebError> error(AdoptCOM, WebError::createInstance(resourceError, userInfoBag.get()));
     
    resourceLoadDelegate->plugInFailedWithError(d->webView, error.get(), getWebDataSource(d->frame->loader()->documentLoader()));

    return pluginView;
}

ObjectContentType WebFrame::objectContentType(const KURL& url, const String& mimeTypeIn)
{
    String mimeType = mimeTypeIn;
    if (mimeType.isEmpty())
        mimeType = MIMETypeRegistry::getMIMETypeForExtension(url.path().substring(url.path().reverseFind('.') + 1));

    if (mimeType.isEmpty())
        return ObjectContentFrame; // Go ahead and hope that we can display the content.

    if (MIMETypeRegistry::isSupportedImageMIMEType(mimeType))
        return WebCore::ObjectContentImage;

    if (PluginDatabase::installedPlugins()->isMIMETypeRegistered(mimeType))
        return WebCore::ObjectContentNetscapePlugin;

    if (MIMETypeRegistry::isSupportedNonImageMIMEType(mimeType))
        return WebCore::ObjectContentFrame;

    return WebCore::ObjectContentNone;
}

String WebFrame::overrideMediaType() const
{
    notImplemented();
    return String();
}

void WebFrame::windowObjectCleared()
{
    Frame* coreFrame = core(this);
    ASSERT(coreFrame);

    Settings* settings = coreFrame->settings();
    if (!settings || !settings->isJavaScriptEnabled())
        return;

    COMPtr<IWebFrameLoadDelegate> frameLoadDelegate;
    if (SUCCEEDED(d->webView->frameLoadDelegate(&frameLoadDelegate))) {
        COMPtr<IWebFrameLoadDelegate2> frameLoadDelegate2(Query, frameLoadDelegate);

        JSContextRef context = toRef(coreFrame->scriptProxy()->globalObject()->globalExec());
        JSObjectRef windowObject = toRef(KJS::Window::retrieve(coreFrame)->getObject());
        ASSERT(windowObject);

        if (!frameLoadDelegate2 || 
            FAILED(frameLoadDelegate2->didClearWindowObject(d->webView, context, windowObject, this)))
            frameLoadDelegate->windowScriptObjectAvailable(d->webView, context, windowObject);
    }

    if (WebScriptDebugServer::listenerCount() > 0) {
        detachScriptDebugger();
        attachScriptDebugger();
    }
}

void WebFrame::didPerformFirstNavigation() const
{
    COMPtr<IWebPreferences> preferences;
    if (FAILED(d->webView->preferences(&preferences)))
        return;

    COMPtr<IWebPreferencesPrivate> preferencesPrivate(Query, preferences);
    if (!preferencesPrivate)
        return;
    BOOL automaticallyDetectsCacheModel;
    if (FAILED(preferencesPrivate->automaticallyDetectsCacheModel(&automaticallyDetectsCacheModel)))
        return;

    WebCacheModel cacheModel;
    if (FAILED(preferences->cacheModel(&cacheModel)))
        return;

    if (automaticallyDetectsCacheModel && cacheModel < WebCacheModelDocumentBrowser)
        preferences->setCacheModel(WebCacheModelDocumentBrowser);
}

void WebFrame::registerForIconNotification(bool listen)
{
    d->webView->registerForIconNotification(listen);
}

static IntRect printerRect(HDC printDC)
{
    return IntRect(0, 0, 
                   GetDeviceCaps(printDC, PHYSICALWIDTH)  - 2 * GetDeviceCaps(printDC, PHYSICALOFFSETX),
                   GetDeviceCaps(printDC, PHYSICALHEIGHT) - 2 * GetDeviceCaps(printDC, PHYSICALOFFSETY));
}

void WebFrame::setPrinting(bool printing, float minPageWidth, float maxPageWidth, bool adjustViewSize)
{
    Frame* coreFrame = core(this);
    ASSERT(coreFrame);
    coreFrame->setPrinting(printing, minPageWidth, maxPageWidth, adjustViewSize);
}

HRESULT STDMETHODCALLTYPE WebFrame::setInPrintingMode( 
    /* [in] */ BOOL value,
    /* [in] */ HDC printDC)
{
    if (m_inPrintingMode == !!value)
        return S_OK;

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    m_inPrintingMode = !!value;

    // If we are a frameset just print with the layout we have onscreen, otherwise relayout
    // according to the paper size
    float minLayoutWidth = 0.0f;
    float maxLayoutWidth = 0.0f;
    if (m_inPrintingMode && !coreFrame->isFrameSet()) {
        if (!printDC) {
            ASSERT_NOT_REACHED();
            return E_POINTER;
        }

        const int desiredHorizontalPixelsPerInch = 72;
        int paperHorizontalPixelsPerInch = ::GetDeviceCaps(printDC, LOGPIXELSX);
        int paperWidth = printerRect(printDC).width() * desiredHorizontalPixelsPerInch / paperHorizontalPixelsPerInch;
        minLayoutWidth = paperWidth * PrintingMinimumShrinkFactor;
        maxLayoutWidth = paperWidth * PrintingMaximumShrinkFactor;
    }

    setPrinting(m_inPrintingMode, minLayoutWidth, maxLayoutWidth, true);

    if (!m_inPrintingMode)
        m_pageRects.clear();

    return S_OK;
}

void WebFrame::headerAndFooterHeights(float* headerHeight, float* footerHeight)
{
    if (headerHeight)
        *headerHeight = 0;
    if (footerHeight)
        *footerHeight = 0;
    float height = 0;
    COMPtr<IWebUIDelegate> ui;
    if (FAILED(d->webView->uiDelegate(&ui)))
        return;
    COMPtr<IWebUIDelegate2> ui2;
    if (FAILED(ui->QueryInterface(IID_IWebUIDelegate2, (void**) &ui2)))
        return;
    if (headerHeight && SUCCEEDED(ui2->webViewHeaderHeight(d->webView, &height)))
        *headerHeight = height;
    if (footerHeight && SUCCEEDED(ui2->webViewFooterHeight(d->webView, &height)))
        *footerHeight = height;
}

IntRect WebFrame::printerMarginRect(HDC printDC)
{
    IntRect emptyRect(0, 0, 0, 0);

    COMPtr<IWebUIDelegate> ui;
    if (FAILED(d->webView->uiDelegate(&ui)))
        return emptyRect;
    COMPtr<IWebUIDelegate2> ui2;
    if (FAILED(ui->QueryInterface(IID_IWebUIDelegate2, (void**) &ui2)))
        return emptyRect;

    RECT rect;
    if (FAILED(ui2->webViewPrintingMarginRect(d->webView, &rect)))
        return emptyRect;

    rect.left = MulDiv(rect.left, ::GetDeviceCaps(printDC, LOGPIXELSX), 1000);
    rect.top = MulDiv(rect.top, ::GetDeviceCaps(printDC, LOGPIXELSY), 1000);
    rect.right = MulDiv(rect.right, ::GetDeviceCaps(printDC, LOGPIXELSX), 1000);
    rect.bottom = MulDiv(rect.bottom, ::GetDeviceCaps(printDC, LOGPIXELSY), 1000);

    return IntRect(rect.left, rect.top, (rect.right - rect.left), rect.bottom - rect.top);
}

const Vector<WebCore::IntRect>& WebFrame::computePageRects(HDC printDC)
{
    ASSERT(m_inPrintingMode);
    
    Frame* coreFrame = core(this);
    ASSERT(coreFrame);
    ASSERT(coreFrame->document());

    if (!printDC)
        return m_pageRects;

    // adjust the page rect by the header and footer
    float headerHeight = 0, footerHeight = 0;
    headerAndFooterHeights(&headerHeight, &footerHeight);
    IntRect pageRect = printerRect(printDC);
    IntRect marginRect = printerMarginRect(printDC);
    IntRect adjustedRect = IntRect(
        pageRect.x() + marginRect.x(),
        pageRect.y() + marginRect.y(),
        pageRect.width() - marginRect.x() - marginRect.right(),
        pageRect.height() - marginRect.y() - marginRect.bottom());

    computePageRectsForFrame(coreFrame, adjustedRect, headerHeight, footerHeight, 1.0,m_pageRects, m_pageHeight);
    
    return m_pageRects;
}

HRESULT STDMETHODCALLTYPE WebFrame::getPrintedPageCount( 
    /* [in] */ HDC printDC,
    /* [retval][out] */ UINT *pageCount)
{
    if (!pageCount || !printDC) {
        ASSERT_NOT_REACHED();
        return E_POINTER;
    }

    *pageCount = 0;

    if (!m_inPrintingMode) {
        ASSERT_NOT_REACHED();
        return E_FAIL;
    }

    Frame* coreFrame = core(this);
    if (!coreFrame || !coreFrame->document())
        return E_FAIL;

    const Vector<IntRect>& pages = computePageRects(printDC);
    *pageCount = (UINT) pages.size();
    
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::spoolPages( 
    /* [in] */ HDC printDC,
    /* [in] */ UINT startPage,
    /* [in] */ UINT endPage,
    /* [retval][out] */ void* ctx)
{
    if (!printDC || !ctx) {
        ASSERT_NOT_REACHED();
        return E_POINTER;
    }

    if (!m_inPrintingMode) {
        ASSERT_NOT_REACHED();
        return E_FAIL;
    }

    Frame* coreFrame = core(this);
    if (!coreFrame || !coreFrame->document())
        return E_FAIL;

    UINT pageCount = (UINT) m_pageRects.size();
    PlatformGraphicsContext* pctx = (PlatformGraphicsContext*)ctx;

    if (!pageCount || startPage > pageCount) {
        ASSERT_NOT_REACHED();
        return E_FAIL;
    }

    if (startPage > 0)
        startPage--;

    if (endPage == 0)
        endPage = pageCount;

    COMPtr<IWebUIDelegate> ui;
    if (FAILED(d->webView->uiDelegate(&ui)))
        return E_FAIL;
    // FIXME: we can return early after the updated app is released
    COMPtr<IWebUIDelegate2> ui2;
    if (FAILED(ui->QueryInterface(IID_IWebUIDelegate2, (void**) &ui2)))
        ui2 = 0;

    float headerHeight = 0, footerHeight = 0;
    headerAndFooterHeights(&headerHeight, &footerHeight);
    GraphicsContext spoolCtx(pctx);

    for (UINT ii = startPage; ii < endPage; ii++) {
        IntRect pageRect = m_pageRects[ii];

        CGContextSaveGState(pctx);

        IntRect printRect = printerRect(printDC);
        CGRect mediaBox = CGRectMake(CGFloat(0),
                                     CGFloat(0),
                                     CGFloat(printRect.width()),
                                     CGFloat(printRect.height()));

        CGContextBeginPage(pctx, &mediaBox);

        CGFloat scale = (float)mediaBox.size.width/ (float)pageRect.width();
        CGAffineTransform ctm = CGContextGetBaseCTM(pctx);
        ctm = CGAffineTransformScale(ctm, -scale, -scale);
        ctm = CGAffineTransformTranslate(ctm, CGFloat(-pageRect.x()), CGFloat(-pageRect.y()+headerHeight)); // reserves space for header
        CGContextScaleCTM(pctx, scale, scale);
        CGContextTranslateCTM(pctx, CGFloat(-pageRect.x()), CGFloat(-pageRect.y()+headerHeight));   // reserves space for header
        CGContextSetBaseCTM(pctx, ctm);

        coreFrame->paint(&spoolCtx, pageRect);

        if (ui2) {
            CGContextTranslateCTM(pctx, CGFloat(pageRect.x()), CGFloat(pageRect.y())-headerHeight);

            int x = pageRect.x();
            int y = 0;
            if (headerHeight) {
                RECT headerRect = {x, y, x+pageRect.width(), y+(int)headerHeight};
                ui2->drawHeaderInRect(d->webView, &headerRect, (OLE_HANDLE)(LONG64)pctx);
            }

            if (footerHeight) {
                y = max((int)headerHeight+pageRect.height(), m_pageHeight-(int)footerHeight);
                RECT footerRect = {x, y, x+pageRect.width(), y+(int)footerHeight};
                ui2->drawFooterInRect(d->webView, &footerRect, (OLE_HANDLE)(LONG64)pctx, ii+1, pageCount);
            }
        }

        CGContextEndPage(pctx);
        CGContextRestoreGState(pctx);
    }
 
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::isFrameSet( 
    /* [retval][out] */ BOOL* result)
{
    *result = FALSE;

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    *result = coreFrame->isFrameSet() ? TRUE : FALSE;
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::string( 
    /* [retval][out] */ BSTR *result)
{
    *result = 0;

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    RefPtr<Range> allRange(rangeOfContents(coreFrame->document()));
    String allString = plainText(allRange.get());
    *result = BString(allString).release();
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::size( 
    /* [retval][out] */ SIZE *size)
{
    if (!size)
        return E_POINTER;
    size->cx = size->cy = 0;

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;
    FrameView* view = coreFrame->view();
    if (!view)
        return E_FAIL;
    size->cx = view->width();
    size->cy = view->height();
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::hasScrollBars( 
    /* [retval][out] */ BOOL *result)
{
    if (!result)
        return E_POINTER;
    *result = FALSE;

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    FrameView* view = coreFrame->view();
    if (!view)
        return E_FAIL;

    if (view->vScrollbarMode() == ScrollbarAlwaysOn || view->visibleHeight() < view->contentsHeight() ||
            view->hScrollbarMode() == ScrollbarAlwaysOn || view->visibleWidth() < view->contentsWidth())
        *result = TRUE;

    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::contentBounds( 
    /* [retval][out] */ RECT *result)
{
    if (!result)
        return E_POINTER;
    ::SetRectEmpty(result);

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    FrameView* view = coreFrame->view();
    if (!view)
        return E_FAIL;

    result->bottom = view->contentsHeight();
    result->right = view->contentsWidth();
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::frameBounds( 
    /* [retval][out] */ RECT *result)
{
    if (!result)
        return E_POINTER;
    ::SetRectEmpty(result);

    Frame* coreFrame = core(this);
    if (!coreFrame)
        return E_FAIL;

    FrameView* view = coreFrame->view();
    if (!view)
        return E_FAIL;

    FloatRect bounds = view->visibleContentRectConsideringExternalScrollers();
    result->bottom = (LONG) bounds.height();
    result->right = (LONG) bounds.width();
    return S_OK;
}

HRESULT STDMETHODCALLTYPE WebFrame::isDescendantOfFrame( 
    /* [in] */ IWebFrame *ancestor,
    /* [retval][out] */ BOOL *result)
{
    if (!result)
        return E_POINTER;
    *result = FALSE;

    Frame* coreFrame = core(this);
    COMPtr<WebFrame> ancestorWebFrame;
    if (!ancestor || FAILED(ancestor->QueryInterface(IID_WebFrame, (void**)&ancestorWebFrame)))
        return S_OK;

    *result = (coreFrame && coreFrame->tree()->isDescendantOf(core(ancestorWebFrame.get()))) ? TRUE : FALSE;
    return S_OK;
}

void WebFrame::unmarkAllMisspellings()
{
    Frame* coreFrame = core(this);
    for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) {
        Document *doc = frame->document();
        if (!doc)
            return;

        doc->removeMarkers(DocumentMarker::Spelling);
    }
}

void WebFrame::unmarkAllBadGrammar()
{
    Frame* coreFrame = core(this);
    for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) {
        Document *doc = frame->document();
        if (!doc)
            return;

        doc->removeMarkers(DocumentMarker::Grammar);
    }
}

WebView* WebFrame::webView() const
{
    return d->webView;
}
