/* * Copyright (C) 2010 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 INC. AND ITS CONTRIBUTORS ``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 ITS 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 "InjectedBundlePage.h" #include "InjectedBundle.h" #include "StringFunctions.h" #include #include #include #include #include #include #include #include #include #include using namespace std; namespace WTR { static bool hasPrefix(const string& searchString, const string& prefix) { return searchString.length() >= prefix.length() && searchString.substr(0, prefix.length()) == prefix; } static JSValueRef propertyValue(JSContextRef context, JSObjectRef object, const char* propertyName) { if (!object) return 0; JSRetainPtr propertyNameString(Adopt, JSStringCreateWithUTF8CString(propertyName)); return JSObjectGetProperty(context, object, propertyNameString.get(), 0); } static double propertyValueDouble(JSContextRef context, JSObjectRef object, const char* propertyName) { JSValueRef value = propertyValue(context, object, propertyName); if (!value) return 0; return JSValueToNumber(context, value, 0); } static int propertyValueInt(JSContextRef context, JSObjectRef object, const char* propertyName) { return static_cast(propertyValueDouble(context, object, propertyName)); } static double numericWindowPropertyValue(WKBundleFrameRef frame, const char* propertyName) { JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame); return propertyValueDouble(context, JSContextGetGlobalObject(context), propertyName); } static string dumpPath(JSGlobalContextRef context, JSObjectRef nodeValue) { JSValueRef nodeNameValue = propertyValue(context, nodeValue, "nodeName"); JSRetainPtr jsStringNodeName(Adopt, JSValueToStringCopy(context, nodeNameValue, 0)); WKRetainPtr nodeName = toWK(jsStringNodeName); JSValueRef parentNode = propertyValue(context, nodeValue, "parentNode"); ostringstream out; out << nodeName; if (parentNode && JSValueIsObject(context, parentNode)) out << " > " << dumpPath(context, (JSObjectRef)parentNode); return out.str(); } static string dumpPath(WKBundlePageRef page, WKBundleScriptWorldRef world, WKBundleNodeHandleRef node) { if (!node) return "(null)"; WKBundleFrameRef frame = WKBundlePageGetMainFrame(page); JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world); JSValueRef nodeValue = WKBundleFrameGetJavaScriptWrapperForNodeForWorld(frame, node, world); ASSERT(JSValueIsObject(context, nodeValue)); JSObjectRef nodeObject = (JSObjectRef)nodeValue; return dumpPath(context, nodeObject); } static string toStr(WKBundlePageRef page, WKBundleScriptWorldRef world, WKBundleRangeHandleRef rangeRef) { if (!rangeRef) return "(null)"; WKBundleFrameRef frame = WKBundlePageGetMainFrame(page); JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world); JSValueRef rangeValue = WKBundleFrameGetJavaScriptWrapperForRangeForWorld(frame, rangeRef, world); ASSERT(JSValueIsObject(context, rangeValue)); JSObjectRef rangeObject = (JSObjectRef)rangeValue; JSValueRef startNodeValue = propertyValue(context, rangeObject, "startContainer"); ASSERT(JSValueIsObject(context, startNodeValue)); JSObjectRef startNodeObject = (JSObjectRef)startNodeValue; JSValueRef endNodeValue = propertyValue(context, rangeObject, "endContainer"); ASSERT(JSValueIsObject(context, endNodeValue)); JSObjectRef endNodeObject = (JSObjectRef)endNodeValue; int startOffset = propertyValueInt(context, rangeObject, "startOffset"); int endOffset = propertyValueInt(context, rangeObject, "endOffset"); ostringstream out; out << "range from " << startOffset << " of " << dumpPath(context, startNodeObject) << " to " << endOffset << " of " << dumpPath(context, endNodeObject); return out.str(); } static ostream& operator<<(ostream& out, WKBundleCSSStyleDeclarationRef style) { // DumpRenderTree calls -[DOMCSSStyleDeclaration description], which just dumps class name and object address. // No existing tests actually hit this code path at the time of this writing, because WebCore doesn't call // the editing client if the styling operation source is CommandFromDOM or CommandFromDOMWithUserInterface. out << ""; return out; } static ostream& operator<<(ostream& out, WKBundleFrameRef frame) { WKRetainPtr name(AdoptWK, WKBundleFrameCopyName(frame)); if (WKBundleFrameIsMainFrame(frame)) { if (!WKStringIsEmpty(name.get())) out << "main frame \"" << name << "\""; else out << "main frame"; } else { if (!WKStringIsEmpty(name.get())) out << "frame \"" << name << "\""; else out << "frame (anonymous)"; } return out; } InjectedBundlePage::InjectedBundlePage(WKBundlePageRef page) : m_page(page) , m_world(AdoptWK, WKBundleScriptWorldCreateWorld()) { WKBundlePageLoaderClient loaderClient = { 0, this, didStartProvisionalLoadForFrame, didReceiveServerRedirectForProvisionalLoadForFrame, didFailProvisionalLoadWithErrorForFrame, didCommitLoadForFrame, didFinishDocumentLoadForFrame, didFinishLoadForFrame, didFailLoadWithErrorForFrame, didSameDocumentNavigationForFrame, didReceiveTitleForFrame, 0, 0, 0, didDisplayInsecureContentForFrame, didRunInsecureContentForFrame, didClearWindowForFrame, didCancelClientRedirectForFrame, willPerformClientRedirectForFrame, didHandleOnloadEventsForFrame, }; WKBundlePageSetPageLoaderClient(m_page, &loaderClient); WKBundlePageResourceLoadClient resourceLoadClient = { 0, this, didInitiateLoadForResource, willSendRequestForFrame, didReceiveResponseForResource, didReceiveContentLengthForResource, didFinishLoadForResource, didFailLoadForResource }; WKBundlePageSetResourceLoadClient(m_page, &resourceLoadClient); WKBundlePagePolicyClient policyClient = { 0, this, decidePolicyForNavigationAction, decidePolicyForNewWindowAction, decidePolicyForResponse, unableToImplementPolicy }; WKBundlePageSetPolicyClient(m_page, &policyClient); WKBundlePageUIClient uiClient = { 0, this, willAddMessageToConsole, willSetStatusbarText, willRunJavaScriptAlert, willRunJavaScriptConfirm, willRunJavaScriptPrompt, 0, /*mouseDidMoveOverElement*/ 0, /*pageDidScroll*/ 0, /*paintCustomOverhangArea*/ 0, /*shouldGenerateFileForUpload*/ 0, /*generateFileForUpload*/ }; WKBundlePageSetUIClient(m_page, &uiClient); WKBundlePageEditorClient editorClient = { 0, this, shouldBeginEditing, shouldEndEditing, shouldInsertNode, shouldInsertText, shouldDeleteRange, shouldChangeSelectedRange, shouldApplyStyle, didBeginEditing, didEndEditing, didChange, didChangeSelection }; WKBundlePageSetEditorClient(m_page, &editorClient); #if ENABLE(FULLSCREEN_API) WKBundlePageFullScreenClient fullScreenClient = { 0, this, supportsFullScreen, enterFullScreenForElement, exitFullScreenForElement, }; WKBundlePageSetFullScreenClient(m_page, &fullScreenClient); #endif } InjectedBundlePage::~InjectedBundlePage() { } void InjectedBundlePage::stopLoading() { WKBundlePageStopLoading(m_page); } void InjectedBundlePage::reset() { WKBundlePageClearMainFrameName(m_page); WKBundlePageSetPageZoomFactor(m_page, 1); WKBundlePageSetTextZoomFactor(m_page, 1); WKPoint origin = { 0, 0 }; WKBundlePageSetScaleAtOrigin(m_page, 1, origin); m_previousTestBackForwardListItem = adoptWK(WKBundleBackForwardListCopyItemAtIndex(WKBundlePageGetBackForwardList(m_page), 0)); WKBundleFrameClearOpener(WKBundlePageGetMainFrame(m_page)); } // Loader Client Callbacks void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo) { static_cast(const_cast(clientInfo))->didStartProvisionalLoadForFrame(frame); } void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo) { static_cast(const_cast(clientInfo))->didReceiveServerRedirectForProvisionalLoadForFrame(frame); } void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo) { static_cast(const_cast(clientInfo))->didFailProvisionalLoadWithErrorForFrame(frame, error); } void InjectedBundlePage::didCommitLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo) { static_cast(const_cast(clientInfo))->didCommitLoadForFrame(frame); } void InjectedBundlePage::didFinishLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo) { static_cast(const_cast(clientInfo))->didFinishLoadForFrame(frame); } void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo) { static_cast(const_cast(clientInfo))->didFinishDocumentLoadForFrame(frame); } void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo) { static_cast(const_cast(clientInfo))->didFailLoadWithErrorForFrame(frame, error); } void InjectedBundlePage::didReceiveTitleForFrame(WKBundlePageRef page, WKStringRef title, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo) { static_cast(const_cast(clientInfo))->didReceiveTitleForFrame(title, frame); } void InjectedBundlePage::didClearWindowForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void *clientInfo) { static_cast(const_cast(clientInfo))->didClearWindowForFrame(frame, world); } void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo) { static_cast(const_cast(clientInfo))->didCancelClientRedirectForFrame(frame); } void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKURLRef url, double delay, double date, const void* clientInfo) { static_cast(const_cast(clientInfo))->willPerformClientRedirectForFrame(frame, url, delay, date); } void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef*, const void* clientInfo) { static_cast(const_cast(clientInfo))->didSameDocumentNavigationForFrame(frame, type); } void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo) { static_cast(const_cast(clientInfo))->didHandleOnloadEventsForFrame(frame); } void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo) { static_cast(const_cast(clientInfo))->didDisplayInsecureContentForFrame(frame); } void InjectedBundlePage::didRunInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo) { static_cast(const_cast(clientInfo))->didRunInsecureContentForFrame(frame); } void InjectedBundlePage::didInitiateLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, bool pageLoadIsProvisional, const void* clientInfo) { static_cast(const_cast(clientInfo))->didInitiateLoadForResource(page, frame, identifier, request, pageLoadIsProvisional); } WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, WKURLResponseRef redirectResponse, const void* clientInfo) { return static_cast(const_cast(clientInfo))->willSendRequestForFrame(page, frame, identifier, request, redirectResponse); } void InjectedBundlePage::didReceiveResponseForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLResponseRef response, const void* clientInfo) { static_cast(const_cast(clientInfo))->didReceiveResponseForResource(page, frame, identifier, response); } void InjectedBundlePage::didReceiveContentLengthForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, uint64_t length, const void* clientInfo) { static_cast(const_cast(clientInfo))->didReceiveContentLengthForResource(page, frame, identifier, length); } void InjectedBundlePage::didFinishLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, const void* clientInfo) { static_cast(const_cast(clientInfo))->didFinishLoadForResource(page, frame, identifier); } void InjectedBundlePage::didFailLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKErrorRef error, const void* clientInfo) { static_cast(const_cast(clientInfo))->didFinishLoadForResource(page, frame, identifier, error); } void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundleFrameRef frame) { if (!InjectedBundle::shared().isTestRunning()) return; if (InjectedBundle::shared().topLoadingFrame()) return; InjectedBundle::shared().setTopLoadingFrame(frame); } void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundleFrameRef frame) { } void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef error) { if (!InjectedBundle::shared().isTestRunning()) return; if (frame != InjectedBundle::shared().topLoadingFrame()) return; InjectedBundle::shared().setTopLoadingFrame(0); if (InjectedBundle::shared().layoutTestController()->waitToDump()) return; InjectedBundle::shared().done(); } void InjectedBundlePage::didCommitLoadForFrame(WKBundleFrameRef frame) { } enum FrameNamePolicy { ShouldNotIncludeFrameName, ShouldIncludeFrameName }; static void dumpFrameScrollPosition(WKBundleFrameRef frame, FrameNamePolicy shouldIncludeFrameName = ShouldNotIncludeFrameName) { double x = numericWindowPropertyValue(frame, "pageXOffset"); double y = numericWindowPropertyValue(frame, "pageYOffset"); if (fabs(x) > 0.00000001 || fabs(y) > 0.00000001) { if (shouldIncludeFrameName) { WKRetainPtr name(AdoptWK, WKBundleFrameCopyName(frame)); InjectedBundle::shared().os() << "frame '" << name << "' "; } InjectedBundle::shared().os() << "scrolled to " << x << "," << y << "\n"; } } static void dumpDescendantFrameScrollPositions(WKBundleFrameRef frame) { WKRetainPtr childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame)); size_t size = WKArrayGetSize(childFrames.get()); for (size_t i = 0; i < size; ++i) { WKBundleFrameRef subframe = static_cast(WKArrayGetItemAtIndex(childFrames.get(), i)); dumpFrameScrollPosition(subframe, ShouldIncludeFrameName); dumpDescendantFrameScrollPositions(subframe); } } void InjectedBundlePage::dumpAllFrameScrollPositions() { WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page); dumpFrameScrollPosition(frame); dumpDescendantFrameScrollPositions(frame); } static JSRetainPtr toJS(const char* string) { return JSRetainPtr(Adopt, JSStringCreateWithUTF8CString(string)); } static bool hasDocumentElement(WKBundleFrameRef frame) { JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame); JSObjectRef globalObject = JSContextGetGlobalObject(context); JSValueRef documentValue = JSObjectGetProperty(context, globalObject, toJS("document").get(), 0); if (!documentValue) return false; ASSERT(JSValueIsObject(context, documentValue)); JSObjectRef document = JSValueToObject(context, documentValue, 0); JSValueRef documentElementValue = JSObjectGetProperty(context, document, toJS("documentElement").get(), 0); if (!documentElementValue) return false; return JSValueToBoolean(context, documentElementValue); } static void dumpFrameText(WKBundleFrameRef frame) { // If the frame doesn't have a document element, its inner text will be an empty string, so // we'll end up just appending a single newline below. But DumpRenderTree doesn't append // anything in this case, so we shouldn't either. if (!hasDocumentElement(frame)) return; WKRetainPtr text(AdoptWK, WKBundleFrameCopyInnerText(frame)); InjectedBundle::shared().os() << text << "\n"; } static void dumpDescendantFramesText(WKBundleFrameRef frame) { WKRetainPtr childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame)); size_t size = WKArrayGetSize(childFrames.get()); for (size_t i = 0; i < size; ++i) { WKBundleFrameRef subframe = static_cast(WKArrayGetItemAtIndex(childFrames.get(), i)); WKRetainPtr subframeName(AdoptWK, WKBundleFrameCopyName(subframe)); InjectedBundle::shared().os() << "\n--------\nFrame: '" << subframeName << "'\n--------\n"; dumpFrameText(subframe); dumpDescendantFramesText(subframe); } } void InjectedBundlePage::dumpAllFramesText() { WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page); dumpFrameText(frame); dumpDescendantFramesText(frame); } void InjectedBundlePage::dump() { ASSERT(InjectedBundle::shared().isTestRunning()); InjectedBundle::shared().layoutTestController()->invalidateWaitToDumpWatchdogTimer(); // Force a paint before dumping. This matches DumpRenderTree on Windows. (DumpRenderTree on Mac // does this at a slightly different time.) See for details. WKBundlePageForceRepaint(m_page); WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page); string url = toSTD(adoptWK(WKURLCopyString(adoptWK(WKBundleFrameCopyURL(frame)).get()))); if (strstr(url.c_str(), "dumpAsText/")) InjectedBundle::shared().layoutTestController()->dumpAsText(); switch (InjectedBundle::shared().layoutTestController()->whatToDump()) { case LayoutTestController::RenderTree: { WKRetainPtr text(AdoptWK, WKBundlePageCopyRenderTreeExternalRepresentation(m_page)); InjectedBundle::shared().os() << text; break; } case LayoutTestController::MainFrameText: dumpFrameText(WKBundlePageGetMainFrame(m_page)); break; case LayoutTestController::AllFramesText: dumpAllFramesText(); break; } if (InjectedBundle::shared().layoutTestController()->shouldDumpAllFrameScrollPositions()) dumpAllFrameScrollPositions(); else if (InjectedBundle::shared().layoutTestController()->shouldDumpMainFrameScrollPosition()) dumpFrameScrollPosition(WKBundlePageGetMainFrame(m_page)); if (InjectedBundle::shared().layoutTestController()->shouldDumpBackForwardListsForAllWindows()) InjectedBundle::shared().dumpBackForwardListsForAllPages(); if (InjectedBundle::shared().shouldDumpPixels() && InjectedBundle::shared().layoutTestController()->shouldDumpPixels()) InjectedBundle::shared().setPixelResult(adoptWK(WKBundlePageCreateSnapshotInViewCoordinates(m_page, WKBundleFrameGetVisibleContentBounds(WKBundlePageGetMainFrame(m_page)), kWKImageOptionsShareable)).get()); InjectedBundle::shared().done(); } void InjectedBundlePage::didFinishLoadForFrame(WKBundleFrameRef frame) { if (!InjectedBundle::shared().isTestRunning()) return; if (frame != InjectedBundle::shared().topLoadingFrame()) return; InjectedBundle::shared().setTopLoadingFrame(0); if (InjectedBundle::shared().layoutTestController()->waitToDump()) return; InjectedBundle::shared().page()->dump(); } void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef) { if (!InjectedBundle::shared().isTestRunning()) return; if (frame != InjectedBundle::shared().topLoadingFrame()) return; InjectedBundle::shared().setTopLoadingFrame(0); if (InjectedBundle::shared().layoutTestController()->waitToDump()) return; InjectedBundle::shared().done(); } void InjectedBundlePage::didReceiveTitleForFrame(WKStringRef title, WKBundleFrameRef frame) { if (!InjectedBundle::shared().isTestRunning()) return; if (!InjectedBundle::shared().layoutTestController()->shouldDumpTitleChanges()) return; InjectedBundle::shared().os() << "TITLE CHANGED: " << title << "\n"; } void InjectedBundlePage::didClearWindowForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world) { if (!InjectedBundle::shared().isTestRunning()) return; JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world); JSObjectRef window = JSContextGetGlobalObject(context); if (WKBundleScriptWorldNormalWorld() != world) { JSObjectSetProperty(context, window, toJS("__worldID").get(), JSValueMakeNumber(context, LayoutTestController::worldIDForWorld(world)), kJSPropertyAttributeReadOnly, 0); return; } JSValueRef exception = 0; InjectedBundle::shared().layoutTestController()->makeWindowObject(context, window, &exception); InjectedBundle::shared().gcController()->makeWindowObject(context, window, &exception); InjectedBundle::shared().eventSendingController()->makeWindowObject(context, window, &exception); } void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundleFrameRef frame) { } void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundleFrameRef frame, WKURLRef url, double delay, double date) { } void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundleFrameRef frame, WKSameDocumentNavigationType type) { } void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundleFrameRef frame) { if (!InjectedBundle::shared().isTestRunning()) return; unsigned pendingFrameUnloadEvents = WKBundleFrameGetPendingUnloadCount(frame); if (pendingFrameUnloadEvents) InjectedBundle::shared().os() << frame << " - has " << pendingFrameUnloadEvents << " onunload handler(s)\n"; } void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundleFrameRef frame) { } void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundleFrameRef frame) { } void InjectedBundlePage::didRunInsecureContentForFrame(WKBundleFrameRef frame) { } void InjectedBundlePage::didInitiateLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, WKURLRequestRef, bool) { } // Resource Load Client Callbacks WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef, WKBundleFrameRef, uint64_t, WKURLRequestRef request, WKURLResponseRef) { if (InjectedBundle::shared().isTestRunning() && InjectedBundle::shared().layoutTestController()->willSendRequestReturnsNull()) return 0; WKRetainPtr url = adoptWK(WKURLRequestCopyURL(request)); WKRetainPtr host = adoptWK(WKURLCopyHostName(url.get())); WKRetainPtr scheme = adoptWK(WKURLCopyScheme(url.get())); if (host && !WKStringIsEmpty(host.get()) && (WKStringIsEqualToUTF8CStringIgnoringCase(scheme.get(), "http") || WKStringIsEqualToUTF8CStringIgnoringCase(scheme.get(), "https")) && !WKStringIsEqualToUTF8CString(host.get(), "127.0.0.1") && !WKStringIsEqualToUTF8CString(host.get(), "255.255.255.255") // Used in some tests that expect to get back an error. && !WKStringIsEqualToUTF8CStringIgnoringCase(host.get(), "localhost")) { InjectedBundle::shared().os() << "Blocked access to external URL " << url << "\n"; return 0; } WKRetain(request); return request; } void InjectedBundlePage::didReceiveResponseForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t, WKURLResponseRef) { } void InjectedBundlePage::didReceiveContentLengthForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t, uint64_t) { } void InjectedBundlePage::didFinishLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t) { } void InjectedBundlePage::didFailLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t, WKErrorRef) { } // Policy Client Callbacks WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNavigationAction(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleNavigationActionRef navigationAction, WKURLRequestRef request, WKTypeRef* userData, const void* clientInfo) { return static_cast(const_cast(clientInfo))->decidePolicyForNavigationAction(page, frame, navigationAction, request, userData); } WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNewWindowAction(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleNavigationActionRef navigationAction, WKURLRequestRef request, WKStringRef frameName, WKTypeRef* userData, const void* clientInfo) { return static_cast(const_cast(clientInfo))->decidePolicyForNewWindowAction(page, frame, navigationAction, request, frameName, userData); } WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForResponse(WKBundlePageRef page, WKBundleFrameRef frame, WKURLResponseRef response, WKURLRequestRef request, WKTypeRef* userData, const void* clientInfo) { return static_cast(const_cast(clientInfo))->decidePolicyForResponse(page, frame, response, request, userData); } void InjectedBundlePage::unableToImplementPolicy(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef* userData, const void* clientInfo) { static_cast(const_cast(clientInfo))->unableToImplementPolicy(page, frame, error, userData); } WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNavigationAction(WKBundlePageRef, WKBundleFrameRef, WKBundleNavigationActionRef, WKURLRequestRef request, WKTypeRef*) { return WKBundlePagePolicyActionUse; } WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNewWindowAction(WKBundlePageRef, WKBundleFrameRef, WKBundleNavigationActionRef, WKURLRequestRef, WKStringRef, WKTypeRef*) { return WKBundlePagePolicyActionUse; } WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForResponse(WKBundlePageRef, WKBundleFrameRef, WKURLResponseRef, WKURLRequestRef, WKTypeRef*) { return WKBundlePagePolicyActionUse; } void InjectedBundlePage::unableToImplementPolicy(WKBundlePageRef, WKBundleFrameRef, WKErrorRef, WKTypeRef*) { } // UI Client Callbacks void InjectedBundlePage::willAddMessageToConsole(WKBundlePageRef page, WKStringRef message, uint32_t lineNumber, const void *clientInfo) { static_cast(const_cast(clientInfo))->willAddMessageToConsole(message, lineNumber); } void InjectedBundlePage::willSetStatusbarText(WKBundlePageRef page, WKStringRef statusbarText, const void *clientInfo) { static_cast(const_cast(clientInfo))->willSetStatusbarText(statusbarText); } void InjectedBundlePage::willRunJavaScriptAlert(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo) { static_cast(const_cast(clientInfo))->willRunJavaScriptAlert(message, frame); } void InjectedBundlePage::willRunJavaScriptConfirm(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo) { return static_cast(const_cast(clientInfo))->willRunJavaScriptConfirm(message, frame); } void InjectedBundlePage::willRunJavaScriptPrompt(WKBundlePageRef page, WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef frame, const void *clientInfo) { static_cast(const_cast(clientInfo))->willRunJavaScriptPrompt(message, defaultValue, frame); } static string lastFileURLPathComponent(const string& path) { size_t pos = path.find("file://"); ASSERT(string::npos != pos); string tmpPath = path.substr(pos + 7); if (tmpPath.empty()) return tmpPath; // Remove the trailing delimiter if (tmpPath[tmpPath.length() - 1] == '/') tmpPath.erase(tmpPath.length() - 1); pos = tmpPath.rfind('/'); if (string::npos != pos) return tmpPath.substr(pos + 1); return tmpPath; } void InjectedBundlePage::willAddMessageToConsole(WKStringRef message, uint32_t lineNumber) { if (!InjectedBundle::shared().isTestRunning()) return; string messageString = toSTD(message); size_t fileProtocolStart = messageString.find("file://"); if (fileProtocolStart != string::npos) // FIXME: The code below does not handle additional text after url nor multiple urls. This matches DumpRenderTree implementation. messageString = messageString.substr(0, fileProtocolStart) + lastFileURLPathComponent(messageString.substr(fileProtocolStart)); InjectedBundle::shared().os() << "CONSOLE MESSAGE: line " << lineNumber << ": " << messageString << "\n"; } void InjectedBundlePage::willSetStatusbarText(WKStringRef statusbarText) { if (!InjectedBundle::shared().isTestRunning()) return; if (!InjectedBundle::shared().layoutTestController()->shouldDumpStatusCallbacks()) return; InjectedBundle::shared().os() << "UI DELEGATE STATUS CALLBACK: setStatusText:" << statusbarText << "\n"; } void InjectedBundlePage::willRunJavaScriptAlert(WKStringRef message, WKBundleFrameRef) { if (!InjectedBundle::shared().isTestRunning()) return; InjectedBundle::shared().os() << "ALERT: " << message << "\n"; } void InjectedBundlePage::willRunJavaScriptConfirm(WKStringRef message, WKBundleFrameRef) { if (!InjectedBundle::shared().isTestRunning()) return; InjectedBundle::shared().os() << "CONFIRM: " << message << "\n"; } void InjectedBundlePage::willRunJavaScriptPrompt(WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef) { InjectedBundle::shared().os() << "PROMPT: " << message << ", default text: " << defaultValue << "\n"; } // Editor Client Callbacks bool InjectedBundlePage::shouldBeginEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo) { return static_cast(const_cast(clientInfo))->shouldBeginEditing(range); } bool InjectedBundlePage::shouldEndEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo) { return static_cast(const_cast(clientInfo))->shouldEndEditing(range); } bool InjectedBundlePage::shouldInsertNode(WKBundlePageRef page, WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo) { return static_cast(const_cast(clientInfo))->shouldInsertNode(node, rangeToReplace, action); } bool InjectedBundlePage::shouldInsertText(WKBundlePageRef page, WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo) { return static_cast(const_cast(clientInfo))->shouldInsertText(text, rangeToReplace, action); } bool InjectedBundlePage::shouldDeleteRange(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo) { return static_cast(const_cast(clientInfo))->shouldDeleteRange(range); } bool InjectedBundlePage::shouldChangeSelectedRange(WKBundlePageRef page, WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting, const void* clientInfo) { return static_cast(const_cast(clientInfo))->shouldChangeSelectedRange(fromRange, toRange, affinity, stillSelecting); } bool InjectedBundlePage::shouldApplyStyle(WKBundlePageRef page, WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range, const void* clientInfo) { return static_cast(const_cast(clientInfo))->shouldApplyStyle(style, range); } void InjectedBundlePage::didBeginEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo) { static_cast(const_cast(clientInfo))->didBeginEditing(notificationName); } void InjectedBundlePage::didEndEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo) { static_cast(const_cast(clientInfo))->didEndEditing(notificationName); } void InjectedBundlePage::didChange(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo) { static_cast(const_cast(clientInfo))->didChange(notificationName); } void InjectedBundlePage::didChangeSelection(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo) { static_cast(const_cast(clientInfo))->didChangeSelection(notificationName); } bool InjectedBundlePage::shouldBeginEditing(WKBundleRangeHandleRef range) { if (!InjectedBundle::shared().isTestRunning()) return true; if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks()) InjectedBundle::shared().os() << "EDITING DELEGATE: shouldBeginEditingInDOMRange:" << toStr(m_page, m_world.get(), range) << "\n"; return InjectedBundle::shared().layoutTestController()->shouldAllowEditing(); } bool InjectedBundlePage::shouldEndEditing(WKBundleRangeHandleRef range) { if (!InjectedBundle::shared().isTestRunning()) return true; if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks()) InjectedBundle::shared().os() << "EDITING DELEGATE: shouldEndEditingInDOMRange:" << toStr(m_page, m_world.get(), range) << "\n"; return InjectedBundle::shared().layoutTestController()->shouldAllowEditing(); } bool InjectedBundlePage::shouldInsertNode(WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action) { if (!InjectedBundle::shared().isTestRunning()) return true; static const char* insertactionstring[] = { "WebViewInsertActionTyped", "WebViewInsertActionPasted", "WebViewInsertActionDropped", }; if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks()) InjectedBundle::shared().os() << "EDITING DELEGATE: shouldInsertNode:" << dumpPath(m_page, m_world.get(), node) << " replacingDOMRange:" << toStr(m_page, m_world.get(), rangeToReplace) << " givenAction:" << insertactionstring[action] << "\n"; return InjectedBundle::shared().layoutTestController()->shouldAllowEditing(); } bool InjectedBundlePage::shouldInsertText(WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action) { if (!InjectedBundle::shared().isTestRunning()) return true; static const char *insertactionstring[] = { "WebViewInsertActionTyped", "WebViewInsertActionPasted", "WebViewInsertActionDropped", }; if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks()) InjectedBundle::shared().os() << "EDITING DELEGATE: shouldInsertText:" << text << " replacingDOMRange:" << toStr(m_page, m_world.get(), rangeToReplace) << " givenAction:" << insertactionstring[action] << "\n"; return InjectedBundle::shared().layoutTestController()->shouldAllowEditing(); } bool InjectedBundlePage::shouldDeleteRange(WKBundleRangeHandleRef range) { if (!InjectedBundle::shared().isTestRunning()) return true; if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks()) InjectedBundle::shared().os() << "EDITING DELEGATE: shouldDeleteDOMRange:" << toStr(m_page, m_world.get(), range) << "\n"; return InjectedBundle::shared().layoutTestController()->shouldAllowEditing(); } bool InjectedBundlePage::shouldChangeSelectedRange(WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting) { if (!InjectedBundle::shared().isTestRunning()) return true; static const char *affinitystring[] = { "NSSelectionAffinityUpstream", "NSSelectionAffinityDownstream" }; static const char *boolstring[] = { "FALSE", "TRUE" }; if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks()) InjectedBundle::shared().os() << "EDITING DELEGATE: shouldChangeSelectedDOMRange:" << toStr(m_page, m_world.get(), fromRange) << " toDOMRange:" << toStr(m_page, m_world.get(), toRange) << " affinity:" << affinitystring[affinity] << " stillSelecting:" << boolstring[stillSelecting] << "\n"; return InjectedBundle::shared().layoutTestController()->shouldAllowEditing(); } bool InjectedBundlePage::shouldApplyStyle(WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range) { if (!InjectedBundle::shared().isTestRunning()) return true; if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks()) InjectedBundle::shared().os() << "EDITING DELEGATE: shouldApplyStyle:" << style << " toElementsInDOMRange:" << toStr(m_page, m_world.get(), range) << "\n"; return InjectedBundle::shared().layoutTestController()->shouldAllowEditing(); } void InjectedBundlePage::didBeginEditing(WKStringRef notificationName) { if (!InjectedBundle::shared().isTestRunning()) return; if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks()) InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidBeginEditing:" << notificationName << "\n"; } void InjectedBundlePage::didEndEditing(WKStringRef notificationName) { if (!InjectedBundle::shared().isTestRunning()) return; if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks()) InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidEndEditing:" << notificationName << "\n"; } void InjectedBundlePage::didChange(WKStringRef notificationName) { if (!InjectedBundle::shared().isTestRunning()) return; if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks()) InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidChange:" << notificationName << "\n"; } void InjectedBundlePage::didChangeSelection(WKStringRef notificationName) { if (!InjectedBundle::shared().isTestRunning()) return; if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks()) InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidChangeSelection:" << notificationName << "\n"; } #if ENABLE(FULLSCREEN_API) bool InjectedBundlePage::supportsFullScreen(WKBundlePageRef pageRef, WKFullScreenKeyboardRequestType requestType) { if (InjectedBundle::shared().layoutTestController()->shouldDumpFullScreenCallbacks()) InjectedBundle::shared().os() << "supportsFullScreen() == true\n"; return true; } void InjectedBundlePage::enterFullScreenForElement(WKBundlePageRef pageRef, WKBundleNodeHandleRef elementRef) { if (InjectedBundle::shared().layoutTestController()->shouldDumpFullScreenCallbacks()) InjectedBundle::shared().os() << "enterFullScreenForElement()\n"; WKBundlePageWillEnterFullScreen(pageRef); WKBundlePageDidEnterFullScreen(pageRef); } void InjectedBundlePage::exitFullScreenForElement(WKBundlePageRef pageRef, WKBundleNodeHandleRef elementRef) { if (InjectedBundle::shared().layoutTestController()->shouldDumpFullScreenCallbacks()) InjectedBundle::shared().os() << "exitFullScreenForElement()\n"; WKBundlePageWillExitFullScreen(pageRef); WKBundlePageDidExitFullScreen(pageRef); } #endif static bool compareByTargetName(WKBundleBackForwardListItemRef item1, WKBundleBackForwardListItemRef item2) { return toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item1))) < toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item2))); } static void dumpBackForwardListItem(WKBundleBackForwardListItemRef item, unsigned indent, bool isCurrentItem) { unsigned column = 0; if (isCurrentItem) { InjectedBundle::shared().os() << "curr->"; column = 6; } for (unsigned i = column; i < indent; i++) InjectedBundle::shared().os() << ' '; string url = toSTD(adoptWK(WKURLCopyString(adoptWK(WKBundleBackForwardListItemCopyURL(item)).get()))); if (hasPrefix(url, "file:")) { string directoryName = "/LayoutTests/"; size_t start = url.find(directoryName); if (start == string::npos) start = 0; else start += directoryName.size(); InjectedBundle::shared().os() << "(file test):" << url.substr(start); } else InjectedBundle::shared().os() << url; string target = toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item))); if (target.length()) InjectedBundle::shared().os() << " (in frame \"" << target << "\")"; // FIXME: Need WKBackForwardListItemIsTargetItem. if (WKBundleBackForwardListItemIsTargetItem(item)) InjectedBundle::shared().os() << " **nav target**"; InjectedBundle::shared().os() << '\n'; if (WKRetainPtr kids = adoptWK(WKBundleBackForwardListItemCopyChildren(item))) { // Sort to eliminate arbitrary result ordering which defeats reproducible testing. size_t size = WKArrayGetSize(kids.get()); Vector sortedKids(size); for (size_t i = 0; i < size; ++i) sortedKids[i] = static_cast(WKArrayGetItemAtIndex(kids.get(), i)); stable_sort(sortedKids.begin(), sortedKids.end(), compareByTargetName); for (size_t i = 0; i < size; ++i) dumpBackForwardListItem(sortedKids[i], indent + 4, false); } } void InjectedBundlePage::dumpBackForwardList() { InjectedBundle::shared().os() << "\n============== Back Forward List ==============\n"; WKBundleBackForwardListRef list = WKBundlePageGetBackForwardList(m_page); // Print out all items in the list after m_previousTestBackForwardListItem. // Gather items from the end of the list, then print them out from oldest to newest. Vector > itemsToPrint; for (unsigned i = WKBundleBackForwardListGetForwardListCount(list); i; --i) { WKRetainPtr item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i)); // Something is wrong if the item from the last test is in the forward part of the list. ASSERT(!WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get())); itemsToPrint.append(item); } ASSERT(!WKBundleBackForwardListItemIsSame(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0)).get(), m_previousTestBackForwardListItem.get())); itemsToPrint.append(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0))); int currentItemIndex = itemsToPrint.size() - 1; int backListCount = WKBundleBackForwardListGetBackListCount(list); for (int i = -1; i >= -backListCount; --i) { WKRetainPtr item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i)); if (WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get())) break; itemsToPrint.append(item); } for (int i = itemsToPrint.size() - 1; i >= 0; i--) dumpBackForwardListItem(itemsToPrint[i].get(), 8, i == currentItemIndex); InjectedBundle::shared().os() << "===============================================\n"; } } // namespace WTR