/* * Copyright (C) 2010 Apple Inc. All rights reserved. * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies) * * 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 "QtBuiltinBundlePage.h" #include "QtBuiltinBundle.h" #include "WKArray.h" #include "WKBundleFrame.h" #include "WKRetainPtr.h" #include "WKString.h" #include "WKStringPrivate.h" #include "WKStringQt.h" #include #include namespace WebKit { QtBuiltinBundlePage::QtBuiltinBundlePage(QtBuiltinBundle* bundle, WKBundlePageRef page) : m_bundle(bundle) , m_page(page) , m_navigatorQtObject(0) , m_navigatorQtObjectEnabled(false) { WKBundlePageLoaderClient loaderClient = { kWKBundlePageLoaderClientCurrentVersion, this, 0, // didStartProvisionalLoadForFrame 0, // didReceiveServerRedirectForProvisionalLoadForFrame 0, // didFailProvisionalLoadWithErrorForFrame 0, // didCommitLoadForFrame 0, // didFinishDocumentLoadForFrame 0, // didFinishLoadForFrame 0, // didFailLoadWithErrorForFrame 0, // didSameDocumentNavigationForFrame 0, // didReceiveTitleForFrame 0, // didFirstLayoutForFrame 0, // didFirstVisuallyNonEmptyLayoutForFrame 0, // didRemoveFrameFromHierarchy 0, // didDisplayInsecureContentForFrame 0, // didRunInsecureContentForFrame didClearWindowForFrame, 0, // didCancelClientRedirectForFrame 0, // willPerformClientRedirectForFrame 0, // didHandleOnloadEventsForFrame 0, // didLayoutForFrame 0, // didNewFirstVisuallyNonEmptyLayoutForFrame 0, // didDetectXSSForFrame 0, // shouldGoToBackForwardListItem 0, // didCreateGlobalObjectForFrame 0, // willDisconnectDOMWindowExtensionFromGlobalObject 0, // didReconnectDOMWindowExtensionToGlobalObject 0, // willDestroyGlobalObjectForDOMWindowExtension 0, // didFinishProgress 0, // shouldForceUniversalAccessFromLocalURL 0, // didReceiveIntentForFrame 0, // registerIntentServiceForFrame 0, // didLayout 0, // featuresUsedInPage 0, // willLoadURLRequest 0, // willLoadDataRequest }; WKBundlePageSetPageLoaderClient(m_page, &loaderClient); } QtBuiltinBundlePage::~QtBuiltinBundlePage() { if (!m_navigatorQtObject) return; WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page); JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame); JSValueUnprotect(context, m_navigatorQtObject); } void QtBuiltinBundlePage::didClearWindowForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void* clientInfo) { static_cast(const_cast(clientInfo))->didClearWindowForFrame(frame, world); } static JSValueRef qt_postMessageCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef*) { // FIXME: should it work regardless of the thisObject? if (argumentCount < 1 || !JSValueIsString(context, arguments[0])) return JSValueMakeUndefined(context); QtBuiltinBundlePage* bundlePage = reinterpret_cast(JSObjectGetPrivate(thisObject)); ASSERT(bundlePage); // FIXME: needed? if (!bundlePage->navigatorQtObjectEnabled()) return JSValueMakeUndefined(context); JSRetainPtr jsContents = JSValueToStringCopy(context, arguments[0], 0); WKRetainPtr contents(AdoptWK, WKStringCreateWithJSString(jsContents.get())); bundlePage->postMessageFromNavigatorQtObject(contents.get()); return JSValueMakeUndefined(context); } void QtBuiltinBundlePage::didClearWindowForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world) { if (!WKBundleFrameIsMainFrame(frame) || WKBundleScriptWorldNormalWorld() != world) return; JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world); registerNavigatorQtObject(context); } void QtBuiltinBundlePage::postMessageFromNavigatorQtObject(WKStringRef contents) { static WKStringRef messageName = WKStringCreateWithUTF8CString("MessageFromNavigatorQtObject"); WKTypeRef body[] = { page(), contents }; WKRetainPtr messageBody(AdoptWK, WKArrayCreate(body, sizeof(body) / sizeof(WKTypeRef))); WKBundlePostMessage(m_bundle->toRef(), messageName, messageBody.get()); } static JSObjectRef createWrappedMessage(JSGlobalContextRef context, WKStringRef data) { static JSStringRef dataName = JSStringCreateWithUTF8CString("data"); JSRetainPtr jsData = WKStringCopyJSString(data); JSObjectRef wrappedMessage = JSObjectMake(context, 0, 0); JSObjectSetProperty(context, wrappedMessage, dataName, JSValueMakeString(context, jsData.get()), kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly, 0); return wrappedMessage; } void QtBuiltinBundlePage::didReceiveMessageToNavigatorQtObject(WKStringRef contents) { static JSStringRef onmessageName = JSStringCreateWithUTF8CString("onmessage"); if (!m_navigatorQtObject) return; WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page); JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame); JSValueRef onmessageValue = JSObjectGetProperty(context, m_navigatorQtObject, onmessageName, 0); if (!JSValueIsObject(context, onmessageValue)) return; JSObjectRef onmessageFunction = JSValueToObject(context, onmessageValue, 0); if (!JSObjectIsFunction(context, onmessageFunction)) return; JSObjectRef wrappedMessage = createWrappedMessage(context, contents); JSObjectCallAsFunction(context, onmessageFunction, 0, 1, &wrappedMessage, 0); } void QtBuiltinBundlePage::setNavigatorQtObjectEnabled(bool enabled) { if (enabled == m_navigatorQtObjectEnabled) return; // Note that this will take effect only after the next page load. m_navigatorQtObjectEnabled = enabled; } void QtBuiltinBundlePage::registerNavigatorQtObject(JSGlobalContextRef context) { static JSStringRef postMessageName = JSStringCreateWithUTF8CString("postMessage"); static JSStringRef navigatorName = JSStringCreateWithUTF8CString("navigator"); static JSStringRef qtName = JSStringCreateWithUTF8CString("qt"); if (m_navigatorQtObject) JSValueUnprotect(context, m_navigatorQtObject); m_navigatorQtObject = JSObjectMake(context, navigatorQtObjectClass(), this); JSValueProtect(context, m_navigatorQtObject); JSObjectRef postMessage = JSObjectMakeFunctionWithCallback(context, postMessageName, qt_postMessageCallback); JSObjectSetProperty(context, m_navigatorQtObject, postMessageName, postMessage, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly, 0); JSValueRef navigatorValue = JSObjectGetProperty(context, JSContextGetGlobalObject(context), navigatorName, 0); if (!JSValueIsObject(context, navigatorValue)) return; JSObjectRef navigatorObject = JSValueToObject(context, navigatorValue, 0); JSObjectSetProperty(context, navigatorObject, qtName, m_navigatorQtObject, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly, 0); } JSClassRef QtBuiltinBundlePage::navigatorQtObjectClass() { static JSClassRef classRef = 0; if (!classRef) { const JSClassDefinition navigatorQtObjectClass = kJSClassDefinitionEmpty; classRef = JSClassCreate(&navigatorQtObjectClass); } return classRef; } } // namespace WebKit