1/* 2 * Copyright (C) 2010 Apple Inc. All rights reserved. 3 * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies) 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 24 * THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "QtBuiltinBundlePage.h" 29 30#include "QtBuiltinBundle.h" 31#include "WKArray.h" 32#include "WKBundleFrame.h" 33#include "WKRetainPtr.h" 34#include "WKString.h" 35#include "WKStringPrivate.h" 36#include "WKStringQt.h" 37#include <JavaScript.h> 38#include <JavaScriptCore/JSRetainPtr.h> 39 40namespace WebKit { 41 42QtBuiltinBundlePage::QtBuiltinBundlePage(QtBuiltinBundle* bundle, WKBundlePageRef page) 43 : m_bundle(bundle) 44 , m_page(page) 45 , m_navigatorQtObject(0) 46 , m_navigatorQtObjectEnabled(false) 47{ 48 WKBundlePageLoaderClient loaderClient = { 49 kWKBundlePageLoaderClientCurrentVersion, 50 this, 51 0, // didStartProvisionalLoadForFrame 52 0, // didReceiveServerRedirectForProvisionalLoadForFrame 53 0, // didFailProvisionalLoadWithErrorForFrame 54 0, // didCommitLoadForFrame 55 0, // didFinishDocumentLoadForFrame 56 0, // didFinishLoadForFrame 57 0, // didFailLoadWithErrorForFrame 58 0, // didSameDocumentNavigationForFrame 59 0, // didReceiveTitleForFrame 60 0, // didFirstLayoutForFrame 61 0, // didFirstVisuallyNonEmptyLayoutForFrame 62 0, // didRemoveFrameFromHierarchy 63 0, // didDisplayInsecureContentForFrame 64 0, // didRunInsecureContentForFrame 65 didClearWindowForFrame, 66 0, // didCancelClientRedirectForFrame 67 0, // willPerformClientRedirectForFrame 68 0, // didHandleOnloadEventsForFrame 69 0, // didLayoutForFrame 70 0, // didNewFirstVisuallyNonEmptyLayoutForFrame 71 0, // didDetectXSSForFrame 72 0, // shouldGoToBackForwardListItem 73 0, // didCreateGlobalObjectForFrame 74 0, // willDisconnectDOMWindowExtensionFromGlobalObject 75 0, // didReconnectDOMWindowExtensionToGlobalObject 76 0, // willDestroyGlobalObjectForDOMWindowExtension 77 0, // didFinishProgress 78 0, // shouldForceUniversalAccessFromLocalURL 79 0, // didReceiveIntentForFrame 80 0, // registerIntentServiceForFrame 81 0, // didLayout 82 0, // featuresUsedInPage 83 0, // willLoadURLRequest 84 0, // willLoadDataRequest 85 }; 86 WKBundlePageSetPageLoaderClient(m_page, &loaderClient); 87} 88 89QtBuiltinBundlePage::~QtBuiltinBundlePage() 90{ 91 if (!m_navigatorQtObject) 92 return; 93 WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page); 94 JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame); 95 JSValueUnprotect(context, m_navigatorQtObject); 96} 97 98void QtBuiltinBundlePage::didClearWindowForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void* clientInfo) 99{ 100 static_cast<QtBuiltinBundlePage*>(const_cast<void*>(clientInfo))->didClearWindowForFrame(frame, world); 101} 102 103static JSValueRef qt_postMessageCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef*) 104{ 105 // FIXME: should it work regardless of the thisObject? 106 107 if (argumentCount < 1 || !JSValueIsString(context, arguments[0])) 108 return JSValueMakeUndefined(context); 109 110 QtBuiltinBundlePage* bundlePage = reinterpret_cast<QtBuiltinBundlePage*>(JSObjectGetPrivate(thisObject)); 111 ASSERT(bundlePage); 112 113 // FIXME: needed? 114 if (!bundlePage->navigatorQtObjectEnabled()) 115 return JSValueMakeUndefined(context); 116 117 JSRetainPtr<JSStringRef> jsContents = JSValueToStringCopy(context, arguments[0], 0); 118 WKRetainPtr<WKStringRef> contents(AdoptWK, WKStringCreateWithJSString(jsContents.get())); 119 bundlePage->postMessageFromNavigatorQtObject(contents.get()); 120 return JSValueMakeUndefined(context); 121} 122 123void QtBuiltinBundlePage::didClearWindowForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world) 124{ 125 if (!WKBundleFrameIsMainFrame(frame) || WKBundleScriptWorldNormalWorld() != world) 126 return; 127 JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world); 128 registerNavigatorQtObject(context); 129} 130 131void QtBuiltinBundlePage::postMessageFromNavigatorQtObject(WKStringRef contents) 132{ 133 static WKStringRef messageName = WKStringCreateWithUTF8CString("MessageFromNavigatorQtObject"); 134 WKTypeRef body[] = { page(), contents }; 135 WKRetainPtr<WKArrayRef> messageBody(AdoptWK, WKArrayCreate(body, sizeof(body) / sizeof(WKTypeRef))); 136 WKBundlePostMessage(m_bundle->toRef(), messageName, messageBody.get()); 137} 138 139static JSObjectRef createWrappedMessage(JSGlobalContextRef context, WKStringRef data) 140{ 141 static JSStringRef dataName = JSStringCreateWithUTF8CString("data"); 142 143 JSRetainPtr<JSStringRef> jsData = WKStringCopyJSString(data); 144 JSObjectRef wrappedMessage = JSObjectMake(context, 0, 0); 145 JSObjectSetProperty(context, wrappedMessage, dataName, JSValueMakeString(context, jsData.get()), kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly, 0); 146 return wrappedMessage; 147} 148 149void QtBuiltinBundlePage::didReceiveMessageToNavigatorQtObject(WKStringRef contents) 150{ 151 static JSStringRef onmessageName = JSStringCreateWithUTF8CString("onmessage"); 152 153 if (!m_navigatorQtObject) 154 return; 155 156 WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page); 157 JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame); 158 159 JSValueRef onmessageValue = JSObjectGetProperty(context, m_navigatorQtObject, onmessageName, 0); 160 if (!JSValueIsObject(context, onmessageValue)) 161 return; 162 163 JSObjectRef onmessageFunction = JSValueToObject(context, onmessageValue, 0); 164 if (!JSObjectIsFunction(context, onmessageFunction)) 165 return; 166 167 JSObjectRef wrappedMessage = createWrappedMessage(context, contents); 168 JSObjectCallAsFunction(context, onmessageFunction, 0, 1, &wrappedMessage, 0); 169} 170 171void QtBuiltinBundlePage::setNavigatorQtObjectEnabled(bool enabled) 172{ 173 if (enabled == m_navigatorQtObjectEnabled) 174 return; 175 // Note that this will take effect only after the next page load. 176 m_navigatorQtObjectEnabled = enabled; 177} 178 179void QtBuiltinBundlePage::registerNavigatorQtObject(JSGlobalContextRef context) 180{ 181 static JSStringRef postMessageName = JSStringCreateWithUTF8CString("postMessage"); 182 static JSStringRef navigatorName = JSStringCreateWithUTF8CString("navigator"); 183 static JSStringRef qtName = JSStringCreateWithUTF8CString("qt"); 184 185 if (m_navigatorQtObject) 186 JSValueUnprotect(context, m_navigatorQtObject); 187 m_navigatorQtObject = JSObjectMake(context, navigatorQtObjectClass(), this); 188 JSValueProtect(context, m_navigatorQtObject); 189 190 JSObjectRef postMessage = JSObjectMakeFunctionWithCallback(context, postMessageName, qt_postMessageCallback); 191 JSObjectSetProperty(context, m_navigatorQtObject, postMessageName, postMessage, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly, 0); 192 193 JSValueRef navigatorValue = JSObjectGetProperty(context, JSContextGetGlobalObject(context), navigatorName, 0); 194 if (!JSValueIsObject(context, navigatorValue)) 195 return; 196 JSObjectRef navigatorObject = JSValueToObject(context, navigatorValue, 0); 197 JSObjectSetProperty(context, navigatorObject, qtName, m_navigatorQtObject, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly, 0); 198} 199 200JSClassRef QtBuiltinBundlePage::navigatorQtObjectClass() 201{ 202 static JSClassRef classRef = 0; 203 if (!classRef) { 204 const JSClassDefinition navigatorQtObjectClass = kJSClassDefinitionEmpty; 205 classRef = JSClassCreate(&navigatorQtObjectClass); 206 } 207 return classRef; 208} 209 210} // namespace WebKit 211