1 | /* |
2 | * Copyright (C) 2012 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 | * THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | |
28 | #if WK_HAVE_C_SPI |
29 | |
30 | #include "InjectedBundleTest.h" |
31 | #include <WebKit/WKBundleDOMWindowExtension.h> |
32 | #include <WebKit/WKBundleFrame.h> |
33 | #include <WebKit/WKBundlePage.h> |
34 | #include <WebKit/WKBundlePageGroup.h> |
35 | #include <WebKit/WKBundlePrivate.h> |
36 | #include <WebKit/WKBundleScriptWorld.h> |
37 | #include <WebKit/WKRetainPtr.h> |
38 | #include <wtf/HashMap.h> |
39 | #include <assert.h> |
40 | |
41 | namespace TestWebKitAPI { |
42 | |
43 | static void didFinishLoadForFrameCallback(WKBundlePageRef, WKBundleFrameRef, WKTypeRef*, const void* clientInfo); |
44 | static void globalObjectIsAvailableForFrameCallback(WKBundlePageRef, WKBundleFrameRef, WKBundleScriptWorldRef, const void* clientInfo); |
45 | static void willDisconnectDOMWindowExtensionFromGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef, const void* clientInfo); |
46 | static void didReconnectDOMWindowExtensionToGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef, const void* clientInfo); |
47 | static void willDestroyGlobalObjectForDOMWindowExtensionCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef, const void* clientInfo); |
48 | |
49 | |
50 | enum ExtensionState { |
51 | Uncreated = 0, Connected, Disconnected, Destroyed, Removed |
52 | }; |
53 | |
54 | const char* stateNames[5] = { |
55 | "Uncreated" , |
56 | "Connected" , |
57 | "Disconnected" , |
58 | "Destroyed" , |
59 | "Removed" |
60 | }; |
61 | |
62 | typedef struct { |
63 | const char* name; |
64 | ExtensionState state; |
65 | } ExtensionRecord; |
66 | |
67 | class DOMWindowExtensionBasic : public InjectedBundleTest { |
68 | public: |
69 | DOMWindowExtensionBasic(const std::string& identifier); |
70 | |
71 | virtual void initialize(WKBundleRef, WKTypeRef userData); |
72 | virtual void didCreatePage(WKBundleRef, WKBundlePageRef); |
73 | virtual void willDestroyPage(WKBundleRef, WKBundlePageRef); |
74 | |
75 | void globalObjectIsAvailableForFrame(WKBundleFrameRef, WKBundleScriptWorldRef); |
76 | void willDisconnectDOMWindowExtensionFromGlobalObject(WKBundleDOMWindowExtensionRef); |
77 | void didReconnectDOMWindowExtensionToGlobalObject(WKBundleDOMWindowExtensionRef); |
78 | void willDestroyGlobalObjectForDOMWindowExtension(WKBundleDOMWindowExtensionRef); |
79 | |
80 | void frameLoadFinished(WKBundleFrameRef); |
81 | |
82 | private: |
83 | void updateExtensionStateRecord(WKBundleDOMWindowExtensionRef, ExtensionState); |
84 | void sendExtensionStateMessage(); |
85 | void sendBundleMessage(const char*); |
86 | |
87 | WKBundleRef m_bundle; |
88 | ExtensionRecord m_extensionRecords[6]; |
89 | HashMap<WKBundleDOMWindowExtensionRef, int> m_extensionToRecordMap; |
90 | bool m_finishedOneMainFrameLoad; |
91 | }; |
92 | |
93 | static InjectedBundleTest::Register<DOMWindowExtensionBasic> registrar("DOMWindowExtensionBasic" ); |
94 | |
95 | DOMWindowExtensionBasic::DOMWindowExtensionBasic(const std::string& identifier) |
96 | : InjectedBundleTest(identifier) |
97 | , m_finishedOneMainFrameLoad(false) |
98 | { |
99 | m_extensionRecords[0].name = "First page, main frame, standard world" ; |
100 | m_extensionRecords[1].name = "First page, main frame, non-standard world" ; |
101 | m_extensionRecords[2].name = "First page, subframe, standard world" ; |
102 | m_extensionRecords[3].name = "First page, subframe, non-standard world" ; |
103 | m_extensionRecords[4].name = "Second page, main frame, standard world" ; |
104 | m_extensionRecords[5].name = "Second page, main frame, non-standard world" ; |
105 | |
106 | for (size_t i = 0; i < 6; ++i) |
107 | m_extensionRecords[i].state = Uncreated; |
108 | } |
109 | |
110 | void DOMWindowExtensionBasic::frameLoadFinished(WKBundleFrameRef frame) |
111 | { |
112 | bool mainFrame = !WKBundleFrameGetParentFrame(frame); |
113 | if (mainFrame) |
114 | m_finishedOneMainFrameLoad = true; |
115 | |
116 | char body[16384]; |
117 | sprintf(body, "%s finished loading" , mainFrame ? "Main frame" : "Subframe" ); |
118 | |
119 | // Only consider load finished for the main frame |
120 | const char* name = mainFrame ? "DidFinishLoadForMainFrame" : "DidFinishLoadForFrame" ; |
121 | |
122 | WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString(name)); |
123 | WKRetainPtr<WKStringRef> messageBody = adoptWK(WKStringCreateWithUTF8CString(body)); |
124 | WKBundlePostMessage(m_bundle, messageName.get(), messageBody.get()); |
125 | |
126 | sendExtensionStateMessage(); |
127 | } |
128 | |
129 | void DOMWindowExtensionBasic::sendExtensionStateMessage() |
130 | { |
131 | char body[16384]; |
132 | sprintf(body, "Extension states:\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s" , |
133 | m_extensionRecords[0].name, stateNames[m_extensionRecords[0].state], |
134 | m_extensionRecords[1].name, stateNames[m_extensionRecords[1].state], |
135 | m_extensionRecords[2].name, stateNames[m_extensionRecords[2].state], |
136 | m_extensionRecords[3].name, stateNames[m_extensionRecords[3].state], |
137 | m_extensionRecords[4].name, stateNames[m_extensionRecords[4].state], |
138 | m_extensionRecords[5].name, stateNames[m_extensionRecords[5].state]); |
139 | |
140 | WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("ExtensionStates" )); |
141 | WKRetainPtr<WKStringRef> messageBody = adoptWK(WKStringCreateWithUTF8CString(body)); |
142 | WKBundlePostMessage(m_bundle, messageName.get(), messageBody.get()); |
143 | } |
144 | |
145 | void DOMWindowExtensionBasic::initialize(WKBundleRef bundle, WKTypeRef userData) |
146 | { |
147 | assert(WKGetTypeID(userData) == WKBundlePageGroupGetTypeID()); |
148 | WKBundlePageGroupRef pageGroup = static_cast<WKBundlePageGroupRef>(userData); |
149 | |
150 | WKRetainPtr<WKStringRef> source = adoptWK(WKStringCreateWithUTF8CString("alert('Unimportant alert');" )); |
151 | WKBundleAddUserScript(bundle, pageGroup, WKBundleScriptWorldCreateWorld(), source.get(), 0, 0, 0, kWKInjectAtDocumentStart, kWKInjectInAllFrames); |
152 | } |
153 | |
154 | void DOMWindowExtensionBasic::didCreatePage(WKBundleRef bundle, WKBundlePageRef page) |
155 | { |
156 | m_bundle = bundle; |
157 | |
158 | WKBundlePageLoaderClientV1 pageLoaderClient; |
159 | memset(&pageLoaderClient, 0, sizeof(pageLoaderClient)); |
160 | |
161 | pageLoaderClient.base.version = 1; |
162 | pageLoaderClient.base.clientInfo = this; |
163 | pageLoaderClient.didFinishLoadForFrame = didFinishLoadForFrameCallback; |
164 | pageLoaderClient.globalObjectIsAvailableForFrame = globalObjectIsAvailableForFrameCallback; |
165 | pageLoaderClient.willDisconnectDOMWindowExtensionFromGlobalObject = willDisconnectDOMWindowExtensionFromGlobalObjectCallback; |
166 | pageLoaderClient.didReconnectDOMWindowExtensionToGlobalObject = didReconnectDOMWindowExtensionToGlobalObjectCallback; |
167 | pageLoaderClient.willDestroyGlobalObjectForDOMWindowExtension = willDestroyGlobalObjectForDOMWindowExtensionCallback; |
168 | |
169 | WKBundlePageSetPageLoaderClient(page, &pageLoaderClient.base); |
170 | } |
171 | |
172 | void DOMWindowExtensionBasic::willDestroyPage(WKBundleRef, WKBundlePageRef) |
173 | { |
174 | HashMap<WKBundleDOMWindowExtensionRef, int>::iterator it = m_extensionToRecordMap.begin(); |
175 | HashMap<WKBundleDOMWindowExtensionRef, int>::iterator end = m_extensionToRecordMap.end(); |
176 | for (; it != end; ++it) { |
177 | updateExtensionStateRecord(it->key, Removed); |
178 | WKRelease(it->key); |
179 | } |
180 | |
181 | m_extensionToRecordMap.clear(); |
182 | |
183 | sendExtensionStateMessage(); |
184 | sendBundleMessage("TestComplete" ); |
185 | } |
186 | |
187 | void DOMWindowExtensionBasic::updateExtensionStateRecord(WKBundleDOMWindowExtensionRef extension, ExtensionState state) |
188 | { |
189 | int index = m_extensionToRecordMap.get(extension); |
190 | m_extensionRecords[index].state = state; |
191 | } |
192 | |
193 | void DOMWindowExtensionBasic::sendBundleMessage(const char* message) |
194 | { |
195 | WKRetainPtr<WKStringRef> wkMessage = adoptWK(WKStringCreateWithUTF8CString(message)); |
196 | WKBundlePostMessage(m_bundle, wkMessage.get(), wkMessage.get()); |
197 | } |
198 | |
199 | void DOMWindowExtensionBasic::globalObjectIsAvailableForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world) |
200 | { |
201 | WKBundleDOMWindowExtensionRef extension = WKBundleDOMWindowExtensionCreate(frame, world); |
202 | |
203 | int index; |
204 | bool standard; |
205 | standard = world == WKBundleScriptWorldNormalWorld(); |
206 | |
207 | if (WKBundleFrameGetParentFrame(frame)) |
208 | index = standard ? 2 : 3; |
209 | else |
210 | index = m_finishedOneMainFrameLoad ? (standard ? 4 : 5) : (standard ? 0 : 1); |
211 | |
212 | m_extensionToRecordMap.set(extension, index); |
213 | |
214 | updateExtensionStateRecord(extension, Connected); |
215 | sendBundleMessage("GlobalObjectIsAvailableForFrame called" ); |
216 | } |
217 | |
218 | void DOMWindowExtensionBasic::willDisconnectDOMWindowExtensionFromGlobalObject(WKBundleDOMWindowExtensionRef extension) |
219 | { |
220 | updateExtensionStateRecord(extension, Disconnected); |
221 | sendBundleMessage("WillDisconnectDOMWindowExtensionFromGlobalObject called" ); |
222 | } |
223 | |
224 | void DOMWindowExtensionBasic::didReconnectDOMWindowExtensionToGlobalObject(WKBundleDOMWindowExtensionRef extension) |
225 | { |
226 | updateExtensionStateRecord(extension, Connected); |
227 | sendBundleMessage("DidReconnectDOMWindowExtensionToGlobalObject called" ); |
228 | } |
229 | |
230 | void DOMWindowExtensionBasic::willDestroyGlobalObjectForDOMWindowExtension(WKBundleDOMWindowExtensionRef) |
231 | { |
232 | } |
233 | |
234 | static void didFinishLoadForFrameCallback(WKBundlePageRef, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo) |
235 | { |
236 | ((DOMWindowExtensionBasic*)clientInfo)->frameLoadFinished(frame); |
237 | } |
238 | |
239 | static void globalObjectIsAvailableForFrameCallback(WKBundlePageRef, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void* clientInfo) |
240 | { |
241 | ((DOMWindowExtensionBasic*)clientInfo)->globalObjectIsAvailableForFrame(frame, world); |
242 | } |
243 | |
244 | static void willDisconnectDOMWindowExtensionFromGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef extension, const void* clientInfo) |
245 | { |
246 | ((DOMWindowExtensionBasic*)clientInfo)->willDisconnectDOMWindowExtensionFromGlobalObject(extension); |
247 | } |
248 | |
249 | static void didReconnectDOMWindowExtensionToGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef extension, const void* clientInfo) |
250 | { |
251 | ((DOMWindowExtensionBasic*)clientInfo)->didReconnectDOMWindowExtensionToGlobalObject(extension); |
252 | } |
253 | |
254 | static void willDestroyGlobalObjectForDOMWindowExtensionCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef extension , const void* clientInfo) |
255 | { |
256 | ((DOMWindowExtensionBasic*)clientInfo)->willDestroyGlobalObjectForDOMWindowExtension(extension); |
257 | } |
258 | |
259 | } // namespace TestWebKitAPI |
260 | |
261 | #endif |
262 | |