|
1 /* |
|
2 * Copyright (C) 2010 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 "InjectedBundlePage.h" |
|
27 |
|
28 #include "InjectedBundle.h" |
|
29 #include <JavaScriptCore/JSRetainPtr.h> |
|
30 #include <WebKit2/WKArray.h> |
|
31 #include <WebKit2/WKBundleFrame.h> |
|
32 #include <WebKit2/WKBundlePagePrivate.h> |
|
33 #include <WebKit2/WKRetainPtr.h> |
|
34 #include <WebKit2/WKString.h> |
|
35 #include <WebKit2/WKStringCF.h> |
|
36 #include <wtf/PassOwnPtr.h> |
|
37 #include <wtf/RetainPtr.h> |
|
38 #include <wtf/Vector.h> |
|
39 |
|
40 namespace WTR { |
|
41 |
|
42 static PassOwnPtr<Vector<char> > WKStringToUTF8(WKStringRef wkStringRef) |
|
43 { |
|
44 RetainPtr<CFStringRef> cfString(AdoptCF, WKStringCopyCFString(0, wkStringRef)); |
|
45 CFIndex bufferLength = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfString.get()), kCFStringEncodingUTF8) + 1; |
|
46 OwnPtr<Vector<char> > buffer(new Vector<char>(bufferLength)); |
|
47 if (!CFStringGetCString(cfString.get(), buffer->data(), bufferLength, kCFStringEncodingUTF8)) { |
|
48 buffer->shrink(1); |
|
49 (*buffer)[0] = 0; |
|
50 } else |
|
51 buffer->shrink(strlen(buffer->data()) + 1); |
|
52 return buffer.release(); |
|
53 } |
|
54 |
|
55 InjectedBundlePage::InjectedBundlePage(WKBundlePageRef page) |
|
56 : m_page(page) |
|
57 , m_isLoading(false) |
|
58 { |
|
59 WKBundlePageLoaderClient loaderClient = { |
|
60 0, |
|
61 this, |
|
62 _didStartProvisionalLoadForFrame, |
|
63 _didReceiveServerRedirectForProvisionalLoadForFrame, |
|
64 _didFailProvisionalLoadWithErrorForFrame, |
|
65 _didCommitLoadForFrame, |
|
66 _didFinishLoadForFrame, |
|
67 _didFailLoadWithErrorForFrame, |
|
68 _didReceiveTitleForFrame, |
|
69 _didClearWindowForFrame |
|
70 }; |
|
71 WKBundlePageSetLoaderClient(m_page, &loaderClient); |
|
72 |
|
73 WKBundlePageUIClient uiClient = { |
|
74 0, |
|
75 this, |
|
76 _willAddMessageToConsole, |
|
77 _willSetStatusbarText, |
|
78 _willRunJavaScriptAlert, |
|
79 _willRunJavaScriptConfirm, |
|
80 _willRunJavaScriptPrompt |
|
81 }; |
|
82 WKBundlePageSetUIClient(m_page, &uiClient); |
|
83 } |
|
84 |
|
85 InjectedBundlePage::~InjectedBundlePage() |
|
86 { |
|
87 } |
|
88 |
|
89 // Loader Client Callbacks |
|
90 |
|
91 void InjectedBundlePage::_didStartProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void *clientInfo) |
|
92 { |
|
93 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didStartProvisionalLoadForFrame(frame); |
|
94 } |
|
95 |
|
96 void InjectedBundlePage::_didReceiveServerRedirectForProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void *clientInfo) |
|
97 { |
|
98 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveServerRedirectForProvisionalLoadForFrame(frame); |
|
99 } |
|
100 |
|
101 void InjectedBundlePage::_didFailProvisionalLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void *clientInfo) |
|
102 { |
|
103 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailProvisionalLoadWithErrorForFrame(frame); |
|
104 } |
|
105 |
|
106 void InjectedBundlePage::_didCommitLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void *clientInfo) |
|
107 { |
|
108 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCommitLoadForFrame(frame); |
|
109 } |
|
110 |
|
111 void InjectedBundlePage::_didFinishLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void *clientInfo) |
|
112 { |
|
113 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(frame); |
|
114 } |
|
115 |
|
116 void InjectedBundlePage::_didFailLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void *clientInfo) |
|
117 { |
|
118 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailLoadWithErrorForFrame(frame); |
|
119 } |
|
120 |
|
121 void InjectedBundlePage::_didReceiveTitleForFrame(WKBundlePageRef page, WKStringRef title, WKBundleFrameRef frame, const void *clientInfo) |
|
122 { |
|
123 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveTitleForFrame(title, frame); |
|
124 } |
|
125 |
|
126 void InjectedBundlePage::_didClearWindowForFrame(WKBundlePageRef page, WKBundleFrameRef frame, JSGlobalContextRef context, JSObjectRef window, const void *clientInfo) |
|
127 { |
|
128 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didClearWindowForFrame(frame, context, window); |
|
129 } |
|
130 |
|
131 void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundleFrameRef frame) |
|
132 { |
|
133 if (frame == WKBundlePageGetMainFrame(m_page)) |
|
134 m_isLoading = true; |
|
135 } |
|
136 |
|
137 void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundleFrameRef frame) |
|
138 { |
|
139 } |
|
140 |
|
141 void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundleFrameRef frame) |
|
142 { |
|
143 } |
|
144 |
|
145 void InjectedBundlePage::didCommitLoadForFrame(WKBundleFrameRef frame) |
|
146 { |
|
147 } |
|
148 |
|
149 static double numericWindowPropertyValue(WKBundleFrameRef frame, const char* propertyName) |
|
150 { |
|
151 JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame); |
|
152 JSObjectRef window = JSContextGetGlobalObject(context); |
|
153 JSValueRef exception = 0; |
|
154 JSRetainPtr<JSStringRef> propertyNameString(Adopt, JSStringCreateWithUTF8CString(propertyName)); |
|
155 JSValueRef value = JSObjectGetProperty(context, window, propertyNameString.get(), &exception); |
|
156 if (exception) |
|
157 return 0; |
|
158 return JSValueToNumber(context, value, &exception); |
|
159 } |
|
160 |
|
161 enum FrameNamePolicy { ShouldNotIncludeFrameName, ShouldIncludeFrameName }; |
|
162 |
|
163 static void dumpFrameScrollPosition(WKBundleFrameRef frame, FrameNamePolicy shouldIncludeFrameName = ShouldNotIncludeFrameName) |
|
164 { |
|
165 double x = numericWindowPropertyValue(frame, "pageXOffset"); |
|
166 double y = numericWindowPropertyValue(frame, "pageYOffset"); |
|
167 if (fabs(x) > 0.00000001 || fabs(y) > 0.00000001) { |
|
168 if (shouldIncludeFrameName) { |
|
169 WKRetainPtr<WKStringRef> name(AdoptWK, WKBundleFrameCopyName(frame)); |
|
170 InjectedBundle::shared().os() << "frame '" << WKStringToUTF8(name.get())->data() << "' "; |
|
171 } |
|
172 InjectedBundle::shared().os() << "scrolled to " << x << "," << y << "\n"; |
|
173 } |
|
174 } |
|
175 |
|
176 static void dumpDescendantFrameScrollPositions(WKBundleFrameRef frame) |
|
177 { |
|
178 WKRetainPtr<WKArrayRef> childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame)); |
|
179 size_t size = WKArrayGetSize(childFrames.get()); |
|
180 for (size_t i = 0; i < size; ++i) { |
|
181 // FIXME: I don't like that we have to const_cast here. Can we change WKArray? |
|
182 WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(const_cast<void*>(WKArrayGetItemAtIndex(childFrames.get(), i))); |
|
183 dumpFrameScrollPosition(subframe, ShouldIncludeFrameName); |
|
184 dumpDescendantFrameScrollPositions(subframe); |
|
185 } |
|
186 } |
|
187 |
|
188 void InjectedBundlePage::dumpAllFrameScrollPositions() |
|
189 { |
|
190 WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page); |
|
191 dumpFrameScrollPosition(frame); |
|
192 dumpDescendantFrameScrollPositions(frame); |
|
193 } |
|
194 |
|
195 void InjectedBundlePage::dump() |
|
196 { |
|
197 InjectedBundle::shared().layoutTestController()->invalidateWaitToDumpWatchdog(); |
|
198 |
|
199 if (InjectedBundle::shared().layoutTestController()->shouldDumpAsText()) { |
|
200 // FIXME: Support dumping subframes when layoutTestController()->dumpChildFramesAsText() is true. |
|
201 WKRetainPtr<WKStringRef> innerText(AdoptWK, WKBundleFrameCopyInnerText(WKBundlePageGetMainFrame(m_page))); |
|
202 InjectedBundle::shared().os() << WKStringToUTF8(innerText.get())->data() << "\n"; |
|
203 } else { |
|
204 WKRetainPtr<WKStringRef> externalRepresentation(AdoptWK, WKBundlePageCopyRenderTreeExternalRepresentation(m_page)); |
|
205 InjectedBundle::shared().os() << WKStringToUTF8(externalRepresentation.get())->data(); |
|
206 } |
|
207 |
|
208 if (InjectedBundle::shared().layoutTestController()->shouldDumpAllFrameScrollPositions()) |
|
209 dumpAllFrameScrollPositions(); |
|
210 else if (InjectedBundle::shared().layoutTestController()->shouldDumpMainFrameScrollPosition()) |
|
211 dumpFrameScrollPosition(WKBundlePageGetMainFrame(m_page)); |
|
212 |
|
213 InjectedBundle::shared().done(); |
|
214 } |
|
215 |
|
216 void InjectedBundlePage::didFinishLoadForFrame(WKBundleFrameRef frame) |
|
217 { |
|
218 if (!WKBundleFrameIsMainFrame(frame)) |
|
219 return; |
|
220 |
|
221 m_isLoading = false; |
|
222 |
|
223 if (InjectedBundle::shared().layoutTestController()->waitToDump()) |
|
224 return; |
|
225 |
|
226 dump(); |
|
227 } |
|
228 |
|
229 void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundleFrameRef frame) |
|
230 { |
|
231 if (!WKBundleFrameIsMainFrame(frame)) |
|
232 return; |
|
233 |
|
234 m_isLoading = false; |
|
235 |
|
236 InjectedBundle::shared().done(); |
|
237 } |
|
238 |
|
239 void InjectedBundlePage::didReceiveTitleForFrame(WKStringRef title, WKBundleFrameRef frame) |
|
240 { |
|
241 } |
|
242 |
|
243 void InjectedBundlePage::didClearWindowForFrame(WKBundleFrameRef frame, JSGlobalContextRef context, JSObjectRef window) |
|
244 { |
|
245 JSValueRef exception = 0; |
|
246 InjectedBundle::shared().layoutTestController()->makeWindowObject(context, window, &exception); |
|
247 } |
|
248 |
|
249 // UI Client Callbacks |
|
250 |
|
251 void InjectedBundlePage::_willAddMessageToConsole(WKBundlePageRef page, WKStringRef message, uint32_t lineNumber, const void *clientInfo) |
|
252 { |
|
253 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willAddMessageToConsole(message, lineNumber); |
|
254 } |
|
255 |
|
256 void InjectedBundlePage::_willSetStatusbarText(WKBundlePageRef page, WKStringRef statusbarText, const void *clientInfo) |
|
257 { |
|
258 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willSetStatusbarText(statusbarText); |
|
259 } |
|
260 |
|
261 void InjectedBundlePage::_willRunJavaScriptAlert(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo) |
|
262 { |
|
263 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptAlert(message, frame); |
|
264 } |
|
265 |
|
266 void InjectedBundlePage::_willRunJavaScriptConfirm(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo) |
|
267 { |
|
268 return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptConfirm(message, frame); |
|
269 } |
|
270 |
|
271 void InjectedBundlePage::_willRunJavaScriptPrompt(WKBundlePageRef page, WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef frame, const void *clientInfo) |
|
272 { |
|
273 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptPrompt(message, defaultValue, frame); |
|
274 } |
|
275 |
|
276 void InjectedBundlePage::willAddMessageToConsole(WKStringRef message, uint32_t lineNumber) |
|
277 { |
|
278 // FIXME: Strip file: urls. |
|
279 InjectedBundle::shared().os() << "CONSOLE MESSAGE: line " << lineNumber << ": " << WKStringToUTF8(message)->data() << "\n"; |
|
280 } |
|
281 |
|
282 void InjectedBundlePage::willSetStatusbarText(WKStringRef statusbarText) |
|
283 { |
|
284 if (!InjectedBundle::shared().layoutTestController()->shouldDumpStatusCallbacks()) |
|
285 return; |
|
286 |
|
287 InjectedBundle::shared().os() << "UI DELEGATE STATUS CALLBACK: setStatusText:" << WKStringToUTF8(statusbarText)->data() << "\n"; |
|
288 } |
|
289 |
|
290 void InjectedBundlePage::willRunJavaScriptAlert(WKStringRef message, WKBundleFrameRef) |
|
291 { |
|
292 InjectedBundle::shared().os() << "ALERT: " << WKStringToUTF8(message)->data() << "\n"; |
|
293 } |
|
294 |
|
295 void InjectedBundlePage::willRunJavaScriptConfirm(WKStringRef message, WKBundleFrameRef) |
|
296 { |
|
297 InjectedBundle::shared().os() << "CONFIRM: " << WKStringToUTF8(message)->data() << "\n"; |
|
298 } |
|
299 |
|
300 void InjectedBundlePage::willRunJavaScriptPrompt(WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef) |
|
301 { |
|
302 InjectedBundle::shared().os() << "PROMPT: " << WKStringToUTF8(message)->data() << ", default text: " << WKStringToUTF8(defaultValue)->data() << "\n"; |
|
303 } |
|
304 |
|
305 } // namespace WTR |