blob: 6efae8f6229849ca1f596b7355ceef43cfd690a8 [file] [log] [blame]
/*
* Copyright (C) 2006-2009, 2011, 2013-2015 Apple Inc. All rights reserved.
* Copyright (C) Research In Motion Limited 2009. 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 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 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 "WebKitDLL.h"
#include "WebFrame.h"
#include "CFDictionaryPropertyBag.h"
#include "COMPropertyBag.h"
#include "DOMCoreClasses.h"
#include "MarshallingHelpers.h"
#include "PluginDatabase.h"
#include "PluginView.h"
#include "WebActionPropertyBag.h"
#include "WebChromeClient.h"
#include "WebDataSource.h"
#include "WebDocumentLoader.h"
#include "WebDownload.h"
#include "WebEditorClient.h"
#include "WebError.h"
#include "WebFrameLoaderClient.h"
#include "WebFramePolicyListener.h"
#include "WebHistory.h"
#include "WebHistoryItem.h"
#include "WebKit.h"
#include "WebKitStatisticsPrivate.h"
#include "WebMutableURLRequest.h"
#include "WebNotificationCenter.h"
#include "WebScriptWorld.h"
#include "WebURLResponse.h"
#include "WebView.h"
#include <JavaScriptCore/APICast.h>
#include <JavaScriptCore/HeapInlines.h>
#include <JavaScriptCore/JSCJSValue.h>
#include <JavaScriptCore/JSLock.h>
#include <JavaScriptCore/JSObject.h>
#include <WebCore/BString.h>
#include <WebCore/COMPtr.h>
#include <WebCore/CSSAnimationController.h>
#include <WebCore/DOMWindow.h>
#include <WebCore/Document.h>
#include <WebCore/DocumentLoader.h>
#include <WebCore/DocumentMarkerController.h>
#include <WebCore/Editor.h>
#include <WebCore/Event.h>
#include <WebCore/EventHandler.h>
#include <WebCore/FormState.h>
#include <WebCore/Frame.h>
#include <WebCore/FrameLoadRequest.h>
#include <WebCore/FrameLoader.h>
#include <WebCore/FrameTree.h>
#include <WebCore/FrameView.h>
#include <WebCore/FrameWin.h>
#include <WebCore/GDIObjectCounter.h>
#include <WebCore/GraphicsContext.h>
#include <WebCore/HTMLAppletElement.h>
#include <WebCore/HTMLFormControlElement.h>
#include <WebCore/HTMLFormElement.h>
#include <WebCore/HTMLFrameOwnerElement.h>
#include <WebCore/HTMLInputElement.h>
#include <WebCore/HTMLNames.h>
#include <WebCore/HTMLPlugInElement.h>
#include <WebCore/HistoryItem.h>
#include <WebCore/JSDOMBinding.h>
#include <WebCore/JSDOMWindow.h>
#include <WebCore/KeyboardEvent.h>
#include <WebCore/MIMETypeRegistry.h>
#include <WebCore/MemoryCache.h>
#include <WebCore/MouseRelatedEvent.h>
#include <WebCore/NotImplemented.h>
#include <WebCore/Page.h>
#include <WebCore/PlatformKeyboardEvent.h>
#include <WebCore/PluginData.h>
#include <WebCore/PolicyChecker.h>
#include <WebCore/PrintContext.h>
#include <WebCore/RenderTreeAsText.h>
#include <WebCore/RenderView.h>
#include <WebCore/ResourceHandle.h>
#include <WebCore/ResourceLoader.h>
#include <WebCore/ResourceRequest.h>
#include <WebCore/ScriptController.h>
#include <WebCore/SecurityOrigin.h>
#include <WebCore/Settings.h>
#include <WebCore/TextIterator.h>
#include <wtf/MathExtras.h>
#if USE(CG)
#include <CoreGraphics/CoreGraphics.h>
#elif USE(CAIRO)
#include <WebCore/PlatformContextCairo.h>
#include <cairo-win32.h>
#endif
#if USE(CG)
// CG SPI used for printing
extern "C" {
CGAffineTransform CGContextGetBaseCTM(CGContextRef c);
void CGContextSetBaseCTM(CGContextRef c, CGAffineTransform m);
}
#endif
using namespace WebCore;
using namespace HTMLNames;
using JSC::JSGlobalObject;
using JSC::JSLock;
using JSC::JSValue;
#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;
//-----------------------------------------------------------------------------
// Helpers to convert from WebCore to WebKit type
WebFrame* kit(Frame* frame)
{
if (!frame)
return nullptr;
// FIXME: Doesn't this need to be aware of EmptyFrameLoaderClient?
FrameLoaderClient& frameLoaderClient = frame->loader().client();
return static_cast<WebFrameLoaderClient&>(frameLoaderClient).webFrame();
}
Frame* core(WebFrame* webFrame)
{
if (!webFrame)
return nullptr;
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 nullptr;
return const_cast<WebFrame*>(webFrame)->impl();
}
//-----------------------------------------------------------------------------
static Element* elementFromDOMElement(IDOMElement* element)
{
if (!element)
return nullptr;
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 nullptr;
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) && is<HTMLFormElement>(ele))
return downcast<HTMLFormElement>(ele);
}
return nullptr;
}
static HTMLInputElement* inputElementFromDOMElement(IDOMElement* element)
{
if (!element)
return nullptr;
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) && is<HTMLInputElement>(ele))
return downcast<HTMLInputElement>(ele);
}
return nullptr;
}
// WebFramePrivate ------------------------------------------------------------
class WebFrame::WebFramePrivate {
public:
WebFramePrivate()
{
}
~WebFramePrivate() { }
FrameView* frameView() { return frame ? frame->view() : nullptr; }
Frame* frame { nullptr };
WebView* webView { nullptr };
};
// WebFrame ----------------------------------------------------------------
WebFrame::WebFrame()
: d(new WebFrame::WebFramePrivate)
{
WebFrameCount++;
gClassCount++;
gClassNameCount().add("WebFrame");
}
WebFrame::~WebFrame()
{
delete d;
WebFrameCount--;
gClassCount--;
gClassNameCount().remove("WebFrame");
}
WebFrame* WebFrame::createInstance()
{
WebFrame* instance = new WebFrame();
instance->AddRef();
return instance;
}
HRESULT WebFrame::setAllowsScrolling(BOOL flag)
{
if (Frame* frame = core(this))
if (FrameView* view = frame->view())
view->setCanHaveScrollbars(!!flag);
return S_OK;
}
HRESULT WebFrame::allowsScrolling(_Out_ BOOL* flag)
{
if (!flag)
return E_POINTER;
*flag = FALSE;
if (Frame* frame = core(this)) {
if (FrameView* view = frame->view())
*flag = view->canHaveScrollbars();
}
return S_OK;
}
HRESULT WebFrame::setIsDisconnected(BOOL flag)
{
ASSERT_NOT_REACHED();
return E_FAIL;
}
HRESULT WebFrame::setExcludeFromTextSearch(BOOL flag)
{
ASSERT_NOT_REACHED();
return E_NOTIMPL;
}
HRESULT WebFrame::reloadFromOrigin()
{
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
coreFrame->loader().reload(WebCore::ReloadOption::FromOrigin);
return S_OK;
}
HRESULT WebFrame::paintDocumentRectToContext(RECT rect, _In_ HDC deviceContext)
{
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
FrameView* view = coreFrame->view();
if (!view)
return E_FAIL;
// We can't paint with a layout still pending.
view->updateLayoutAndStyleIfNeededRecursive();
GraphicsContext gc(deviceContext);
gc.setShouldIncludeChildWindows(true);
gc.save();
LONG width = rect.right - rect.left;
LONG height = rect.bottom - rect.top;
FloatRect dirtyRect;
dirtyRect.setWidth(width);
dirtyRect.setHeight(height);
gc.clip(dirtyRect);
gc.translate(-rect.left, -rect.top);
float scaleFactor = webView()->deviceScaleFactor();
gc.scale(WebCore::FloatSize(scaleFactor, scaleFactor));
view->paintContents(gc, rect);
gc.restore();
return S_OK;
}
HRESULT WebFrame::paintScrollViewRectToContextAtPoint(RECT rect, POINT pt, _In_ HDC deviceContext)
{
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
FrameView* view = coreFrame->view();
if (!view)
return E_FAIL;
// We can't paint with a layout still pending.
view->updateLayoutAndStyleIfNeededRecursive();
GraphicsContext gc(deviceContext);
gc.setShouldIncludeChildWindows(true);
gc.save();
IntRect dirtyRect(rect);
dirtyRect.move(-pt.x, -pt.y);
float scaleFactor = webView()->deviceScaleFactor();
gc.scale(WebCore::FloatSize(scaleFactor, scaleFactor));
view->paint(gc, dirtyRect);
gc.restore();
return S_OK;
}
// IUnknown -------------------------------------------------------------------
HRESULT WebFrame::QueryInterface(_In_ REFIID riid, _COM_Outptr_ void** ppvObject)
{
if (!ppvObject)
return E_POINTER;
*ppvObject = nullptr;
if (IsEqualGUID(riid, __uuidof(WebFrame)))
*ppvObject = this;
else if (IsEqualGUID(riid, IID_IUnknown))
*ppvObject = static_cast<IWebFrame2*>(this);
else if (IsEqualGUID(riid, IID_IWebFrame))
*ppvObject = static_cast<IWebFrame2*>(this);
else if (IsEqualGUID(riid, IID_IWebFrame2))
*ppvObject = static_cast<IWebFrame2*>(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 WebFrame::AddRef()
{
return ++m_refCount;
}
ULONG WebFrame::Release()
{
ULONG newRef = --m_refCount;
if (!newRef)
delete(this);
return newRef;
}
// IWebFrame -------------------------------------------------------------------
HRESULT WebFrame::name(_Deref_opt_out_ BSTR* frameName)
{
if (!frameName) {
ASSERT_NOT_REACHED();
return E_POINTER;
}
*frameName = nullptr;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
*frameName = BString(coreFrame->tree().uniqueName()).release();
return S_OK;
}
HRESULT WebFrame::webView(_COM_Outptr_opt_ IWebView** view)
{
if (!view)
return E_POINTER;
*view = nullptr;
if (!d->webView)
return E_FAIL;
*view = d->webView;
(*view)->AddRef();
return S_OK;
}
HRESULT WebFrame::frameView(_COM_Outptr_opt_ IWebFrameView** view)
{
ASSERT_NOT_REACHED();
if (!view)
return E_POINTER;
*view = nullptr;
return E_NOTIMPL;
}
HRESULT WebFrame::DOMDocument(_COM_Outptr_opt_ IDOMDocument** result)
{
if (!result) {
ASSERT_NOT_REACHED();
return E_POINTER;
}
*result = nullptr;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
if (Document* document = coreFrame->document())
*result = DOMDocument::createInstance(document);
return *result ? S_OK : E_FAIL;
}
HRESULT WebFrame::DOMWindow(_COM_Outptr_opt_ IDOMWindow** window)
{
if (!window) {
ASSERT_NOT_REACHED();
return E_POINTER;
}
*window = nullptr;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
if (WebCore::DOMWindow* coreWindow = coreFrame->document()->domWindow())
*window = ::DOMWindow::createInstance(coreWindow);
return *window ? S_OK : E_FAIL;
}
HRESULT WebFrame::frameElement(_COM_Outptr_opt_ IDOMHTMLElement** frameElement)
{
if (!frameElement)
return E_POINTER;
*frameElement = nullptr;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
COMPtr<IDOMElement> domElement(AdoptCOM, DOMElement::createInstance(coreFrame->ownerElement()));
COMPtr<IDOMHTMLElement> htmlElement(Query, domElement);
if (!htmlElement)
return E_FAIL;
return htmlElement.copyRefTo(frameElement);
}
HRESULT WebFrame::currentForm(_COM_Outptr_opt_ IDOMElement** currentForm)
{
if (!currentForm) {
ASSERT_NOT_REACHED();
return E_POINTER;
}
*currentForm = nullptr;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
if (HTMLFormElement* formElement = coreFrame->selection().currentForm())
*currentForm = DOMElement::createInstance(formElement);
return *currentForm ? S_OK : E_FAIL;
}
JSGlobalContextRef WebFrame::globalContext()
{
Frame* coreFrame = core(this);
if (!coreFrame)
return nullptr;
return toGlobalRef(coreFrame->script().globalObject(mainThreadNormalWorld()));
}
JSGlobalContextRef WebFrame::globalContextForScriptWorld(IWebScriptWorld* iWorld)
{
Frame* coreFrame = core(this);
if (!coreFrame)
return 0;
COMPtr<WebScriptWorld> world(Query, iWorld);
if (!world)
return 0;
return toGlobalRef(coreFrame->script().globalObject(world->world()));
}
HRESULT WebFrame::loadRequest(_In_opt_ IWebURLRequest* request)
{
if (!request)
return S_OK;
COMPtr<WebMutableURLRequest> requestImpl;
HRESULT hr = request->QueryInterface(&requestImpl);
if (FAILED(hr))
return hr;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
coreFrame->loader().load(FrameLoadRequest(*coreFrame, requestImpl->resourceRequest(), ShouldOpenExternalURLsPolicy::ShouldNotAllow));
return S_OK;
}
void WebFrame::loadData(Ref<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));
// FIXME: We should really be using MarshallingHelpers::BSTRToKURL here,
// but that would turn a null BSTR into a null URL, and we crash inside of
// WebCore if we use a null URL in constructing the ResourceRequest.
URL baseCoreURL = URL(URL(), String(baseURL ? baseURL : L"", SysStringLen(baseURL)));
URL failingCoreURL = MarshallingHelpers::BSTRToKURL(failingURL);
ResourceRequest request(baseCoreURL);
ResourceResponse response(URL(), mimeTypeString, data->size(), encodingString);
SubstituteData substituteData(WTFMove(data), failingCoreURL, response, SubstituteData::SessionHistoryVisibility::Hidden);
// 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(FrameLoadRequest(*coreFrame, request, ShouldOpenExternalURLsPolicy::ShouldNotAllow, substituteData));
}
HRESULT WebFrame::loadData(_In_opt_ IStream* data, _In_ BSTR mimeType, _In_ BSTR textEncodingName, _In_ BSTR url)
{
auto sharedBuffer = SharedBuffer::create();
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(WTFMove(sharedBuffer), mimeType, textEncodingName, url, nullptr);
return S_OK;
}
HRESULT WebFrame::loadPlainTextString(_In_ BSTR plainText, _In_ BSTR url)
{
auto sharedBuffer = SharedBuffer::create(reinterpret_cast<char*>(plainText), sizeof(UChar) * SysStringLen(plainText));
BString plainTextMimeType(TEXT("text/plain"), 10);
BString utf16Encoding(TEXT("utf-16"), 6);
loadData(WTFMove(sharedBuffer), plainTextMimeType, utf16Encoding, url, nullptr);
return S_OK;
}
void WebFrame::loadHTMLString(_In_ BSTR htmlString, _In_ BSTR baseURL, _In_ BSTR unreachableURL)
{
auto sharedBuffer = SharedBuffer::create(reinterpret_cast<char*>(htmlString), sizeof(UChar) * SysStringLen(htmlString));
BString utf16Encoding(TEXT("utf-16"), 6);
loadData(WTFMove(sharedBuffer), 0, utf16Encoding, baseURL, unreachableURL);
}
HRESULT WebFrame::loadHTMLString(_In_ BSTR htmlString, _In_ BSTR baseURL)
{
loadHTMLString(htmlString, baseURL, nullptr);
return S_OK;
}
HRESULT WebFrame::loadAlternateHTMLString(_In_ BSTR str, _In_ BSTR baseURL, _In_ BSTR unreachableURL)
{
loadHTMLString(str, baseURL, unreachableURL);
return S_OK;
}
HRESULT WebFrame::loadArchive(_In_opt_ IWebArchive* /*archive*/)
{
ASSERT_NOT_REACHED();
return E_NOTIMPL;
}
static inline WebDataSource* getWebDataSource(DocumentLoader* loader)
{
return loader ? static_cast<WebDocumentLoader*>(loader)->dataSource() : nullptr;
}
HRESULT WebFrame::dataSource(_COM_Outptr_opt_ IWebDataSource** source)
{
if (!source) {
ASSERT_NOT_REACHED();
return E_POINTER;
}
*source = nullptr;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
WebDataSource* webDataSource = getWebDataSource(coreFrame->loader().documentLoader());
*source = webDataSource;
if (webDataSource)
webDataSource->AddRef();
return *source ? S_OK : E_FAIL;
}
HRESULT WebFrame::provisionalDataSource(_COM_Outptr_opt_ IWebDataSource** source)
{
if (!source) {
ASSERT_NOT_REACHED();
return E_POINTER;
}
*source = nullptr;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
WebDataSource* webDataSource = getWebDataSource(coreFrame->loader().provisionalDocumentLoader());
*source = webDataSource;
if (webDataSource)
webDataSource->AddRef();
return *source ? S_OK : E_FAIL;
}
URL WebFrame::url() const
{
Frame* coreFrame = core(this);
if (!coreFrame)
return URL();
return coreFrame->document()->url();
}
HRESULT WebFrame::stopLoading()
{
if (Frame* coreFrame = core(this))
coreFrame->loader().stopAllLoaders();
return S_OK;
}
HRESULT WebFrame::reload()
{
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
coreFrame->loader().reload();
return S_OK;
}
HRESULT WebFrame::findFrameNamed(_In_ BSTR name, _COM_Outptr_opt_ IWebFrame** frame)
{
if (!frame) {
ASSERT_NOT_REACHED();
return E_POINTER;
}
*frame = nullptr;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
Frame* foundFrame = coreFrame->tree().find(AtomString(name, SysStringLen(name)), *coreFrame);
if (!foundFrame)
return S_OK;
WebFrame* foundWebFrame = kit(foundFrame);
if (!foundWebFrame)
return E_FAIL;
return foundWebFrame->QueryInterface(IID_IWebFrame, (void**)frame);
}
HRESULT WebFrame::parentFrame(_COM_Outptr_opt_ IWebFrame** frame)
{
if (!frame)
return E_POINTER;
*frame = nullptr;
HRESULT hr = S_OK;
if (Frame* coreFrame = core(this))
if (WebFrame* webFrame = kit(coreFrame->tree().parent()))
hr = webFrame->QueryInterface(IID_IWebFrame, (void**) frame);
return hr;
}
class EnumChildFrames final : public IEnumVARIANT
{
public:
EnumChildFrames(Frame* f)
: m_frame(f), m_curChild(f ? f->tree().firstChild() : nullptr)
{
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(_In_ REFIID riid, _COM_Outptr_ void** ppvObject)
{
*ppvObject = nullptr;
if (IsEqualGUID(riid, IID_IUnknown) || IsEqualGUID(riid, IID_IEnumVARIANT))
*ppvObject = this;
else
return E_NOINTERFACE;
AddRef();
return S_OK;
}
virtual ULONG STDMETHODCALLTYPE AddRef()
{
return ++m_refCount;
}
virtual ULONG STDMETHODCALLTYPE Release()
{
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 { 1 };
Frame* m_frame;
Frame* m_curChild;
};
HRESULT WebFrame::childFrames(_COM_Outptr_opt_ IEnumVARIANT** enumFrames)
{
if (!enumFrames)
return E_POINTER;
*enumFrames = new EnumChildFrames(core(this));
return S_OK;
}
// IWebFramePrivate ------------------------------------------------------
enum WebRenderTreeAsTextOption {
WebRenderTreeAsTextShowAllLayers = 1 << 0,
WebRenderTreeAsTextShowLayerNesting = 1 << 1,
WebRenderTreeAsTextShowCompositedLayers = 1 << 2,
WebRenderTreeAsTextShowOverflow = 1 << 3,
WebRenderTreeAsTextShowSVGGeometry = 1 << 4,
WebRenderTreeAsTextShowLayerFragments = 1 << 5
};
typedef unsigned WebRenderTreeAsTextOptions;
static OptionSet<RenderAsTextFlag> toRenderAsTextFlags(WebRenderTreeAsTextOptions options)
{
OptionSet<RenderAsTextFlag> flags;
if (options & WebRenderTreeAsTextShowAllLayers)
flags.add(RenderAsTextFlag::ShowAllLayers);
if (options & WebRenderTreeAsTextShowLayerNesting)
flags.add(RenderAsTextFlag::ShowLayerNesting);
if (options & WebRenderTreeAsTextShowCompositedLayers)
flags.add(RenderAsTextFlag::ShowCompositedLayers);
if (options & WebRenderTreeAsTextShowOverflow)
flags.add(RenderAsTextFlag::ShowOverflow);
if (options & WebRenderTreeAsTextShowSVGGeometry)
flags.add(RenderAsTextFlag::ShowSVGGeometry);
if (options & WebRenderTreeAsTextShowLayerFragments)
flags.add(RenderAsTextFlag::ShowLayerFragments);
return flags;
}
HRESULT WebFrame::renderTreeAsExternalRepresentation(unsigned options, _Deref_opt_out_ BSTR* result)
{
if (!result)
return E_POINTER;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
*result = BString(externalRepresentation(coreFrame, toRenderAsTextFlags(options))).release();
return S_OK;
}
HRESULT WebFrame::renderTreeAsExternalRepresentationForPrinting(_Deref_opt_out_ BSTR* result)
{
if (!result)
return E_POINTER;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
*result = BString(externalRepresentation(coreFrame, { RenderAsTextFlag::PrintingMode })).release();
return S_OK;
}
HRESULT WebFrame::pageNumberForElementById(_In_ BSTR id, float pageWidthInPixels, float pageHeightInPixels, _Out_ int* pageNumber)
{
// TODO: Please remove this function if not needed as this is LTC specific function
// and has been moved to Internals.
ASSERT_NOT_REACHED();
return E_NOTIMPL;
}
HRESULT WebFrame::numberOfPages(float pageWidthInPixels, float pageHeightInPixels, _Out_ int* pageCount)
{
// TODO: Please remove this function if not needed as this is LTC specific function
// and has been moved to Internals.
ASSERT_NOT_REACHED();
return E_NOTIMPL;
}
HRESULT WebFrame::scrollOffset(_Out_ SIZE* offset)
{
if (!offset) {
ASSERT_NOT_REACHED();
return E_POINTER;
}
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
FrameView* view = coreFrame->view();
if (!view)
return E_FAIL;
*offset = toIntSize(view->scrollPosition());
return S_OK;
}
HRESULT WebFrame::layout()
{
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
FrameView* view = coreFrame->view();
if (!view)
return E_FAIL;
view->layoutContext().layout();
return S_OK;
}
HRESULT WebFrame::firstLayoutDone(_Out_ BOOL* result)
{
if (!result) {
ASSERT_NOT_REACHED();
return E_POINTER;
}
*result = FALSE;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
*result = coreFrame->loader().stateMachine().firstLayoutDone();
return S_OK;
}
HRESULT WebFrame::pendingFrameUnloadEventCount(_Out_ UINT* result)
{
if (!result) {
ASSERT_NOT_REACHED();
return E_POINTER;
}
*result = 0;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
*result = coreFrame->document()->domWindow()->pendingUnloadEventListeners();
return S_OK;
}
HRESULT WebFrame::hasSpellingMarker(UINT from, UINT length, BOOL* result)
{
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
*result = coreFrame->editor().selectionStartHasMarkerFor(DocumentMarker::Spelling, from, length);
return S_OK;
}
HRESULT WebFrame::clearOpener()
{
HRESULT hr = S_OK;
if (Frame* coreFrame = core(this))
coreFrame->loader().setOpener(nullptr);
return hr;
}
HRESULT WebFrame::setTextDirection(_In_ BSTR direction)
{
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
String directionString(direction, SysStringLen(direction));
if (directionString == "auto")
coreFrame->editor().setBaseWritingDirection(WritingDirection::Natural);
else if (directionString == "ltr")
coreFrame->editor().setBaseWritingDirection(WritingDirection::LeftToRight);
else if (directionString == "rtl")
coreFrame->editor().setBaseWritingDirection(WritingDirection::RightToLeft);
return S_OK;
}
// IWebDocumentText -----------------------------------------------------------
HRESULT WebFrame::supportsTextEncoding(_Out_ BOOL* result)
{
*result = FALSE;
return E_NOTIMPL;
}
HRESULT WebFrame::selectedString(_Deref_opt_out_ BSTR* result)
{
if (!result)
return E_POINTER;
*result = nullptr;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
String text = coreFrame->displayStringModifiedByEncoding(coreFrame->editor().selectedText());
*result = BString(text).release();
return S_OK;
}
HRESULT WebFrame::selectAll()
{
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
if (!coreFrame->editor().command("SelectAll").execute())
return E_FAIL;
return S_OK;
}
HRESULT WebFrame::deselectAll()
{
return E_NOTIMPL;
}
// WebFrame ---------------------------------------------------------------
Ref<Frame> WebFrame::createSubframeWithOwnerElement(IWebView* webView, Page* page, HTMLFrameOwnerElement* ownerElement)
{
webView->QueryInterface(&d->webView);
d->webView->Release(); // don't hold the extra ref
HWND viewWindow;
d->webView->viewWindow(&viewWindow);
this->AddRef(); // We release this ref in frameLoaderDestroyed()
auto frame = Frame::create(page, ownerElement, new WebFrameLoaderClient(this));
d->frame = frame.ptr();
return frame;
}
void WebFrame::initWithWebView(IWebView* webView, Page* page)
{
webView->QueryInterface(&d->webView);
d->webView->Release(); // don't hold the extra ref
HWND viewWindow;
d->webView->viewWindow(&viewWindow);
this->AddRef(); // We release this ref in frameLoaderDestroyed()
d->frame = &page->mainFrame();
}
Frame* WebFrame::impl()
{
return d->frame;
}
void WebFrame::invalidate()
{
Frame* coreFrame = core(this);
ASSERT(coreFrame);
if (Document* document = coreFrame->document())
document->resolveStyle(WebCore::Document::ResolveStyleType::Rebuild);
}
HRESULT WebFrame::inViewSourceMode(BOOL* flag)
{
return E_NOTIMPL;
}
HRESULT WebFrame::setInViewSourceMode(BOOL flag)
{
return E_NOTIMPL;
}
HRESULT WebFrame::elementWithName(BSTR name, IDOMElement* form, IDOMElement** element)
{
if (!form)
return E_INVALIDARG;
HTMLFormElement* formElement = formElementFromDOMElement(form);
if (formElement) {
AtomString targetName((UChar*)name, SysStringLen(name));
for (auto& associatedElement : formElement->copyAssociatedElementsVector()) {
if (!is<HTMLFormControlElement>(associatedElement.get()))
continue;
auto& elt = downcast<HTMLFormControlElement>(associatedElement.get());
// 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(_In_opt_ IDOMElement *element, _Out_ BOOL* result)
{
*result = false;
if (!element)
return E_INVALIDARG;
HTMLInputElement *inputElement = inputElementFromDOMElement(element);
if (!inputElement)
*result = false;
else
*result = inputElement->isTextField() && !inputElement->isPasswordField() && inputElement->shouldAutocomplete();
return S_OK;
}
HRESULT WebFrame::resumeAnimations()
{
Frame* frame = core(this);
if (!frame)
return E_UNEXPECTED;
frame->animation().resumeAnimations();
return S_OK;
}
HRESULT WebFrame::suspendAnimations()
{
Frame* frame = core(this);
if (!frame)
return E_UNEXPECTED;
frame->animation().suspendAnimations();
return S_OK;
}
HRESULT WebFrame::pauseAnimation(_In_ BSTR animationName, _In_opt_ IDOMNode* node, double secondsFromNow, _Out_ BOOL* animationWasRunning)
{
if (!node || !animationWasRunning)
return E_POINTER;
*animationWasRunning = FALSE;
Frame* frame = core(this);
if (!frame)
return E_UNEXPECTED;
COMPtr<DOMNode> domNode(Query, node);
if (!domNode)
return E_FAIL;
*animationWasRunning = frame->animation().pauseAnimationAtTime(downcast<Element>(*domNode->node()), String(animationName, SysStringLen(animationName)), secondsFromNow);
return S_OK;
}
HRESULT WebFrame::pauseTransition(_In_ BSTR propertyName, _In_opt_ IDOMNode* node, double secondsFromNow, _Out_ BOOL* transitionWasRunning)
{
if (!node || !transitionWasRunning)
return E_POINTER;
*transitionWasRunning = FALSE;
Frame* frame = core(this);
if (!frame)
return E_UNEXPECTED;
COMPtr<DOMNode> domNode(Query, node);
if (!domNode)
return E_FAIL;
*transitionWasRunning = frame->animation().pauseTransitionAtTime(downcast<Element>(*domNode->node()), String(propertyName, SysStringLen(propertyName)), secondsFromNow);
return S_OK;
}
HRESULT WebFrame::visibleContentRect(_Out_ RECT* rect)
{
if (!rect)
return E_POINTER;
SetRectEmpty(rect);
Frame* frame = core(this);
if (!frame)
return E_UNEXPECTED;
FrameView* view = frame->view();
if (!view)
return E_FAIL;
*rect = view->visibleContentRect();
return S_OK;
}
HRESULT WebFrame::numberOfActiveAnimations(_Out_ UINT* number)
{
if (!number)
return E_POINTER;
*number = 0;
Frame* frame = core(this);
if (!frame)
return E_UNEXPECTED;
*number = frame->animation().numberOfActiveAnimations(frame->document());
return S_OK;
}
HRESULT WebFrame::isDisplayingStandaloneImage(_Out_ BOOL* result)
{
if (!result)
return E_POINTER;
*result = FALSE;
Frame* frame = core(this);
if (!frame)
return E_UNEXPECTED;
Document* document = frame->document();
*result = document && document->isImageDocument();
return S_OK;
}
HRESULT WebFrame::allowsFollowingLink(_In_ BSTR url, _Out_ BOOL* result)
{
if (!result)
return E_POINTER;
*result = TRUE;
Frame* frame = core(this);
if (!frame)
return E_UNEXPECTED;
*result = frame->document()->securityOrigin().canDisplay(MarshallingHelpers::BSTRToKURL(url));
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;
auto elements = formElement->copyAssociatedElementsVector();
int inCount = *cControls;
int count = (int) elements.size();
*cControls = count;
if (!controls)
return S_OK;
if (inCount < count)
return E_FAIL;
*cControls = 0;
for (auto& element : elements) {
if (element->isEnumeratable()) {
// Skip option elements, other duds.
controls[*cControls] = DOMElement::createInstance(&element->asHTMLElement());
(*cControls)++;
}
}
return S_OK;
}
HRESULT WebFrame::elementIsPassword(IDOMElement *element, bool *result)
{
HTMLInputElement* inputElement = inputElementFromDOMElement(element);
*result = inputElement && inputElement->isPasswordField();
return S_OK;
}
HRESULT WebFrame::searchForLabelsBeforeElement(const BSTR* labels, unsigned cLabels, IDOMElement* beforeElement, unsigned* outResultDistance, BOOL* outResultIsInCellAbove, BSTR* result)
{
if (!result) {
ASSERT_NOT_REACHED();
return E_POINTER;
}
if (outResultDistance)
*outResultDistance = 0;
if (outResultIsInCellAbove)
*outResultIsInCellAbove = FALSE;
*result = 0;
if (!cLabels)
return S_OK;
if (cLabels < 1)
return E_INVALIDARG;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
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;
size_t resultDistance;
bool resultIsInCellAbove;
String label = coreFrame->searchForLabelsBeforeElement(labelStrings, coreElement, &resultDistance, &resultIsInCellAbove);
*result = BString(label).release();
if (label.length() && !*result)
return E_OUTOFMEMORY;
if (outResultDistance)
*outResultDistance = resultDistance;
if (outResultIsInCellAbove)
*outResultIsInCellAbove = resultIsInCellAbove;
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_UNEXPECTED;
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 = BString(label).release();
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) {
BString mimeTypeBStr;
if (SUCCEEDED(urlResponse->MIMEType(&mimeTypeBStr))) {
String mimeType(mimeTypeBStr, SysStringLen(mimeTypeBStr));
*result = mimeType == "text/html" || WebCore::MIMETypeRegistry::isXMLMIMEType(mimeType);
}
}
return hr;
}
HRESULT WebFrame::layerTreeAsText(_Deref_out_opt_ BSTR* result)
{
if (!result)
return E_POINTER;
*result = nullptr;
Frame* frame = core(this);
if (!frame)
return E_UNEXPECTED;
String text = frame->layerTreeAsText();
*result = BString(text).release();
return S_OK;
}
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();
}
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, const FloatSize& pageSize, const FloatSize& originalPageSize, float maximumShrinkRatio, AdjustViewSizeOrNot adjustViewSize)
{
Frame* coreFrame = core(this);
ASSERT(coreFrame);
coreFrame->setPrinting(printing, pageSize, originalPageSize, maximumShrinkRatio, adjustViewSize ? AdjustViewSize : DoNotAdjustViewSize);
}
HRESULT WebFrame::setInPrintingMode(BOOL value, _In_ HDC printDC)
{
if (m_inPrintingMode == !!value)
return S_OK;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
if (!coreFrame->document())
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
FloatSize minLayoutSize(0.0, 0.0);
FloatSize originalPageSize(0.0, 0.0);
if (m_inPrintingMode && !coreFrame->document()->isFrameSet()) {
if (!printDC) {
ASSERT_NOT_REACHED();
return E_POINTER;
}
const int desiredPixelsPerInch = 72;
IntRect printRect = printerRect(printDC);
int paperHorizontalPixelsPerInch = ::GetDeviceCaps(printDC, LOGPIXELSX);
int paperVerticalPixelsPerInch = ::GetDeviceCaps(printDC, LOGPIXELSY);
int paperWidth = printRect.width() * desiredPixelsPerInch / paperHorizontalPixelsPerInch;
int paperHeight = printRect.height() * desiredPixelsPerInch / paperVerticalPixelsPerInch;
originalPageSize = FloatSize(paperWidth, paperHeight);
Frame* coreFrame = core(this);
minLayoutSize = coreFrame->resizePageRectsKeepingRatio(originalPageSize, FloatSize(paperWidth * PrintingMinimumShrinkFactor, paperHeight * PrintingMinimumShrinkFactor));
}
setPrinting(m_inPrintingMode, minLayoutSize, originalPageSize, PrintingMaximumShrinkFactor / PrintingMinimumShrinkFactor, AdjustViewSize);
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;
if (headerHeight && SUCCEEDED(ui->webViewHeaderHeight(d->webView, &height)))
*headerHeight = height;
if (footerHeight && SUCCEEDED(ui->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;
RECT rect;
if (FAILED(ui->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.maxX(),
pageRect.height() - marginRect.y() - marginRect.maxY());
computePageRectsForFrame(coreFrame, adjustedRect, headerHeight, footerHeight, 1.0,m_pageRects, m_pageHeight);
return m_pageRects;
}
HRESULT WebFrame::getPrintedPageCount(_In_ HDC printDC, _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)
return E_UNEXPECTED;
if (!coreFrame->document())
return E_FAIL;
const Vector<IntRect>& pages = computePageRects(printDC);
*pageCount = (UINT) pages.size();
return S_OK;
}
#if USE(CG)
void WebFrame::drawHeader(PlatformGraphicsContext* pctx, IWebUIDelegate* ui, const IntRect& pageRect, float headerHeight)
{
int x = pageRect.x();
int y = 0;
RECT headerRect = {x, y, x+pageRect.width(), y+static_cast<int>(headerHeight)};
ui->drawHeaderInRect(d->webView, &headerRect, reinterpret_cast<ULONG_PTR>(pctx));
}
void WebFrame::drawFooter(PlatformGraphicsContext* pctx, IWebUIDelegate* ui, const IntRect& pageRect, UINT page, UINT pageCount, float headerHeight, float footerHeight)
{
int x = pageRect.x();
int y = std::max((int)headerHeight+pageRect.height(), m_pageHeight-static_cast<int>(footerHeight));
RECT footerRect = {x, y, x+pageRect.width(), y+static_cast<int>(footerHeight)};
ui->drawFooterInRect(d->webView, &footerRect, reinterpret_cast<ULONG_PTR>(pctx), page + 1, pageCount);
}
void WebFrame::spoolPage(PlatformGraphicsContext* pctx, GraphicsContext& spoolCtx, HDC printDC, IWebUIDelegate* ui, float headerHeight, float footerHeight, UINT page, UINT pageCount)
{
Frame* coreFrame = core(this);
IntRect pageRect = m_pageRects[page];
CGContextSaveGState(pctx);
IntRect printRect = printerRect(printDC);
CGRect mediaBox = CGRectMake(CGFloat(0),
CGFloat(0),
CGFloat(printRect.width()),
CGFloat(printRect.height()));
CGContextBeginPage(pctx, &mediaBox);
CGFloat scale = static_cast<float>(mediaBox.size.width)/static_cast<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->view()->paintContents(spoolCtx, pageRect);
CGContextTranslateCTM(pctx, CGFloat(pageRect.x()), CGFloat(pageRect.y())-headerHeight);
if (headerHeight)
drawHeader(pctx, ui, pageRect, headerHeight);
if (footerHeight)
drawFooter(pctx, ui, pageRect, page, pageCount, headerHeight, footerHeight);
CGContextEndPage(pctx);
CGContextRestoreGState(pctx);
}
#elif USE(CAIRO)
static float scaleFactor(HDC printDC, const IntRect& marginRect, const IntRect& pageRect)
{
const IntRect& printRect = printerRect(printDC);
IntRect adjustedRect = IntRect(
printRect.x() + marginRect.x(),
printRect.y() + marginRect.y(),
printRect.width() - marginRect.x() - marginRect.maxX(),
printRect.height() - marginRect.y() - marginRect.maxY());
float scale = static_cast<float>(adjustedRect.width()) / static_cast<float>(pageRect.width());
if (!scale)
scale = 1.0;
return scale;
}
static HDC hdcFromContext(PlatformGraphicsContext* pctx)
{
return cairo_win32_surface_get_dc(cairo_get_target(pctx->cr()));
}
void WebFrame::drawHeader(PlatformGraphicsContext* pctx, IWebUIDelegate* ui, const IntRect& pageRect, float headerHeight)
{
HDC hdc = hdcFromContext(pctx);
int x = pageRect.x();
int y = 0;
RECT headerRect = {x, y, x + pageRect.width(), y + static_cast<int>(headerHeight)};
ui->drawHeaderInRect(d->webView, &headerRect, reinterpret_cast<ULONG_PTR>(hdc));
}
void WebFrame::drawFooter(PlatformGraphicsContext* pctx, IWebUIDelegate* ui, const IntRect& pageRect, UINT page, UINT pageCount, float headerHeight, float footerHeight)
{
HDC hdc = hdcFromContext(pctx);
int x = pageRect.x();
int y = std::max(static_cast<int>(headerHeight) + pageRect.height(), m_pageHeight -static_cast<int>(footerHeight));
RECT footerRect = {x, y, x + pageRect.width(), y + static_cast<int>(footerHeight)};
ui->drawFooterInRect(d->webView, &footerRect, reinterpret_cast<ULONG_PTR>(hdc), page+1, pageCount);
}
static XFORM buildXFORMFromCairo(HDC targetDC, cairo_t* previewContext)
{
XFORM scaled;
GetWorldTransform(targetDC, &scaled);
cairo_matrix_t ctm;
cairo_get_matrix(previewContext, &ctm);
// Scale to the preview screen bounds
scaled.eM11 = ctm.xx;
scaled.eM22 = ctm.yy;
return scaled;
}
void WebFrame::spoolPage(PlatformGraphicsContext* pctx, GraphicsContext& spoolCtx, HDC printDC, IWebUIDelegate* ui, float headerHeight, float footerHeight, UINT page, UINT pageCount)
{
Frame* coreFrame = core(this);
const IntRect& pageRect = m_pageRects[page];
const IntRect& marginRect = printerMarginRect(printDC);
// In preview, the printDC is a placeholder, so just always use the HDC backing the graphics context.
HDC hdc = hdcFromContext(pctx);
spoolCtx.save();
XFORM original, scaled;
GetWorldTransform(hdc, &original);
cairo_t* cr = pctx->cr();
bool preview = (hdc != printDC);
if (preview) {
// If this is a preview, the Windows HDC was set to a non-scaled state so that Cairo will
// draw correctly. We need to retain the correct preview scale here for use when the Cairo
// drawing completes so that we can scale our GDI-based header/footer calls. This is a
// workaround for a bug in Cairo (see https://bugs.freedesktop.org/show_bug.cgi?id=28161)
scaled = buildXFORMFromCairo(hdc, cr);
}
float scale = scaleFactor(printDC, marginRect, pageRect);
IntRect cairoMarginRect(marginRect);
cairoMarginRect.scale(1 / scale);
// We cannot scale the display HDC because the print surface also scales fonts,
// resulting in invalid printing (and print preview)
cairo_scale(cr, scale, scale);
cairo_translate(cr, cairoMarginRect.x(), cairoMarginRect.y() + headerHeight);
// Modify Cairo (only) to account for page position.
cairo_translate(cr, -pageRect.x(), -pageRect.y());
coreFrame->view()->paintContents(spoolCtx, pageRect);
cairo_translate(cr, pageRect.x(), pageRect.y());
if (preview) {
// If this is a preview, the Windows HDC was set to a non-scaled state so that Cairo would
// draw correctly. We need to rescale the HDC to the correct preview scale so our GDI-based
// header/footer calls will draw properly. This is a workaround for a bug in Cairo.
// (see https://bugs.freedesktop.org/show_bug.cgi?id=28161)
SetWorldTransform(hdc, &scaled);
}
XFORM xform = TransformationMatrix().translate(marginRect.x(), marginRect.y()).scale(scale);
ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY);
if (headerHeight)
drawHeader(pctx, ui, pageRect, headerHeight);
if (footerHeight)
drawFooter(pctx, ui, pageRect, page, pageCount, headerHeight, footerHeight);
SetWorldTransform(hdc, &original);
cairo_show_page(cr);
ASSERT(!cairo_status(cr));
spoolCtx.restore();
}
static void setCairoTransformToPreviewHDC(cairo_t* previewCtx, HDC previewDC)
{
XFORM passedCTM;
GetWorldTransform(previewDC, &passedCTM);
// Reset HDC WorldTransform to unscaled state. Scaling must be
// done in Cairo to avoid drawing errors.
XFORM unscaledCTM = passedCTM;
unscaledCTM.eM11 = 1.0;
unscaledCTM.eM22 = 1.0;
SetWorldTransform(previewDC, &unscaledCTM);
// Make the Cairo transform match the information passed to WebKit
// in the HDC's WorldTransform.
cairo_matrix_t ctm = { passedCTM.eM11, passedCTM.eM12, passedCTM.eM21,
passedCTM.eM22, passedCTM.eDx, passedCTM.eDy };
cairo_set_matrix(previewCtx, &ctm);
}
#endif
HRESULT WebFrame::spoolPages(HDC printDC, UINT startPage, UINT endPage, void* ctx)
{
#if USE(CG)
if (!printDC || !ctx) {
ASSERT_NOT_REACHED();
return E_POINTER;
}
#elif USE(CAIRO)
if (!printDC) {
ASSERT_NOT_REACHED();
return E_POINTER;
}
HDC targetDC = (ctx) ? (HDC)ctx : printDC;
cairo_surface_t* printSurface = 0;
if (ctx)
printSurface = cairo_win32_surface_create(targetDC); // in-memory
else
printSurface = cairo_win32_printing_surface_create(targetDC); // metafile
cairo_t* cr = cairo_create(printSurface);
if (!cr) {
cairo_surface_destroy(printSurface);
return E_FAIL;
}
PlatformContextCairo platformContext(cr);
PlatformGraphicsContext* pctx = &platformContext;
cairo_destroy(cr);
if (ctx) {
// If this is a preview, the Windows HDC was sent with scaling information.
// Retrieve it and reset it so that it draws properly. This is a workaround
// for a bug in Cairo (see https://bugs.freedesktop.org/show_bug.cgi?id=28161)
setCairoTransformToPreviewHDC(cr, targetDC);
}
cairo_surface_set_fallback_resolution(printSurface, 72.0, 72.0);
#endif
if (!m_inPrintingMode) {
ASSERT_NOT_REACHED();
return E_FAIL;
}
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
if (!coreFrame->document())
return E_FAIL;
UINT pageCount = (UINT) m_pageRects.size();
#if USE(CG)
PlatformGraphicsContext* pctx = (PlatformGraphicsContext*)ctx;
#endif
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;
float headerHeight = 0, footerHeight = 0;
headerAndFooterHeights(&headerHeight, &footerHeight);
#if USE(CG) || USE(CAIRO)
GraphicsContext spoolCtx(pctx);
spoolCtx.setShouldIncludeChildWindows(true);
for (UINT ii = startPage; ii < endPage; ii++)
spoolPage(pctx, spoolCtx, printDC, ui.get(), headerHeight, footerHeight, ii, pageCount);
#endif
#if USE(CAIRO)
cairo_surface_finish(printSurface);
ASSERT(!cairo_surface_status(printSurface));
cairo_surface_destroy(printSurface);
#endif
return S_OK;
}
HRESULT WebFrame::isFrameSet(_Out_ BOOL* result)
{
if (!result)
return E_POINTER;
*result = FALSE;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
if (!coreFrame->document())
return E_FAIL;
*result = coreFrame->document()->isFrameSet() ? TRUE : FALSE;
return S_OK;
}
HRESULT WebFrame::string(_Deref_opt_out_ BSTR* result)
{
if (!result)
return E_POINTER;
*result = nullptr;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
RefPtr<Range> allRange(rangeOfContents(*coreFrame->document()));
String allString = plainText(allRange.get());
*result = BString(allString).release();
return S_OK;
}
HRESULT WebFrame::size(_Out_ SIZE* size)
{
if (!size)
return E_POINTER;
size->cx = size->cy = 0;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
FrameView* view = coreFrame->view();
if (!view)
return E_FAIL;
size->cx = view->width();
size->cy = view->height();
return S_OK;
}
HRESULT WebFrame::hasScrollBars(_Out_ BOOL* result)
{
if (!result)
return E_POINTER;
*result = FALSE;
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
FrameView* view = coreFrame->view();
if (!view)
return E_FAIL;
if (view->horizontalScrollbar() || view->verticalScrollbar())
*result = TRUE;
return S_OK;
}
HRESULT WebFrame::contentBounds(_Out_ RECT* result)
{
if (!result)
return E_POINTER;
::SetRectEmpty(result);
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
FrameView* view = coreFrame->view();
if (!view)
return E_FAIL;
result->bottom = view->contentsHeight();
result->right = view->contentsWidth();
return S_OK;
}
HRESULT WebFrame::frameBounds(_Out_ RECT* result)
{
if (!result)
return E_POINTER;
::SetRectEmpty(result);
Frame* coreFrame = core(this);
if (!coreFrame)
return E_UNEXPECTED;
FrameView* view = coreFrame->view();
if (!view)
return E_FAIL;
FloatRect bounds = view->visibleContentRectIncludingScrollbars();
result->bottom = (LONG) bounds.height();
result->right = (LONG) bounds.width();
return S_OK;
}
HRESULT WebFrame::isDescendantOfFrame(_In_opt_ IWebFrame* ancestor, _Out_ BOOL* result)
{
if (!result)
return E_POINTER;
*result = FALSE;
Frame* coreFrame = core(this);
COMPtr<WebFrame> ancestorWebFrame(Query, ancestor);
if (!ancestorWebFrame)
return S_OK;
*result = (coreFrame && coreFrame->tree().isDescendantOf(core(ancestorWebFrame.get()))) ? TRUE : FALSE;
return S_OK;
}
HRESULT WebFrame::stringByEvaluatingJavaScriptInScriptWorld(IWebScriptWorld* iWorld, JSObjectRef globalObjectRef, BSTR script, BSTR* evaluationResult)
{
if (!evaluationResult)
return E_POINTER;
*evaluationResult = 0;
if (!iWorld)
return E_POINTER;
COMPtr<WebScriptWorld> world(Query, iWorld);
if (!world)
return E_INVALIDARG;
Frame* coreFrame = core(this);
String string = String(script, SysStringLen(script));
// Start off with some guess at a frame and a global object, we'll try to do better...!
JSDOMWindow* anyWorldGlobalObject = coreFrame->script().globalObject(mainThreadNormalWorld());
// The global object is probably a proxy object? - if so, we know how to use this!
JSC::JSObject* globalObjectObj = toJS(globalObjectRef);
auto& vm = globalObjectObj->vm();
if (globalObjectObj->inherits<JSWindowProxy>(vm))
anyWorldGlobalObject = JSC::jsDynamicCast<JSDOMWindow*>(vm, static_cast<JSWindowProxy*>(globalObjectObj)->window());
if (!anyWorldGlobalObject)
return E_INVALIDARG;
// Get the frame frome the global object we've settled on.
Frame* frame = anyWorldGlobalObject->wrapped().frame();
ASSERT(frame->document());
JSValue result = frame->script().executeScriptInWorld(world->world(), string, true);
if (!frame) // In case the script removed our frame from the page.
return S_OK;
// This bizarre set of rules matches behavior from WebKit for Safari 2.0.
// If you don't like it, use -[WebScriptObject evaluateWebScript:] or
// JSEvaluateScript instead, since they have less surprising semantics.
if (!result || (!result.isBoolean() && !result.isString() && !result.isNumber()))
return S_OK;
JSC::JSGlobalObject* lexicalGlobalObject = anyWorldGlobalObject;
JSC::JSLockHolder lock(lexicalGlobalObject);
String resultString = result.toWTFString(lexicalGlobalObject);
*evaluationResult = BString(resultString).release();
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->markers().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->markers().removeMarkers(DocumentMarker::Grammar);
}
}
WebView* WebFrame::webView() const
{
return d->webView;
}
void WebFrame::setWebView(WebView* webView)
{
d->webView = webView;
}
COMPtr<IAccessible> WebFrame::accessible() const
{
Frame* coreFrame = core(this);
ASSERT(coreFrame);
Document* currentDocument = coreFrame->document();
if (!currentDocument)
m_accessible = 0;
else if (!m_accessible || m_accessible->document() != currentDocument) {
// Either we've never had a wrapper for this frame's top-level Document,
// the Document renderer was destroyed and its wrapper was detached, or
// the previous Document is in the back/forward cache, and the current document
// needs to be wrapped.
m_accessible = new AccessibleDocument(currentDocument, webView()->viewWindow());
}
return m_accessible.get();
}
void WebFrame::updateBackground()
{
Frame* coreFrame = core(this);
if (!coreFrame || !coreFrame->view())
return;
Optional<Color> backgroundColor;
if (webView()->transparent())
backgroundColor = Color(Color::transparent);
coreFrame->view()->updateBackgroundRecursively(backgroundColor);
}
// IWebFrame2
HRESULT WebFrame::isMainFrame(_Out_ BOOL* value)
{
if (!value)
return E_POINTER;
Frame* coreFrame = core(this);
*value = coreFrame->isMainFrame();
return S_OK;
}