1 /* 2 * Copyright (C) 2005, 2006, 2007, 2009 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 * 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 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "config.h" 30 #include "FrameLoadDelegate.h" 31 32 #include "AccessibilityController.h" 33 #include "DumpRenderTree.h" 34 #include "EventSender.h" 35 #include "GCController.h" 36 #include "LayoutTestController.h" 37 #include "WorkQueueItem.h" 38 #include "WorkQueue.h" 39 #include <WebCore/COMPtr.h> 40 #include <JavaScriptCore/Assertions.h> 41 #include <JavaScriptCore/JavaScriptCore.h> 42 #include <WebKit/WebKit.h> 43 #include <wtf/Vector.h> 44 #include <stdio.h> 45 #include <string> 46 47 using std::string; 48 49 static FrameLoadDelegate* g_delegateWaitingOnTimer; 50 51 string BSTRtoString(BSTR bstr) 52 { 53 int result = WideCharToMultiByte(CP_UTF8, 0, bstr, SysStringLen(bstr) + 1, 0, 0, 0, 0); 54 Vector<char> utf8Vector(result); 55 result = WideCharToMultiByte(CP_UTF8, 0, bstr, SysStringLen(bstr) + 1, utf8Vector.data(), result, 0, 0); 56 if (!result) 57 return string(); 58 59 return string(utf8Vector.data(), utf8Vector.size() - 1); 60 } 61 62 string descriptionSuitableForTestResult(IWebFrame* webFrame) 63 { 64 COMPtr<IWebView> webView; 65 if (FAILED(webFrame->webView(&webView))) 66 return string(); 67 68 COMPtr<IWebFrame> mainFrame; 69 if (FAILED(webView->mainFrame(&mainFrame))) 70 return string(); 71 72 BSTR frameNameBSTR; 73 if (FAILED(webFrame->name(&frameNameBSTR)) || BSTRtoString(frameNameBSTR).empty() ) 74 return (webFrame == mainFrame) ? "main frame" : string(); 75 76 string frameName = (webFrame == mainFrame) ? "main frame" : "frame"; 77 frameName += " \"" + BSTRtoString(frameNameBSTR) + "\""; 78 79 SysFreeString(frameNameBSTR); 80 return frameName; 81 } 82 83 FrameLoadDelegate::FrameLoadDelegate() 84 : m_refCount(1) 85 , m_gcController(new GCController) 86 , m_accessibilityController(new AccessibilityController) 87 { 88 } 89 90 FrameLoadDelegate::~FrameLoadDelegate() 91 { 92 } 93 94 HRESULT STDMETHODCALLTYPE FrameLoadDelegate::QueryInterface(REFIID riid, void** ppvObject) 95 { 96 *ppvObject = 0; 97 if (IsEqualGUID(riid, IID_IUnknown)) 98 *ppvObject = static_cast<IWebFrameLoadDelegate*>(this); 99 else if (IsEqualGUID(riid, IID_IWebFrameLoadDelegate)) 100 *ppvObject = static_cast<IWebFrameLoadDelegate*>(this); 101 else if (IsEqualGUID(riid, IID_IWebFrameLoadDelegatePrivate)) 102 *ppvObject = static_cast<IWebFrameLoadDelegatePrivate*>(this); 103 else if (IsEqualGUID(riid, IID_IWebFrameLoadDelegatePrivate2)) 104 *ppvObject = static_cast<IWebFrameLoadDelegatePrivate2*>(this); 105 else 106 return E_NOINTERFACE; 107 108 AddRef(); 109 return S_OK; 110 } 111 112 ULONG STDMETHODCALLTYPE FrameLoadDelegate::AddRef(void) 113 { 114 return ++m_refCount; 115 } 116 117 ULONG STDMETHODCALLTYPE FrameLoadDelegate::Release(void) 118 { 119 ULONG newRef = --m_refCount; 120 if (!newRef) 121 delete(this); 122 123 return newRef; 124 } 125 126 127 HRESULT STDMETHODCALLTYPE FrameLoadDelegate::didStartProvisionalLoadForFrame( 128 /* [in] */ IWebView* webView, 129 /* [in] */ IWebFrame* frame) 130 { 131 if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) 132 printf("%s - didStartProvisionalLoadForFrame\n", descriptionSuitableForTestResult(frame).c_str()); 133 134 // Make sure we only set this once per test. If it gets cleared, and then set again, we might 135 // end up doing two dumps for one test. 136 if (!topLoadingFrame && !done) 137 topLoadingFrame = frame; 138 139 return S_OK; 140 } 141 142 HRESULT STDMETHODCALLTYPE FrameLoadDelegate::didReceiveServerRedirectForProvisionalLoadForFrame( 143 /* [in] */ IWebView *webView, 144 /* [in] */ IWebFrame *frame) 145 { 146 if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) 147 printf("%s - didReceiveServerRedirectForProvisionalLoadForFrame\n", descriptionSuitableForTestResult(frame).c_str()); 148 149 return S_OK; 150 } 151 152 HRESULT STDMETHODCALLTYPE FrameLoadDelegate::didFailProvisionalLoadWithError( 153 /* [in] */ IWebView *webView, 154 /* [in] */ IWebError *error, 155 /* [in] */ IWebFrame *frame) 156 { 157 if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) 158 printf("%s - didFailProvisionalLoadWithError\n", descriptionSuitableForTestResult(frame).c_str()); 159 160 return S_OK; 161 } 162 163 HRESULT STDMETHODCALLTYPE FrameLoadDelegate::didCommitLoadForFrame( 164 /* [in] */ IWebView *webView, 165 /* [in] */ IWebFrame *frame) 166 { 167 if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) 168 printf("%s - didCommitLoadForFrame\n", descriptionSuitableForTestResult(frame).c_str()); 169 170 COMPtr<IWebViewPrivate> webViewPrivate; 171 HRESULT hr = webView->QueryInterface(&webViewPrivate); 172 if (FAILED(hr)) 173 return hr; 174 webViewPrivate->updateFocusedAndActiveState(); 175 176 return S_OK; 177 } 178 179 HRESULT STDMETHODCALLTYPE FrameLoadDelegate::didReceiveTitle( 180 /* [in] */ IWebView *webView, 181 /* [in] */ BSTR title, 182 /* [in] */ IWebFrame *frame) 183 { 184 if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) 185 printf("%s - didReceiveTitle: %S\n", descriptionSuitableForTestResult(frame).c_str(), title); 186 187 if (::gLayoutTestController->dumpTitleChanges() && !done) 188 printf("TITLE CHANGED: %S\n", title ? title : L""); 189 return S_OK; 190 } 191 192 void FrameLoadDelegate::processWork() 193 { 194 // if another load started, then wait for it to complete. 195 if (topLoadingFrame) 196 return; 197 198 // if we finish all the commands, we're ready to dump state 199 if (WorkQueue::shared()->processWork() && !::gLayoutTestController->waitToDump()) 200 dump(); 201 } 202 203 void FrameLoadDelegate::resetToConsistentState() 204 { 205 m_accessibilityController->resetToConsistentState(); 206 } 207 208 static void CALLBACK processWorkTimer(HWND, UINT, UINT_PTR id, DWORD) 209 { 210 ::KillTimer(0, id); 211 FrameLoadDelegate* d = g_delegateWaitingOnTimer; 212 g_delegateWaitingOnTimer = 0; 213 d->processWork(); 214 } 215 216 void FrameLoadDelegate::locationChangeDone(IWebError*, IWebFrame* frame) 217 { 218 if (frame != topLoadingFrame) 219 return; 220 221 topLoadingFrame = 0; 222 WorkQueue::shared()->setFrozen(true); 223 224 if (::gLayoutTestController->waitToDump()) 225 return; 226 227 if (WorkQueue::shared()->count()) { 228 ASSERT(!g_delegateWaitingOnTimer); 229 g_delegateWaitingOnTimer = this; 230 ::SetTimer(0, 0, 0, processWorkTimer); 231 return; 232 } 233 234 dump(); 235 } 236 237 HRESULT STDMETHODCALLTYPE FrameLoadDelegate::didFinishLoadForFrame( 238 /* [in] */ IWebView* webView, 239 /* [in] */ IWebFrame* frame) 240 { 241 if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) 242 printf("%s - didFinishLoadForFrame\n", descriptionSuitableForTestResult(frame).c_str()); 243 244 locationChangeDone(0, frame); 245 return S_OK; 246 } 247 248 HRESULT STDMETHODCALLTYPE FrameLoadDelegate::didFailLoadWithError( 249 /* [in] */ IWebView* webView, 250 /* [in] */ IWebError* error, 251 /* [in] */ IWebFrame* frame) 252 { 253 if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) 254 printf("%s - didFailLoadWithError\n", descriptionSuitableForTestResult(frame).c_str()); 255 256 locationChangeDone(error, frame); 257 return S_OK; 258 } 259 260 HRESULT STDMETHODCALLTYPE FrameLoadDelegate::willPerformClientRedirectToURL( 261 /* [in] */ IWebView *webView, 262 /* [in] */ BSTR url, 263 /* [in] */ double delaySeconds, 264 /* [in] */ DATE fireDate, 265 /* [in] */ IWebFrame *frame) 266 { 267 if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) 268 printf("%s - willPerformClientRedirectToURL: %S \n", descriptionSuitableForTestResult(frame).c_str(), 269 urlSuitableForTestResult(std::wstring(url, ::SysStringLen(url))).c_str()); 270 271 return S_OK; 272 } 273 274 HRESULT STDMETHODCALLTYPE FrameLoadDelegate::didCancelClientRedirectForFrame( 275 /* [in] */ IWebView *webView, 276 /* [in] */ IWebFrame *frame) 277 { 278 if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) 279 printf("%s - didCancelClientRedirectForFrame\n", descriptionSuitableForTestResult(frame).c_str()); 280 281 return S_OK; 282 } 283 284 285 HRESULT STDMETHODCALLTYPE FrameLoadDelegate::willCloseFrame( 286 /* [in] */ IWebView *webView, 287 /* [in] */ IWebFrame *frame) 288 { 289 return E_NOTIMPL; 290 } 291 292 HRESULT FrameLoadDelegate::didClearWindowObject(IWebView*, JSContextRef, JSObjectRef, IWebFrame*) 293 { 294 return E_NOTIMPL; 295 } 296 297 HRESULT FrameLoadDelegate::didClearWindowObjectForFrameInScriptWorld(IWebView* webView, IWebFrame* frame, IWebScriptWorld* world) 298 { 299 ASSERT_ARG(webView, webView); 300 ASSERT_ARG(frame, frame); 301 ASSERT_ARG(world, world); 302 if (!webView || !frame || !world) 303 return E_POINTER; 304 305 COMPtr<IWebScriptWorld> standardWorld; 306 if (FAILED(world->standardWorld(&standardWorld))) 307 return S_OK; 308 309 if (world == standardWorld) 310 didClearWindowObjectForFrameInStandardWorld(frame); 311 else 312 didClearWindowObjectForFrameInIsolatedWorld(frame, world); 313 return S_OK; 314 } 315 316 void FrameLoadDelegate::didClearWindowObjectForFrameInIsolatedWorld(IWebFrame* frame, IWebScriptWorld* world) 317 { 318 COMPtr<IWebFramePrivate> framePrivate(Query, frame); 319 if (!framePrivate) 320 return; 321 322 JSGlobalContextRef ctx = framePrivate->globalContextForScriptWorld(world); 323 if (!ctx) 324 return; 325 326 JSObjectRef globalObject = JSContextGetGlobalObject(ctx); 327 if (!globalObject) 328 return; 329 330 JSObjectSetProperty(ctx, globalObject, JSRetainPtr<JSStringRef>(Adopt, JSStringCreateWithUTF8CString("__worldID")).get(), JSValueMakeNumber(ctx, worldIDForWorld(world)), kJSPropertyAttributeReadOnly, 0); 331 return; 332 } 333 334 void FrameLoadDelegate::didClearWindowObjectForFrameInStandardWorld(IWebFrame* frame) 335 { 336 JSGlobalContextRef context = frame->globalContext(); 337 JSObjectRef windowObject = JSContextGetGlobalObject(context); 338 339 JSValueRef exception = 0; 340 341 ::gLayoutTestController->makeWindowObject(context, windowObject, &exception); 342 ASSERT(!exception); 343 344 m_gcController->makeWindowObject(context, windowObject, &exception); 345 ASSERT(!exception); 346 347 m_accessibilityController->makeWindowObject(context, windowObject, &exception); 348 ASSERT(!exception); 349 350 JSStringRef eventSenderStr = JSStringCreateWithUTF8CString("eventSender"); 351 JSValueRef eventSender = makeEventSender(context); 352 JSObjectSetProperty(context, windowObject, eventSenderStr, eventSender, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, 0); 353 JSStringRelease(eventSenderStr); 354 } 355 356 HRESULT STDMETHODCALLTYPE FrameLoadDelegate::didFinishDocumentLoadForFrame( 357 /* [in] */ IWebView *sender, 358 /* [in] */ IWebFrame *frame) 359 { 360 if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) 361 printf("%s - didFinishDocumentLoadForFrame\n", 362 descriptionSuitableForTestResult(frame).c_str()); 363 if (!done) { 364 COMPtr<IWebFramePrivate> webFramePrivate; 365 HRESULT hr = frame->QueryInterface(&webFramePrivate); 366 if (FAILED(hr)) 367 return hr; 368 unsigned pendingFrameUnloadEvents; 369 hr = webFramePrivate->pendingFrameUnloadEventCount(&pendingFrameUnloadEvents); 370 if (FAILED(hr)) 371 return hr; 372 if (pendingFrameUnloadEvents) 373 printf("%s - has %u onunload handler(s)\n", 374 descriptionSuitableForTestResult(frame).c_str(), pendingFrameUnloadEvents); 375 } 376 377 return S_OK; 378 } 379 380 HRESULT STDMETHODCALLTYPE FrameLoadDelegate::didHandleOnloadEventsForFrame( 381 /* [in] */ IWebView *sender, 382 /* [in] */ IWebFrame *frame) 383 { 384 if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) 385 printf("%s - didHandleOnloadEventsForFrame\n", 386 descriptionSuitableForTestResult(frame).c_str()); 387 388 return S_OK; 389 } 390 391 HRESULT STDMETHODCALLTYPE FrameLoadDelegate::didFirstVisuallyNonEmptyLayoutInFrame( 392 /* [in] */ IWebView *sender, 393 /* [in] */ IWebFrame *frame) 394 { 395 return S_OK; 396 } 397 398 HRESULT STDMETHODCALLTYPE FrameLoadDelegate::didDisplayInsecureContent( 399 /* [in] */ IWebView *sender) 400 { 401 if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) 402 printf("didDisplayInsecureContent\n"); 403 404 return S_OK; 405 } 406 407 HRESULT STDMETHODCALLTYPE FrameLoadDelegate::didRunInsecureContent( 408 /* [in] */ IWebView *sender, 409 /* [in] */ IWebSecurityOrigin *origin) 410 { 411 if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) 412 printf("didRunInsecureContent\n"); 413 414 return S_OK; 415 } 416 417