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