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 | enum ExtensionState { |
50 | Uncreated = 0, Connected, Disconnected, Destroyed, Removed |
51 | }; |
52 | |
53 | const char* states[5] = { |
54 | "Uncreated" , |
55 | "Connected" , |
56 | "Disconnected" , |
57 | "Destroyed" , |
58 | "Removed" |
59 | }; |
60 | |
61 | typedef struct { |
62 | const char* name; |
63 | ExtensionState state; |
64 | } ExtensionRecord; |
65 | |
66 | class DOMWindowExtensionNoCache : public InjectedBundleTest { |
67 | public: |
68 | DOMWindowExtensionNoCache(const std::string& identifier); |
69 | |
70 | virtual void initialize(WKBundleRef, WKTypeRef userData); |
71 | virtual void didCreatePage(WKBundleRef, WKBundlePageRef); |
72 | virtual void willDestroyPage(WKBundleRef, WKBundlePageRef); |
73 | |
74 | void globalObjectIsAvailableForFrame(WKBundleFrameRef, WKBundleScriptWorldRef); |
75 | void willDisconnectDOMWindowExtensionFromGlobalObject(WKBundleDOMWindowExtensionRef); |
76 | void didReconnectDOMWindowExtensionToGlobalObject(WKBundleDOMWindowExtensionRef); |
77 | void willDestroyGlobalObjectForDOMWindowExtension(WKBundleDOMWindowExtensionRef); |
78 | |
79 | void frameLoadFinished(WKBundleFrameRef); |
80 | |
81 | private: |
82 | void updateExtensionStateRecord(WKBundleDOMWindowExtensionRef, ExtensionState); |
83 | void sendExtensionStateMessage(); |
84 | void sendBundleMessage(const char*); |
85 | |
86 | WKBundleRef m_bundle; |
87 | ExtensionRecord m_extensionRecords[10]; |
88 | HashMap<WKBundleDOMWindowExtensionRef, int> m_extensionToRecordMap; |
89 | int m_numberMainFrameLoads; |
90 | }; |
91 | |
92 | static InjectedBundleTest::Register<DOMWindowExtensionNoCache> registrar("DOMWindowExtensionNoCache" ); |
93 | |
94 | DOMWindowExtensionNoCache::DOMWindowExtensionNoCache(const std::string& identifier) |
95 | : InjectedBundleTest(identifier) |
96 | , m_numberMainFrameLoads(0) |
97 | { |
98 | m_extensionRecords[0].name = "First page, main frame, standard world" ; |
99 | m_extensionRecords[1].name = "First page, main frame, non-standard world" ; |
100 | m_extensionRecords[2].name = "First page, subframe, standard world" ; |
101 | m_extensionRecords[3].name = "First page, subframe, non-standard world" ; |
102 | m_extensionRecords[4].name = "Second page, main frame, standard world" ; |
103 | m_extensionRecords[5].name = "Second page, main frame, non-standard world" ; |
104 | m_extensionRecords[6].name = "First page, main frame, standard world" ; |
105 | m_extensionRecords[7].name = "First page, main frame, non-standard world" ; |
106 | m_extensionRecords[8].name = "First page, subframe, standard world" ; |
107 | m_extensionRecords[9].name = "First page, subframe, non-standard world" ; |
108 | |
109 | for (size_t i = 0; i < 10; ++i) |
110 | m_extensionRecords[i].state = Uncreated; |
111 | } |
112 | |
113 | void DOMWindowExtensionNoCache::frameLoadFinished(WKBundleFrameRef frame) |
114 | { |
115 | bool mainFrame = !WKBundleFrameGetParentFrame(frame); |
116 | if (mainFrame) |
117 | m_numberMainFrameLoads++; |
118 | |
119 | char body[16384]; |
120 | sprintf(body, "%s finished loading" , mainFrame ? "Main frame" : "Subframe" ); |
121 | |
122 | // Only consider load finished for the main frame |
123 | const char* name = mainFrame ? "DidFinishLoadForMainFrame" : "DidFinishLoadForFrame" ; |
124 | |
125 | WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString(name)); |
126 | WKRetainPtr<WKStringRef> messageBody = adoptWK(WKStringCreateWithUTF8CString(body)); |
127 | WKBundlePostMessage(m_bundle, messageName.get(), messageBody.get()); |
128 | |
129 | sendExtensionStateMessage(); |
130 | } |
131 | |
132 | void DOMWindowExtensionNoCache::sendExtensionStateMessage() |
133 | { |
134 | char body[16384]; |
135 | sprintf(body, "Extension states:\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s" , |
136 | m_extensionRecords[0].name, states[m_extensionRecords[0].state], |
137 | m_extensionRecords[1].name, states[m_extensionRecords[1].state], |
138 | m_extensionRecords[2].name, states[m_extensionRecords[2].state], |
139 | m_extensionRecords[3].name, states[m_extensionRecords[3].state], |
140 | m_extensionRecords[4].name, states[m_extensionRecords[4].state], |
141 | m_extensionRecords[5].name, states[m_extensionRecords[5].state], |
142 | m_extensionRecords[6].name, states[m_extensionRecords[6].state], |
143 | m_extensionRecords[7].name, states[m_extensionRecords[7].state], |
144 | m_extensionRecords[8].name, states[m_extensionRecords[8].state], |
145 | m_extensionRecords[9].name, states[m_extensionRecords[9].state]); |
146 | |
147 | WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("ExtensionStates" )); |
148 | WKRetainPtr<WKStringRef> messageBody = adoptWK(WKStringCreateWithUTF8CString(body)); |
149 | WKBundlePostMessage(m_bundle, messageName.get(), messageBody.get()); |
150 | } |
151 | |
152 | void DOMWindowExtensionNoCache::initialize(WKBundleRef bundle, WKTypeRef userData) |
153 | { |
154 | assert(WKGetTypeID(userData) == WKBundlePageGroupGetTypeID()); |
155 | WKBundlePageGroupRef pageGroup = static_cast<WKBundlePageGroupRef>(userData); |
156 | |
157 | WKRetainPtr<WKStringRef> source = adoptWK(WKStringCreateWithUTF8CString("alert('Unimportant alert');" )); |
158 | WKBundleAddUserScript(bundle, pageGroup, WKBundleScriptWorldCreateWorld(), source.get(), 0, 0, 0, kWKInjectAtDocumentStart, kWKInjectInAllFrames); |
159 | } |
160 | |
161 | void DOMWindowExtensionNoCache::didCreatePage(WKBundleRef bundle, WKBundlePageRef page) |
162 | { |
163 | m_bundle = bundle; |
164 | |
165 | WKBundlePageLoaderClientV7 pageLoaderClient; |
166 | memset(&pageLoaderClient, 0, sizeof(pageLoaderClient)); |
167 | |
168 | pageLoaderClient.base.version = 7; |
169 | pageLoaderClient.base.clientInfo = this; |
170 | pageLoaderClient.didFinishLoadForFrame = didFinishLoadForFrameCallback; |
171 | pageLoaderClient.globalObjectIsAvailableForFrame = globalObjectIsAvailableForFrameCallback; |
172 | pageLoaderClient.willDisconnectDOMWindowExtensionFromGlobalObject = willDisconnectDOMWindowExtensionFromGlobalObjectCallback; |
173 | pageLoaderClient.didReconnectDOMWindowExtensionToGlobalObject = didReconnectDOMWindowExtensionToGlobalObjectCallback; |
174 | pageLoaderClient.willDestroyGlobalObjectForDOMWindowExtension = willDestroyGlobalObjectForDOMWindowExtensionCallback; |
175 | |
176 | WKBundlePageSetPageLoaderClient(page, &pageLoaderClient.base); |
177 | } |
178 | |
179 | void DOMWindowExtensionNoCache::willDestroyPage(WKBundleRef, WKBundlePageRef) |
180 | { |
181 | HashMap<WKBundleDOMWindowExtensionRef, int>::iterator it = m_extensionToRecordMap.begin(); |
182 | HashMap<WKBundleDOMWindowExtensionRef, int>::iterator end = m_extensionToRecordMap.end(); |
183 | for (; it != end; ++it) { |
184 | updateExtensionStateRecord(it->key, Removed); |
185 | WKRelease(it->key); |
186 | } |
187 | |
188 | m_extensionToRecordMap.clear(); |
189 | |
190 | sendExtensionStateMessage(); |
191 | sendBundleMessage("TestComplete" ); |
192 | } |
193 | |
194 | void DOMWindowExtensionNoCache::updateExtensionStateRecord(WKBundleDOMWindowExtensionRef extension, ExtensionState state) |
195 | { |
196 | int index = m_extensionToRecordMap.get(extension); |
197 | m_extensionRecords[index].state = state; |
198 | } |
199 | |
200 | void DOMWindowExtensionNoCache::sendBundleMessage(const char* message) |
201 | { |
202 | WKRetainPtr<WKStringRef> wkMessage = adoptWK(WKStringCreateWithUTF8CString(message)); |
203 | WKBundlePostMessage(m_bundle, wkMessage.get(), wkMessage.get()); |
204 | } |
205 | |
206 | void DOMWindowExtensionNoCache::globalObjectIsAvailableForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world) |
207 | { |
208 | WKBundleDOMWindowExtensionRef extension = WKBundleDOMWindowExtensionCreate(frame, world); |
209 | |
210 | int index = 0; |
211 | bool standard; |
212 | standard = world == WKBundleScriptWorldNormalWorld(); |
213 | |
214 | bool mainFrame = !WKBundleFrameGetParentFrame(frame); |
215 | switch (m_numberMainFrameLoads) { |
216 | case 0: |
217 | index = mainFrame ? (standard ? 0 : 1) : (standard ? 2 : 3); |
218 | break; |
219 | case 1: |
220 | index = standard ? 4 : 5; |
221 | break; |
222 | case 2: |
223 | index = mainFrame ? (standard ? 6 : 7) : (standard ? 8 : 9); |
224 | break; |
225 | default: |
226 | ASSERT_NOT_REACHED(); |
227 | break; |
228 | } |
229 | |
230 | m_extensionToRecordMap.set(extension, index); |
231 | |
232 | updateExtensionStateRecord(extension, Connected); |
233 | sendBundleMessage("GlobalObjectIsAvailableForFrame called" ); |
234 | } |
235 | |
236 | void DOMWindowExtensionNoCache::willDisconnectDOMWindowExtensionFromGlobalObject(WKBundleDOMWindowExtensionRef extension) |
237 | { |
238 | // No items should be going into a 0-capacity page cache. |
239 | ASSERT_NOT_REACHED(); |
240 | } |
241 | |
242 | void DOMWindowExtensionNoCache::didReconnectDOMWindowExtensionToGlobalObject(WKBundleDOMWindowExtensionRef) |
243 | { |
244 | // No items should be coming out of a 0-capacity page cache. |
245 | ASSERT_NOT_REACHED(); |
246 | } |
247 | |
248 | void DOMWindowExtensionNoCache::willDestroyGlobalObjectForDOMWindowExtension(WKBundleDOMWindowExtensionRef extension) |
249 | { |
250 | sendBundleMessage("WillDestroyDOMWindowExtensionToGlobalObject called" ); |
251 | updateExtensionStateRecord(extension, Destroyed); |
252 | m_extensionToRecordMap.remove(extension); |
253 | WKRelease(extension); |
254 | } |
255 | |
256 | static void didFinishLoadForFrameCallback(WKBundlePageRef, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo) |
257 | { |
258 | ((DOMWindowExtensionNoCache*)clientInfo)->frameLoadFinished(frame); |
259 | } |
260 | |
261 | static void globalObjectIsAvailableForFrameCallback(WKBundlePageRef, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void* clientInfo) |
262 | { |
263 | ((DOMWindowExtensionNoCache*)clientInfo)->globalObjectIsAvailableForFrame(frame, world); |
264 | } |
265 | |
266 | static void willDisconnectDOMWindowExtensionFromGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef extension, const void* clientInfo) |
267 | { |
268 | ((DOMWindowExtensionNoCache*)clientInfo)->willDisconnectDOMWindowExtensionFromGlobalObject(extension); |
269 | } |
270 | |
271 | static void didReconnectDOMWindowExtensionToGlobalObjectCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef extension, const void* clientInfo) |
272 | { |
273 | ((DOMWindowExtensionNoCache*)clientInfo)->didReconnectDOMWindowExtensionToGlobalObject(extension); |
274 | } |
275 | |
276 | static void willDestroyGlobalObjectForDOMWindowExtensionCallback(WKBundlePageRef, WKBundleDOMWindowExtensionRef extension , const void* clientInfo) |
277 | { |
278 | ((DOMWindowExtensionNoCache*)clientInfo)->willDestroyGlobalObjectForDOMWindowExtension(extension); |
279 | } |
280 | |
281 | } // namespace TestWebKitAPI |
282 | |
283 | #endif |
284 | |