ce0ee7a1f4c2d03f9f9ed759c70883bd98b16cfb
[WebKit-https.git] / Tools / DumpRenderTree / chromium / WebViewHost.cpp
1 /*
2  * Copyright (C) 2010 Google 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 are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "WebViewHost.h"
33
34 #include "LayoutTestController.h"
35 #include "TestNavigationController.h"
36 #include "TestShell.h"
37 #include "TestWebWorker.h"
38 #include "WebCString.h"
39 #include "WebConsoleMessage.h"
40 #include "WebContextMenuData.h"
41 #include "WebDataSource.h"
42 #include "WebDeviceOrientationClientMock.h"
43 #include "WebDragData.h"
44 #include "WebElement.h"
45 #include "WebFrame.h"
46 #include "WebGeolocationClientMock.h"
47 #include "WebHistoryItem.h"
48 #include "WebNode.h"
49 #include "WebRange.h"
50 #include "WebRect.h"
51 #include "WebScreenInfo.h"
52 #include "WebSize.h"
53 #include "WebSpeechInputControllerMock.h"
54 #include "WebStorageNamespace.h"
55 #include "WebURLRequest.h"
56 #include "WebURLResponse.h"
57 #include "WebView.h"
58 #include "WebWindowFeatures.h"
59 #include "skia/ext/platform_canvas.h"
60 #include "webkit/support/webkit_support.h"
61 #include <wtf/Assertions.h>
62 #include <wtf/PassOwnPtr.h>
63
64 using namespace WebCore;
65 using namespace WebKit;
66 using namespace skia;
67 using namespace std;
68
69 static const int screenWidth = 1920;
70 static const int screenHeight = 1080;
71 static const int screenUnavailableBorder = 8;
72
73 // WebNavigationType debugging strings taken from PolicyDelegate.mm.
74 static const char* linkClickedString = "link clicked";
75 static const char* formSubmittedString = "form submitted";
76 static const char* backForwardString = "back/forward";
77 static const char* reloadString = "reload";
78 static const char* formResubmittedString = "form resubmitted";
79 static const char* otherString = "other";
80 static const char* illegalString = "illegal value";
81
82 static int nextPageID = 1;
83
84 // Used to write a platform neutral file:/// URL by only taking the filename
85 // (e.g., converts "file:///tmp/foo.txt" to just "foo.txt").
86 static string urlSuitableForTestResult(const string& url)
87 {
88     if (url.empty() || string::npos == url.find("file://"))
89         return url;
90
91     size_t pos = url.rfind('/');
92     if (pos == string::npos) {
93 #if OS(WINDOWS)
94         pos = url.rfind('\\');
95         if (pos == string::npos)
96             pos = 0;
97 #else
98         pos = 0;
99 #endif
100     }
101     string filename = url.substr(pos + 1);
102     if (filename.empty())
103         return "file:"; // A WebKit test has this in its expected output.
104     return filename;
105 }
106
107 // Used to write a platform neutral file:/// URL by taking the
108 // filename and its directory. (e.g., converts
109 // "file:///tmp/foo/bar.txt" to just "bar.txt").
110 static string descriptionSuitableForTestResult(const string& url)
111 {
112     if (url.empty() || string::npos == url.find("file://"))
113         return url;
114
115     size_t pos = url.rfind('/');
116     if (pos == string::npos || !pos)
117         return "ERROR:" + url;
118     pos = url.rfind('/', pos - 1);
119     if (pos == string::npos)
120         return "ERROR:" + url;
121
122     return url.substr(pos + 1);
123 }
124
125 // Adds a file called "DRTFakeFile" to |data_object| (CF_HDROP).  Use to fake
126 // dragging a file.
127 static void addDRTFakeFileToDataObject(WebDragData* dragData)
128 {
129     dragData->appendToFilenames(WebString::fromUTF8("DRTFakeFile"));
130 }
131
132 // Get a debugging string from a WebNavigationType.
133 static const char* webNavigationTypeToString(WebNavigationType type)
134 {
135     switch (type) {
136     case WebKit::WebNavigationTypeLinkClicked:
137         return linkClickedString;
138     case WebKit::WebNavigationTypeFormSubmitted:
139         return formSubmittedString;
140     case WebKit::WebNavigationTypeBackForward:
141         return backForwardString;
142     case WebKit::WebNavigationTypeReload:
143         return reloadString;
144     case WebKit::WebNavigationTypeFormResubmitted:
145         return formResubmittedString;
146     case WebKit::WebNavigationTypeOther:
147         return otherString;
148     }
149     return illegalString;
150 }
151
152 static string URLDescription(const GURL& url)
153 {
154     if (url.SchemeIs("file"))
155         return url.ExtractFileName();
156     return url.possibly_invalid_spec();
157 }
158
159 static void printResponseDescription(const WebURLResponse& response)
160 {
161     if (response.isNull()) {
162         fputs("(null)", stdout);
163         return;
164     }
165     string url = response.url().spec();
166     printf("<NSURLResponse %s, http status code %d>",
167            descriptionSuitableForTestResult(url).c_str(),
168            response.httpStatusCode());
169 }
170
171 static void printNodeDescription(const WebNode& node, int exception)
172 {
173     if (exception) {
174         fputs("ERROR", stdout);
175         return;
176     }
177     if (node.isNull()) {
178         fputs("(null)", stdout);
179         return;
180     }
181     fputs(node.nodeName().utf8().data(), stdout);
182     const WebNode& parent = node.parentNode();
183     if (!parent.isNull()) {
184         fputs(" > ", stdout);
185         printNodeDescription(parent, 0);
186     }
187 }
188
189 static void printRangeDescription(const WebRange& range)
190 {
191     if (range.isNull()) {
192         fputs("(null)", stdout);
193         return;
194     }
195     printf("range from %d of ", range.startOffset());
196     int exception = 0;
197     WebNode startNode = range.startContainer(exception);
198     printNodeDescription(startNode, exception);
199     printf(" to %d of ", range.endOffset());
200     WebNode endNode = range.endContainer(exception);
201     printNodeDescription(endNode, exception);
202 }
203
204 static string editingActionDescription(WebEditingAction action)
205 {
206     switch (action) {
207     case WebKit::WebEditingActionTyped:
208         return "WebViewInsertActionTyped";
209     case WebKit::WebEditingActionPasted:
210         return "WebViewInsertActionPasted";
211     case WebKit::WebEditingActionDropped:
212         return "WebViewInsertActionDropped";
213     }
214     return "(UNKNOWN ACTION)";
215 }
216
217 static string textAffinityDescription(WebTextAffinity affinity)
218 {
219     switch (affinity) {
220     case WebKit::WebTextAffinityUpstream:
221         return "NSSelectionAffinityUpstream";
222     case WebKit::WebTextAffinityDownstream:
223         return "NSSelectionAffinityDownstream";
224     }
225     return "(UNKNOWN AFFINITY)";
226 }
227
228 // WebViewClient -------------------------------------------------------------
229
230 WebView* WebViewHost::createView(WebFrame*, const WebWindowFeatures&, const WebString&)
231 {
232     if (!layoutTestController()->canOpenWindows())
233         return 0;
234     return m_shell->createNewWindow(WebURL())->webView();
235 }
236
237 WebWidget* WebViewHost::createPopupMenu(WebPopupType)
238 {
239     return 0;
240 }
241
242 WebWidget* WebViewHost::createPopupMenu(const WebPopupMenuInfo&)
243 {
244     return 0;
245 }
246
247 WebStorageNamespace* WebViewHost::createSessionStorageNamespace(unsigned quota)
248 {
249     return WebKit::WebStorageNamespace::createSessionStorageNamespace(quota);
250 }
251
252 void WebViewHost::didAddMessageToConsole(const WebConsoleMessage& message, const WebString& sourceName, unsigned sourceLine)
253 {
254     // This matches win DumpRenderTree's UIDelegate.cpp.
255     string newMessage;
256     if (!message.text.isEmpty()) {
257         newMessage = message.text.utf8();
258         size_t fileProtocol = newMessage.find("file://");
259         if (fileProtocol != string::npos) {
260             newMessage = newMessage.substr(0, fileProtocol)
261                 + urlSuitableForTestResult(newMessage.substr(fileProtocol));
262         }
263     }
264     printf("CONSOLE MESSAGE: line %d: %s\n", sourceLine, newMessage.data());
265 }
266
267 void WebViewHost::didStartLoading()
268 {
269     m_shell->setIsLoading(true);
270 }
271
272 void WebViewHost::didStopLoading()
273 {
274     m_shell->setIsLoading(false);
275 }
276
277 // The output from these methods in layout test mode should match that
278 // expected by the layout tests.  See EditingDelegate.m in DumpRenderTree.
279
280 bool WebViewHost::shouldBeginEditing(const WebRange& range)
281 {
282     if (layoutTestController()->shouldDumpEditingCallbacks()) {
283         fputs("EDITING DELEGATE: shouldBeginEditingInDOMRange:", stdout);
284         printRangeDescription(range);
285         fputs("\n", stdout);
286     }
287     return layoutTestController()->acceptsEditing();
288 }
289
290 bool WebViewHost::shouldEndEditing(const WebRange& range)
291 {
292     if (layoutTestController()->shouldDumpEditingCallbacks()) {
293         fputs("EDITING DELEGATE: shouldEndEditingInDOMRange:", stdout);
294         printRangeDescription(range);
295         fputs("\n", stdout);
296     }
297     return layoutTestController()->acceptsEditing();
298 }
299
300 bool WebViewHost::shouldInsertNode(const WebNode& node, const WebRange& range, WebEditingAction action)
301 {
302     if (layoutTestController()->shouldDumpEditingCallbacks()) {
303         fputs("EDITING DELEGATE: shouldInsertNode:", stdout);
304         printNodeDescription(node, 0);
305         fputs(" replacingDOMRange:", stdout);
306         printRangeDescription(range);
307         printf(" givenAction:%s\n", editingActionDescription(action).c_str());
308     }
309     return layoutTestController()->acceptsEditing();
310 }
311
312 bool WebViewHost::shouldInsertText(const WebString& text, const WebRange& range, WebEditingAction action)
313 {
314     if (layoutTestController()->shouldDumpEditingCallbacks()) {
315         printf("EDITING DELEGATE: shouldInsertText:%s replacingDOMRange:", text.utf8().data());
316         printRangeDescription(range);
317         printf(" givenAction:%s\n", editingActionDescription(action).c_str());
318     }
319     return layoutTestController()->acceptsEditing();
320 }
321
322 bool WebViewHost::shouldChangeSelectedRange(
323     const WebRange& fromRange, const WebRange& toRange, WebTextAffinity affinity, bool stillSelecting)
324 {
325     if (layoutTestController()->shouldDumpEditingCallbacks()) {
326         fputs("EDITING DELEGATE: shouldChangeSelectedDOMRange:", stdout);
327         printRangeDescription(fromRange);
328         fputs(" toDOMRange:", stdout);
329         printRangeDescription(toRange);
330         printf(" affinity:%s stillSelecting:%s\n",
331                textAffinityDescription(affinity).c_str(),
332                (stillSelecting ? "TRUE" : "FALSE"));
333     }
334     return layoutTestController()->acceptsEditing();
335 }
336
337 bool WebViewHost::shouldDeleteRange(const WebRange& range)
338 {
339     if (layoutTestController()->shouldDumpEditingCallbacks()) {
340         fputs("EDITING DELEGATE: shouldDeleteDOMRange:", stdout);
341         printRangeDescription(range);
342         fputs("\n", stdout);
343     }
344     return layoutTestController()->acceptsEditing();
345 }
346
347 bool WebViewHost::shouldApplyStyle(const WebString& style, const WebRange& range)
348 {
349     if (layoutTestController()->shouldDumpEditingCallbacks()) {
350         printf("EDITING DELEGATE: shouldApplyStyle:%s toElementsInDOMRange:", style.utf8().data());
351         printRangeDescription(range);
352         fputs("\n", stdout);
353     }
354     return layoutTestController()->acceptsEditing();
355 }
356
357 bool WebViewHost::isSmartInsertDeleteEnabled()
358 {
359     return m_smartInsertDeleteEnabled;
360 }
361
362 bool WebViewHost::isSelectTrailingWhitespaceEnabled()
363 {
364     return m_selectTrailingWhitespaceEnabled;
365 }
366
367 void WebViewHost::didBeginEditing()
368 {
369     if (!layoutTestController()->shouldDumpEditingCallbacks())
370         return;
371     fputs("EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification\n", stdout);
372 }
373
374 void WebViewHost::didChangeSelection(bool isEmptySelection)
375 {
376     if (layoutTestController()->shouldDumpEditingCallbacks())
377         fputs("EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification\n", stdout);
378     // No need to update clipboard with the selected text in DRT.
379 }
380
381 void WebViewHost::didChangeContents()
382 {
383     if (!layoutTestController()->shouldDumpEditingCallbacks())
384         return;
385     fputs("EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification\n", stdout);
386 }
387
388 void WebViewHost::didEndEditing()
389 {
390     if (!layoutTestController()->shouldDumpEditingCallbacks())
391         return;
392     fputs("EDITING DELEGATE: webViewDidEndEditing:WebViewDidEndEditingNotification\n", stdout);
393 }
394
395 bool WebViewHost::handleCurrentKeyboardEvent()
396 {
397     if (m_editCommandName.empty())
398         return false;
399     WebFrame* frame = webView()->focusedFrame();
400     if (!frame)
401         return false;
402
403     return frame->executeCommand(WebString::fromUTF8(m_editCommandName), WebString::fromUTF8(m_editCommandValue));
404 }
405
406 void WebViewHost::spellCheck(const WebString& text, int& misspelledOffset, int& misspelledLength)
407 {
408     // Check the spelling of the given text.
409     m_spellcheck.spellCheckWord(text, &misspelledOffset, &misspelledLength);
410 }
411
412 WebString WebViewHost::autoCorrectWord(const WebString&)
413 {
414     // Returns an empty string as Mac WebKit ('WebKitSupport/WebEditorClient.mm')
415     // does. (If this function returns a non-empty string, WebKit replaces the
416     // given misspelled string with the result one. This process executes some
417     // editor commands and causes layout-test failures.)
418     return WebString();
419 }
420
421 void WebViewHost::runModalAlertDialog(WebFrame*, const WebString& message)
422 {
423     printf("ALERT: %s\n", message.utf8().data());
424 }
425
426 bool WebViewHost::runModalConfirmDialog(WebFrame*, const WebString& message)
427 {
428     printf("CONFIRM: %s\n", message.utf8().data());
429     return true;
430 }
431
432 bool WebViewHost::runModalPromptDialog(WebFrame* frame, const WebString& message,
433                                        const WebString& defaultValue, WebString*)
434 {
435     printf("PROMPT: %s, default text: %s\n", message.utf8().data(), defaultValue.utf8().data());
436     return true;
437 }
438
439 bool WebViewHost::runModalBeforeUnloadDialog(WebFrame*, const WebString&)
440 {
441     return true; // Allow window closure.
442 }
443
444 void WebViewHost::showContextMenu(WebFrame*, const WebContextMenuData& contextMenuData)
445 {
446     m_lastContextMenuData = adoptPtr(new WebContextMenuData(contextMenuData));
447 }
448
449 void WebViewHost::clearContextMenuData()
450 {
451     m_lastContextMenuData.clear();
452 }
453
454 WebContextMenuData* WebViewHost::lastContextMenuData() const
455 {
456     return m_lastContextMenuData.get();
457 }
458
459 void WebViewHost::setStatusText(const WebString& text)
460 {
461     if (!layoutTestController()->shouldDumpStatusCallbacks())
462         return;
463     // When running tests, write to stdout.
464     printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", text.utf8().data());
465 }
466
467 void WebViewHost::startDragging(const WebDragData& data, WebDragOperationsMask mask, const WebImage&, const WebPoint&)
468 {
469     WebDragData mutableDragData = data;
470     if (layoutTestController()->shouldAddFileToPasteboard()) {
471         // Add a file called DRTFakeFile to the drag&drop clipboard.
472         addDRTFakeFileToDataObject(&mutableDragData);
473     }
474
475     // When running a test, we need to fake a drag drop operation otherwise
476     // Windows waits for real mouse events to know when the drag is over.
477     m_shell->eventSender()->doDragDrop(mutableDragData, mask);
478 }
479
480 void WebViewHost::navigateBackForwardSoon(int offset)
481 {
482     navigationController()->goToOffset(offset);
483 }
484
485 int WebViewHost::historyBackListCount()
486 {
487     return navigationController()->lastCommittedEntryIndex();
488 }
489
490 int WebViewHost::historyForwardListCount()
491 {
492     int currentIndex =navigationController()->lastCommittedEntryIndex();
493     return navigationController()->entryCount() - currentIndex - 1;
494 }
495
496 void WebViewHost::postAccessibilityNotification(const WebAccessibilityObject& obj, WebAccessibilityNotification notification)
497 {
498     if (notification == WebAccessibilityNotificationFocusedUIElementChanged)
499         m_shell->accessibilityController()->setFocusedElement(obj);
500
501     if (m_shell->accessibilityController()->shouldDumpAccessibilityNotifications()) {
502         printf("AccessibilityNotification - ");
503
504         switch (notification) {
505         case WebAccessibilityNotificationActiveDescendantChanged:
506             printf("ActiveDescendantChanged");
507             break;
508         case WebAccessibilityNotificationCheckedStateChanged:
509             printf("CheckedStateChanged");
510             break;
511         case WebAccessibilityNotificationChildrenChanged:
512             printf("ChildrenChanged");
513             break;
514         case WebAccessibilityNotificationFocusedUIElementChanged:
515             printf("FocusedUIElementChanged");
516             break;
517         case WebAccessibilityNotificationLayoutComplete:
518             printf("LayoutComplete");
519             break;
520         case WebAccessibilityNotificationLoadComplete:
521             printf("LoadComplete");
522             break;
523         case WebAccessibilityNotificationSelectedChildrenChanged:
524             printf("SelectedChildrenChanged");
525             break;
526         case WebAccessibilityNotificationSelectedTextChanged:
527             printf("SelectedTextChanged");
528             break;
529         case WebAccessibilityNotificationValueChanged:
530             printf("ValueChanged");
531             break;
532         case WebAccessibilityNotificationScrolledToAnchor:
533             printf("ScrolledToAnchor");
534             break;
535         case WebAccessibilityNotificationLiveRegionChanged:
536             printf("LiveRegionChanged");
537             break;
538         case WebAccessibilityNotificationMenuListValueChanged:
539             printf("MenuListValueChanged");
540             break;
541         case WebAccessibilityNotificationRowCountChanged:
542             printf("RowCountChanged");
543             break;
544         case WebAccessibilityNotificationRowCollapsed:
545             printf("RowCollapsed");
546             break;
547         case WebAccessibilityNotificationRowExpanded:
548             printf("RowExpanded");
549             break;
550         default:
551             break;
552         }
553
554         WebKit::WebNode node = obj.node();
555         if (!node.isNull() && node.isElementNode()) {
556             WebKit::WebElement element = node.to<WebKit::WebElement>();
557             if (element.hasAttribute("id"))
558                 printf(" - id:%s", element.getAttribute("id").utf8().data());
559         }
560
561         printf("\n");
562     }
563 }
564
565 WebNotificationPresenter* WebViewHost::notificationPresenter()
566 {
567     return m_shell->notificationPresenter();
568 }
569
570 WebKit::WebGeolocationClient* WebViewHost::geolocationClient()
571 {
572     return geolocationClientMock();
573 }
574
575 WebKit::WebGeolocationClientMock* WebViewHost::geolocationClientMock()
576 {
577     if (!m_geolocationClientMock)
578         m_geolocationClientMock.set(WebGeolocationClientMock::create());
579     return m_geolocationClientMock.get();
580 }
581
582 WebSpeechInputController* WebViewHost::speechInputController(WebKit::WebSpeechInputListener* listener)
583 {
584     if (!m_speechInputControllerMock)
585         m_speechInputControllerMock.set(WebSpeechInputControllerMock::create(listener));
586     return m_speechInputControllerMock.get();
587 }
588
589 WebDeviceOrientationClientMock* WebViewHost::deviceOrientationClientMock()
590 {
591     if (!m_deviceOrientationClientMock.get())
592         m_deviceOrientationClientMock.set(WebDeviceOrientationClientMock::create());
593     return m_deviceOrientationClientMock.get();
594 }
595
596 MockSpellCheck* WebViewHost::mockSpellCheck()
597 {
598     return &m_spellcheck;
599 }
600
601 WebDeviceOrientationClient* WebViewHost::deviceOrientationClient()
602 {
603     return deviceOrientationClientMock();
604 }
605
606 // WebWidgetClient -----------------------------------------------------------
607
608 void WebViewHost::didInvalidateRect(const WebRect& rect)
609 {
610     updatePaintRect(rect);
611 }
612
613 void WebViewHost::didScrollRect(int, int, const WebRect& clipRect)
614 {
615     // This is used for optimizing painting when the renderer is scrolled. We're
616     // currently not doing any optimizations so just invalidate the region.
617     didInvalidateRect(clipRect);
618 }
619
620 void WebViewHost::scheduleComposite()
621 {
622     WebSize widgetSize = webWidget()->size();
623     WebRect clientRect(0, 0, widgetSize.width, widgetSize.height);
624     didInvalidateRect(clientRect);
625 }
626
627 void WebViewHost::didFocus()
628 {
629     m_shell->setFocus(webWidget(), true);
630 }
631
632 void WebViewHost::didBlur()
633 {
634     m_shell->setFocus(webWidget(), false);
635 }
636
637 WebScreenInfo WebViewHost::screenInfo()
638 {
639     // We don't need to set actual values.
640     WebScreenInfo info;
641     info.depth = 24;
642     info.depthPerComponent = 8;
643     info.isMonochrome = false;
644     info.rect = WebRect(0, 0, screenWidth, screenHeight);
645     // Use values different from info.rect for testing.
646     info.availableRect = WebRect(screenUnavailableBorder, screenUnavailableBorder,
647                                  screenWidth - screenUnavailableBorder * 2,
648                                  screenHeight - screenUnavailableBorder * 2);
649     return info;
650 }
651
652 void WebViewHost::show(WebNavigationPolicy)
653 {
654     m_hasWindow = true;
655     WebSize size = webWidget()->size();
656     updatePaintRect(WebRect(0, 0, size.width, size.height));
657 }
658
659 void WebViewHost::closeWidget()
660 {
661     m_hasWindow = false;
662     m_shell->closeWindow(this);
663     // No more code here, we should be deleted at this point.
664 }
665
666 static void invokeCloseWidget(void* context)
667 {
668     WebViewHost* wvh = static_cast<WebViewHost*>(context);
669     wvh->closeWidget();
670 }
671
672 void WebViewHost::closeWidgetSoon()
673 {
674     webkit_support::PostDelayedTask(invokeCloseWidget, static_cast<void*>(this), 0);
675 }
676
677 void WebViewHost::didChangeCursor(const WebCursorInfo& cursorInfo)
678 {
679     if (!hasWindow())
680         return;
681     m_currentCursor = cursorInfo;
682 }
683
684 WebRect WebViewHost::windowRect()
685 {
686     return m_windowRect;
687 }
688
689 void WebViewHost::setWindowRect(const WebRect& rect)
690 {
691     m_windowRect = rect;
692     const int border2 = TestShell::virtualWindowBorder * 2;
693     if (m_windowRect.width <= border2)
694         m_windowRect.width = 1 + border2;
695     if (m_windowRect.height <= border2)
696         m_windowRect.height = 1 + border2;
697     int width = m_windowRect.width - border2;
698     int height = m_windowRect.height - border2;
699     discardBackingStore();
700     webWidget()->resize(WebSize(width, height));
701     updatePaintRect(WebRect(0, 0, width, height));
702 }
703
704 WebRect WebViewHost::rootWindowRect()
705 {
706     return windowRect();
707 }
708
709 WebRect WebViewHost::windowResizerRect()
710 {
711     // Not necessary.
712     return WebRect();
713 }
714
715 void WebViewHost::runModal()
716 {
717     bool oldState = webkit_support::MessageLoopNestableTasksAllowed();
718     webkit_support::MessageLoopSetNestableTasksAllowed(true);
719     m_inModalLoop = true;
720     webkit_support::RunMessageLoop();
721     webkit_support::MessageLoopSetNestableTasksAllowed(oldState);
722 }
723
724 // WebFrameClient ------------------------------------------------------------
725
726 WebPlugin* WebViewHost::createPlugin(WebFrame* frame, const WebPluginParams& params)
727 {
728     return webkit_support::CreateWebPlugin(frame, params);
729 }
730
731 WebWorker* WebViewHost::createWorker(WebFrame*, WebWorkerClient*)
732 {
733     return new TestWebWorker();
734 }
735
736 WebMediaPlayer* WebViewHost::createMediaPlayer(WebFrame* frame, WebMediaPlayerClient* client)
737 {
738     return webkit_support::CreateMediaPlayer(frame, client);
739 }
740
741 WebApplicationCacheHost* WebViewHost::createApplicationCacheHost(WebFrame* frame, WebApplicationCacheHostClient* client)
742 {
743     return webkit_support::CreateApplicationCacheHost(frame, client);
744 }
745
746 bool WebViewHost::allowPlugins(WebFrame* frame, bool enabledPerSettings)
747 {
748     return enabledPerSettings;
749 }
750
751 bool WebViewHost::allowImages(WebFrame* frame, bool enabledPerSettings)
752 {
753     return enabledPerSettings;
754 }
755
756 void WebViewHost::loadURLExternally(WebFrame*, const WebURLRequest& request, WebNavigationPolicy policy)
757 {
758     ASSERT(policy !=  WebKit::WebNavigationPolicyCurrentTab);
759     WebViewHost* another = m_shell->createNewWindow(request.url());
760     if (another)
761         another->show(policy);
762 }
763
764 WebNavigationPolicy WebViewHost::decidePolicyForNavigation(
765     WebFrame*, const WebURLRequest& request,
766     WebNavigationType type, const WebNode& originatingNode,
767     WebNavigationPolicy defaultPolicy, bool isRedirect)
768 {
769     WebNavigationPolicy result;
770     if (!m_policyDelegateEnabled)
771         return defaultPolicy;
772
773     printf("Policy delegate: attempt to load %s with navigation type '%s'",
774            URLDescription(request.url()).c_str(), webNavigationTypeToString(type));
775     if (!originatingNode.isNull()) {
776         fputs(" originating from ", stdout);
777         printNodeDescription(originatingNode, 0);
778     }
779     fputs("\n", stdout);
780     if (m_policyDelegateIsPermissive)
781         result = WebKit::WebNavigationPolicyCurrentTab;
782     else
783         result = WebKit::WebNavigationPolicyIgnore;
784
785     if (m_policyDelegateShouldNotifyDone)
786         layoutTestController()->policyDelegateDone();
787     return result;
788 }
789
790 bool WebViewHost::canHandleRequest(WebFrame*, const WebURLRequest& request)
791 {
792     GURL url = request.url();
793     // Just reject the scheme used in
794     // LayoutTests/http/tests/misc/redirect-to-external-url.html
795     return !url.SchemeIs("spaceballs");
796 }
797
798 WebURLError WebViewHost::cannotHandleRequestError(WebFrame*, const WebURLRequest& request)
799 {
800     WebURLError error;
801     // A WebKit layout test expects the following values.
802     // unableToImplementPolicyWithError() below prints them.
803     error.domain = WebString::fromUTF8("WebKitErrorDomain");
804     error.reason = 101;
805     error.unreachableURL = request.url();
806     return error;
807 }
808
809 WebURLError WebViewHost::cancelledError(WebFrame*, const WebURLRequest& request)
810 {
811     return webkit_support::CreateCancelledError(request);
812 }
813
814 void WebViewHost::unableToImplementPolicyWithError(WebFrame* frame, const WebURLError& error)
815 {
816     printf("Policy delegate: unable to implement policy with error domain '%s', "
817            "error code %d, in frame '%s'\n",
818            error.domain.utf8().data(), error.reason, frame->name().utf8().data());
819 }
820
821 void WebViewHost::willPerformClientRedirect(WebFrame* frame, const WebURL& from, const WebURL& to,
822                                             double interval, double fire_time)
823 {
824     if (!m_shell->shouldDumpFrameLoadCallbacks())
825         return;
826     printFrameDescription(frame);
827     printf(" - willPerformClientRedirectToURL: %s \n", to.spec().data());
828 }
829
830 void WebViewHost::didCancelClientRedirect(WebFrame* frame)
831 {
832     if (!m_shell->shouldDumpFrameLoadCallbacks())
833         return;
834     printFrameDescription(frame);
835     fputs(" - didCancelClientRedirectForFrame\n", stdout);
836 }
837
838 void WebViewHost::didCreateDataSource(WebFrame*, WebDataSource* ds)
839 {
840     ds->setExtraData(m_pendingExtraData.leakPtr());
841     if (!layoutTestController()->deferMainResourceDataLoad())
842         ds->setDeferMainResourceDataLoad(false);
843 }
844
845 void WebViewHost::didStartProvisionalLoad(WebFrame* frame)
846 {
847     if (m_shell->shouldDumpUserGestureInFrameLoadCallbacks())
848         printFrameUserGestureStatus(frame, " - in didStartProvisionalLoadForFrame\n");
849     if (m_shell->shouldDumpFrameLoadCallbacks()) {
850         printFrameDescription(frame);
851         fputs(" - didStartProvisionalLoadForFrame\n", stdout);
852     }
853
854     if (!m_topLoadingFrame)
855         m_topLoadingFrame = frame;
856
857     if (layoutTestController()->stopProvisionalFrameLoads()) {
858         printFrameDescription(frame);
859         fputs(" - stopping load in didStartProvisionalLoadForFrame callback\n", stdout);
860         frame->stopLoading();
861     }
862     updateAddressBar(frame->view());
863 }
864
865 void WebViewHost::didReceiveServerRedirectForProvisionalLoad(WebFrame* frame)
866 {
867     if (m_shell->shouldDumpFrameLoadCallbacks()) {
868         printFrameDescription(frame);
869         fputs(" - didReceiveServerRedirectForProvisionalLoadForFrame\n", stdout);
870     }
871     updateAddressBar(frame->view());
872 }
873
874 void WebViewHost::didFailProvisionalLoad(WebFrame* frame, const WebURLError& error)
875 {
876     if (m_shell->shouldDumpFrameLoadCallbacks()) {
877         printFrameDescription(frame);
878         fputs(" - didFailProvisionalLoadWithError\n", stdout);
879     }
880
881     locationChangeDone(frame);
882
883     // Don't display an error page if we're running layout tests, because
884     // DumpRenderTree doesn't.
885 }
886
887 void WebViewHost::didCommitProvisionalLoad(WebFrame* frame, bool isNewNavigation)
888 {
889     if (m_shell->shouldDumpFrameLoadCallbacks()) {
890         printFrameDescription(frame);
891         fputs(" - didCommitLoadForFrame\n", stdout);
892     }
893     updateForCommittedLoad(frame, isNewNavigation);
894 }
895
896 void WebViewHost::didClearWindowObject(WebFrame* frame)
897 {
898     m_shell->bindJSObjectsToWindow(frame);
899 }
900
901 void WebViewHost::didReceiveTitle(WebFrame* frame, const WebString& title)
902 {
903     WebCString title8 = title.utf8();
904
905     if (m_shell->shouldDumpFrameLoadCallbacks()) {
906         printFrameDescription(frame);
907         printf(" - didReceiveTitle: %s\n", title8.data());
908     }
909
910     if (layoutTestController()->shouldDumpTitleChanges())
911         printf("TITLE CHANGED: %s\n", title8.data());
912
913     setPageTitle(title);
914 }
915
916 void WebViewHost::didFinishDocumentLoad(WebFrame* frame)
917 {
918     if (m_shell->shouldDumpFrameLoadCallbacks()) {
919         printFrameDescription(frame);
920         fputs(" - didFinishDocumentLoadForFrame\n", stdout);
921     } else {
922         unsigned pendingUnloadEvents = frame->unloadListenerCount();
923         if (pendingUnloadEvents) {
924             printFrameDescription(frame);
925             printf(" - has %u onunload handler(s)\n", pendingUnloadEvents);
926         }
927     }
928 }
929
930 void WebViewHost::didHandleOnloadEvents(WebFrame* frame)
931 {
932     if (m_shell->shouldDumpFrameLoadCallbacks()) {
933         printFrameDescription(frame);
934         fputs(" - didHandleOnloadEventsForFrame\n", stdout);
935     }
936 }
937
938 void WebViewHost::didFailLoad(WebFrame* frame, const WebURLError& error)
939 {
940     if (m_shell->shouldDumpFrameLoadCallbacks()) {
941         printFrameDescription(frame);
942         fputs(" - didFailLoadWithError\n", stdout);
943     }
944     locationChangeDone(frame);
945 }
946
947 void WebViewHost::didFinishLoad(WebFrame* frame)
948 {
949     if (m_shell->shouldDumpFrameLoadCallbacks()) {
950         printFrameDescription(frame);
951         fputs(" - didFinishLoadForFrame\n", stdout);
952     }
953     updateAddressBar(frame->view());
954     locationChangeDone(frame);
955 }
956
957 void WebViewHost::didNavigateWithinPage(WebFrame* frame, bool isNewNavigation)
958 {
959     frame->dataSource()->setExtraData(m_pendingExtraData.leakPtr());
960
961     updateForCommittedLoad(frame, isNewNavigation);
962 }
963
964 void WebViewHost::didChangeLocationWithinPage(WebFrame* frame)
965 {
966     if (m_shell->shouldDumpFrameLoadCallbacks()) {
967         printFrameDescription(frame);
968         fputs(" - didChangeLocationWithinPageForFrame\n", stdout);
969     }
970 }
971
972 void WebViewHost::assignIdentifierToRequest(WebFrame*, unsigned identifier, const WebURLRequest& request)
973 {
974      if (!m_shell->shouldDumpResourceLoadCallbacks())
975         return;
976     ASSERT(!m_resourceIdentifierMap.contains(identifier));
977     m_resourceIdentifierMap.set(identifier, descriptionSuitableForTestResult(request.url().spec()));
978 }
979
980 void WebViewHost::removeIdentifierForRequest(unsigned identifier)
981 {
982     m_resourceIdentifierMap.remove(identifier);
983 }
984
985 void WebViewHost::willSendRequest(WebFrame*, unsigned identifier, WebURLRequest& request, const WebURLResponse& redirectResponse)
986 {
987     // Need to use GURL for host() and SchemeIs()
988     GURL url = request.url();
989     string requestURL = url.possibly_invalid_spec();
990
991     if (layoutTestController()->shouldDumpResourceLoadCallbacks()) {
992         GURL mainDocumentURL = request.firstPartyForCookies();
993         printResourceDescription(identifier);
994         printf(" - willSendRequest <NSURLRequest URL %s, main document URL %s,"
995                " http method %s> redirectResponse ",
996                descriptionSuitableForTestResult(requestURL).c_str(),
997                URLDescription(mainDocumentURL).c_str(),
998                request.httpMethod().utf8().data());
999         printResponseDescription(redirectResponse);
1000         fputs("\n", stdout);
1001     }
1002
1003     if (!redirectResponse.isNull() && m_blocksRedirects) {
1004         fputs("Returning null for this redirect\n", stdout);
1005         // To block the request, we set its URL to an empty one.
1006         request.setURL(WebURL());
1007         return;
1008     }
1009
1010     if (m_requestReturnNull) {
1011         // To block the request, we set its URL to an empty one.
1012         request.setURL(WebURL());
1013         return;
1014     }
1015
1016     string host = url.host();
1017     // 255.255.255.255 is used in some tests that expect to get back an error.
1018     if (!host.empty() && (url.SchemeIs("http") || url.SchemeIs("https"))
1019         && host != "127.0.0.1"
1020         && host != "255.255.255.255"
1021         && host != "localhost"
1022         && !m_shell->allowExternalPages()) {
1023         printf("Blocked access to external URL %s\n", requestURL.c_str());
1024
1025         // To block the request, we set its URL to an empty one.
1026         request.setURL(WebURL());
1027         return;
1028     }
1029
1030     HashSet<String>::const_iterator end = m_clearHeaders.end();
1031     for (HashSet<String>::const_iterator header = m_clearHeaders.begin(); header != end; ++header)
1032         request.clearHTTPHeaderField(WebString(header->characters(), header->length()));
1033
1034     // Set the new substituted URL.
1035     request.setURL(webkit_support::RewriteLayoutTestsURL(request.url().spec()));
1036 }
1037
1038 void WebViewHost::didReceiveResponse(WebFrame*, unsigned identifier, const WebURLResponse& response)
1039 {
1040     if (m_shell->shouldDumpResourceLoadCallbacks()) {
1041         printResourceDescription(identifier);
1042         fputs(" - didReceiveResponse ", stdout);
1043         printResponseDescription(response);
1044         fputs("\n", stdout);
1045     }
1046     if (m_shell->shouldDumpResourceResponseMIMETypes()) {
1047         GURL url = response.url();
1048         WebString mimeType = response.mimeType();
1049         printf("%s has MIME type %s\n",
1050             url.ExtractFileName().c_str(),
1051             // Simulate NSURLResponse's mapping of empty/unknown MIME types to application/octet-stream
1052             mimeType.isEmpty() ? "application/octet-stream" : mimeType.utf8().data());
1053     }
1054 }
1055
1056 void WebViewHost::didFinishResourceLoad(WebFrame*, unsigned identifier)
1057 {
1058     if (m_shell->shouldDumpResourceLoadCallbacks()) {
1059         printResourceDescription(identifier);
1060         fputs(" - didFinishLoading\n", stdout);
1061     }
1062     removeIdentifierForRequest(identifier);
1063 }
1064
1065 void WebViewHost::didFailResourceLoad(WebFrame*, unsigned identifier, const WebURLError& error)
1066 {
1067     if (m_shell->shouldDumpResourceLoadCallbacks()) {
1068         printResourceDescription(identifier);
1069         fputs(" - didFailLoadingWithError: ", stdout);
1070         fputs(webkit_support::MakeURLErrorDescription(error).c_str(), stdout);
1071         fputs("\n", stdout);
1072     }
1073     removeIdentifierForRequest(identifier);
1074 }
1075
1076 void WebViewHost::didDisplayInsecureContent(WebFrame*)
1077 {
1078     if (m_shell->shouldDumpFrameLoadCallbacks())
1079         fputs("didDisplayInsecureContent\n", stdout);
1080 }
1081
1082 void WebViewHost::didRunInsecureContent(WebFrame*, const WebSecurityOrigin& origin)
1083 {
1084     if (m_shell->shouldDumpFrameLoadCallbacks())
1085         fputs("didRunInsecureContent\n", stdout);
1086 }
1087
1088 bool WebViewHost::allowScript(WebFrame*, bool enabledPerSettings)
1089 {
1090     return enabledPerSettings;
1091 }
1092
1093 void WebViewHost::openFileSystem(WebFrame* frame, WebFileSystem::Type type, long long size, bool create, WebFileSystemCallbacks* callbacks)
1094 {
1095     webkit_support::OpenFileSystem(frame, type, size, create, callbacks);
1096 }
1097
1098 // Public functions -----------------------------------------------------------
1099
1100 WebViewHost::WebViewHost(TestShell* shell)
1101     : m_shell(shell)
1102     , m_webWidget(0)
1103 {
1104     reset();
1105 }
1106
1107 WebViewHost::~WebViewHost()
1108 {
1109     // DevTools frontend page is supposed to be navigated only once and
1110     // loading another URL in that Page is an error.
1111     if (m_shell->devToolsWebView() != this) {
1112         // Navigate to an empty page to fire all the destruction logic for the
1113         // current page.
1114         loadURLForFrame(GURL("about:blank"), WebString());
1115     }
1116
1117     webWidget()->close();
1118
1119     if (m_inModalLoop)
1120         webkit_support::QuitMessageLoop();
1121 }
1122
1123 WebView* WebViewHost::webView() const
1124 {
1125     ASSERT(m_webWidget);
1126     // DRT does not support popup widgets. So m_webWidget is always a WebView.
1127     return static_cast<WebView*>(m_webWidget);
1128 }
1129
1130 WebWidget* WebViewHost::webWidget() const
1131 {
1132     ASSERT(m_webWidget);
1133     return m_webWidget;
1134 }
1135
1136 void WebViewHost::reset()
1137 {
1138     m_policyDelegateEnabled = false;
1139     m_policyDelegateIsPermissive = false;
1140     m_policyDelegateShouldNotifyDone = false;
1141     m_topLoadingFrame = 0;
1142     m_pageId = -1;
1143     m_lastPageIdUpdated = -1;
1144     m_hasWindow = false;
1145     m_inModalLoop = false;
1146     m_smartInsertDeleteEnabled = true;
1147 #if OS(WINDOWS)
1148     m_selectTrailingWhitespaceEnabled = true;
1149 #else
1150     m_selectTrailingWhitespaceEnabled = false;
1151 #endif
1152     m_blocksRedirects = false;
1153     m_requestReturnNull = false;
1154     m_isPainting = false;
1155     m_canvas.clear();
1156
1157     m_navigationController.set(new TestNavigationController(this));
1158
1159     m_pendingExtraData.clear();
1160     m_resourceIdentifierMap.clear();
1161     m_clearHeaders.clear();
1162     m_editCommandName.clear();
1163     m_editCommandValue.clear();
1164
1165     if (m_geolocationClientMock.get())
1166         m_geolocationClientMock->resetMock();
1167
1168     if (m_speechInputControllerMock.get())
1169         m_speechInputControllerMock->clearResults();
1170
1171     m_currentCursor = WebCursorInfo();
1172     m_windowRect = WebRect();
1173     m_paintRect = WebRect();
1174
1175     if (m_webWidget)
1176         webView()->mainFrame()->setName(WebString());
1177 }
1178
1179 void WebViewHost::setSelectTrailingWhitespaceEnabled(bool enabled)
1180 {
1181     m_selectTrailingWhitespaceEnabled = enabled;
1182     // In upstream WebKit, smart insert/delete is mutually exclusive with select
1183     // trailing whitespace, however, we allow both because Chromium on Windows
1184     // allows both.
1185 }
1186
1187 void WebViewHost::setSmartInsertDeleteEnabled(bool enabled)
1188 {
1189     m_smartInsertDeleteEnabled = enabled;
1190     // In upstream WebKit, smart insert/delete is mutually exclusive with select
1191     // trailing whitespace, however, we allow both because Chromium on Windows
1192     // allows both.
1193 }
1194
1195 void WebViewHost::setCustomPolicyDelegate(bool isCustom, bool isPermissive)
1196 {
1197     m_policyDelegateEnabled = isCustom;
1198     m_policyDelegateIsPermissive = isPermissive;
1199 }
1200
1201 void WebViewHost::waitForPolicyDelegate()
1202 {
1203     m_policyDelegateEnabled = true;
1204     m_policyDelegateShouldNotifyDone = true;
1205 }
1206
1207 void WebViewHost::setEditCommand(const string& name, const string& value)
1208 {
1209     m_editCommandName = name;
1210     m_editCommandValue = value;
1211 }
1212
1213 void WebViewHost::clearEditCommand()
1214 {
1215     m_editCommandName.clear();
1216     m_editCommandValue.clear();
1217 }
1218
1219 void WebViewHost::loadURLForFrame(const WebURL& url, const WebString& frameName)
1220 {
1221     if (!url.isValid())
1222         return;
1223     TestShell::resizeWindowForTest(this, url);
1224     navigationController()->loadEntry(TestNavigationEntry::create(-1, url, WebString(), frameName).get());
1225 }
1226
1227 bool WebViewHost::navigate(const TestNavigationEntry& entry, bool reload)
1228 {
1229     // Get the right target frame for the entry.
1230     WebFrame* frame = webView()->mainFrame();
1231     if (!entry.targetFrame().isEmpty())
1232         frame = webView()->findFrameByName(entry.targetFrame());
1233
1234     // TODO(mpcomplete): should we clear the target frame, or should
1235     // back/forward navigations maintain the target frame?
1236
1237     // A navigation resulting from loading a javascript URL should not be
1238     // treated as a browser initiated event.  Instead, we want it to look as if
1239     // the page initiated any load resulting from JS execution.
1240     if (!GURL(entry.URL()).SchemeIs("javascript"))
1241         setPendingExtraData(new TestShellExtraData(entry.pageID()));
1242
1243     // If we are reloading, then WebKit will use the state of the current page.
1244     // Otherwise, we give it the state to navigate to.
1245     if (reload) {
1246         frame->reload(false);
1247     } else if (!entry.contentState().isNull()) {
1248         ASSERT(entry.pageID() != -1);
1249         frame->loadHistoryItem(entry.contentState());
1250     } else {
1251         ASSERT(entry.pageID() == -1);
1252         frame->loadRequest(WebURLRequest(entry.URL()));
1253     }
1254
1255     // In case LoadRequest failed before DidCreateDataSource was called.
1256     setPendingExtraData(0);
1257
1258     // Restore focus to the main frame prior to loading new request.
1259     // This makes sure that we don't have a focused iframe. Otherwise, that
1260     // iframe would keep focus when the SetFocus called immediately after
1261     // LoadRequest, thus making some tests fail (see http://b/issue?id=845337
1262     // for more details).
1263     webView()->setFocusedFrame(frame);
1264     m_shell->setFocus(webView(), true);
1265
1266     return true;
1267 }
1268
1269 // Private functions ----------------------------------------------------------
1270
1271 LayoutTestController* WebViewHost::layoutTestController() const
1272 {
1273     return m_shell->layoutTestController();
1274 }
1275
1276 void WebViewHost::updateAddressBar(WebView* webView)
1277 {
1278     WebFrame* mainFrame = webView->mainFrame();
1279     WebDataSource* dataSource = mainFrame->dataSource();
1280     if (!dataSource)
1281         dataSource = mainFrame->provisionalDataSource();
1282     if (!dataSource)
1283         return;
1284
1285     setAddressBarURL(dataSource->request().url());
1286 }
1287
1288 void WebViewHost::locationChangeDone(WebFrame* frame)
1289 {
1290     if (frame != m_topLoadingFrame)
1291         return;
1292     m_topLoadingFrame = 0;
1293     layoutTestController()->locationChangeDone();
1294 }
1295
1296 void WebViewHost::updateForCommittedLoad(WebFrame* frame, bool isNewNavigation)
1297 {
1298     // Code duplicated from RenderView::DidCommitLoadForFrame.
1299     TestShellExtraData* extraData = static_cast<TestShellExtraData*>(frame->dataSource()->extraData());
1300
1301     if (isNewNavigation) {
1302         // New navigation.
1303         updateSessionHistory(frame);
1304         m_pageId = nextPageID++;
1305     } else if (extraData && extraData->pendingPageID != -1 && !extraData->requestCommitted) {
1306         // This is a successful session history navigation!
1307         updateSessionHistory(frame);
1308         m_pageId = extraData->pendingPageID;
1309     }
1310
1311     // Don't update session history multiple times.
1312     if (extraData)
1313         extraData->requestCommitted = true;
1314
1315     updateURL(frame);
1316 }
1317
1318 void WebViewHost::updateURL(WebFrame* frame)
1319 {
1320     WebDataSource* ds = frame->dataSource();
1321     ASSERT(ds);
1322     const WebURLRequest& request = ds->request();
1323     RefPtr<TestNavigationEntry> entry(TestNavigationEntry::create());
1324
1325     // The referrer will be empty on https->http transitions. It
1326     // would be nice if we could get the real referrer from somewhere.
1327     entry->setPageID(m_pageId);
1328     if (ds->hasUnreachableURL())
1329         entry->setURL(ds->unreachableURL());
1330     else
1331         entry->setURL(request.url());
1332
1333     const WebHistoryItem& historyItem = frame->currentHistoryItem();
1334     if (!historyItem.isNull())
1335         entry->setContentState(historyItem);
1336
1337     navigationController()->didNavigateToEntry(entry.get());
1338     updateAddressBar(frame->view());
1339     m_lastPageIdUpdated = max(m_lastPageIdUpdated, m_pageId);
1340 }
1341
1342 void WebViewHost::updateSessionHistory(WebFrame* frame)
1343 {
1344     // If we have a valid page ID at this point, then it corresponds to the page
1345     // we are navigating away from.  Otherwise, this is the first navigation, so
1346     // there is no past session history to record.
1347     if (m_pageId == -1)
1348         return;
1349
1350     TestNavigationEntry* entry = navigationController()->entryWithPageID(m_pageId);
1351     if (!entry)
1352         return;
1353
1354     const WebHistoryItem& historyItem = webView()->mainFrame()->previousHistoryItem();
1355     if (historyItem.isNull())
1356         return;
1357
1358     entry->setContentState(historyItem);
1359 }
1360
1361 void WebViewHost::printFrameDescription(WebFrame* webframe)
1362 {
1363     string name8 = webframe->name().utf8();
1364     if (webframe == webView()->mainFrame()) {
1365         if (!name8.length()) {
1366             fputs("main frame", stdout);
1367             return;
1368         }
1369         printf("main frame \"%s\"", name8.c_str());
1370         return;
1371     }
1372     if (!name8.length()) {
1373         fputs("frame (anonymous)", stdout);
1374         return;
1375     }
1376     printf("frame \"%s\"", name8.c_str());
1377 }
1378
1379 void WebViewHost::printFrameUserGestureStatus(WebFrame* webframe, const char* msg)
1380 {
1381     bool isUserGesture = webframe->isProcessingUserGesture();
1382     printf("Frame with user gesture \"%s\"%s", isUserGesture ? "true" : "false", msg);
1383 }
1384
1385 void WebViewHost::printResourceDescription(unsigned identifier)
1386 {
1387     ResourceMap::iterator it = m_resourceIdentifierMap.find(identifier);
1388     printf("%s", it != m_resourceIdentifierMap.end() ? it->second.c_str() : "<unknown>");
1389 }
1390
1391 void WebViewHost::setPendingExtraData(TestShellExtraData* extraData)
1392 {
1393     m_pendingExtraData.set(extraData);
1394 }
1395
1396 void WebViewHost::setPageTitle(const WebString&)
1397 {
1398     // Nothing to do in layout test.
1399 }
1400
1401 void WebViewHost::setAddressBarURL(const WebURL&)
1402 {
1403     // Nothing to do in layout test.
1404 }
1405
1406 // Painting functions ---------------------------------------------------------
1407
1408 class WebViewHostPaintTask : public MethodTask<WebViewHost> {
1409 public:
1410     WebViewHostPaintTask(WebViewHost* object)
1411         : MethodTask<WebViewHost>(object) {}
1412     virtual void runIfValid() { m_object->paintInvalidatedRegion(); }
1413 };
1414
1415 void WebViewHost::updatePaintRect(const WebRect& rect)
1416 {
1417     // m_paintRect = m_paintRect U rect
1418     if (rect.isEmpty())
1419         return;
1420     if (m_paintRect.isEmpty()) {
1421         m_paintRect = rect;
1422         return;
1423     }
1424     int left = min(m_paintRect.x, rect.x);
1425     int top = min(m_paintRect.y, rect.y);
1426     int right = max(m_paintRect.x + m_paintRect.width, rect.x + rect.width);
1427     int bottom = max(m_paintRect.y + m_paintRect.height, rect.y + rect.height);
1428     m_paintRect = WebRect(left, top, right - left, bottom - top);
1429
1430     postDelayedTask(new WebViewHostPaintTask(this), 0);
1431 }
1432
1433 void WebViewHost::paintRect(const WebRect& rect)
1434 {
1435     ASSERT(!m_isPainting);
1436     ASSERT(canvas());
1437     m_isPainting = true;
1438 #if PLATFORM(CG)
1439     webWidget()->paint(canvas()->getTopPlatformDevice().GetBitmapContext(), rect);
1440 #else
1441     webWidget()->paint(canvas(), rect);
1442 #endif
1443     m_isPainting = false;
1444 }
1445
1446 void WebViewHost::paintInvalidatedRegion()
1447 {
1448     webWidget()->layout();
1449     WebSize widgetSize = webWidget()->size();
1450     WebRect clientRect(0, 0, widgetSize.width, widgetSize.height);
1451
1452     // Paint the canvas if necessary.  Allow painting to generate extra rects
1453     // for the first two calls. This is necessary because some WebCore rendering
1454     // objects update their layout only when painted.
1455     // Store the total area painted in total_paint. Then tell the gdk window
1456     // to update that area after we're done painting it.
1457     for (int i = 0; i < 3; ++i) {
1458         // m_paintRect = intersect(m_paintRect , clientRect)
1459         int left = max(m_paintRect.x, clientRect.x);
1460         int top = max(m_paintRect.y, clientRect.y);
1461         int right = min(m_paintRect.x + m_paintRect.width, clientRect.x + clientRect.width);
1462         int bottom = min(m_paintRect.y + m_paintRect.height, clientRect.y + clientRect.height);
1463         if (left >= right || top >= bottom)
1464             m_paintRect = WebRect();
1465         else
1466             m_paintRect = WebRect(left, top, right - left, bottom - top);
1467
1468         if (m_paintRect.isEmpty())
1469             continue;
1470         WebRect rect(m_paintRect);
1471         m_paintRect = WebRect();
1472         paintRect(rect);
1473     }
1474     ASSERT(m_paintRect.isEmpty());
1475 }
1476
1477 PlatformCanvas* WebViewHost::canvas()
1478 {
1479     if (m_canvas)
1480         return m_canvas.get();
1481     WebSize widgetSize = webWidget()->size();
1482     resetScrollRect();
1483     m_canvas.set(new PlatformCanvas(widgetSize.width, widgetSize.height, true));
1484     return m_canvas.get();
1485 }
1486
1487 void WebViewHost::resetScrollRect()
1488 {
1489 }
1490
1491 void WebViewHost::discardBackingStore()
1492 {
1493     m_canvas.clear();
1494 }
1495
1496 // Paints the entire canvas a semi-transparent black (grayish). This is used
1497 // by the layout tests in fast/repaint. The alpha value matches upstream.
1498 void WebViewHost::displayRepaintMask()
1499 {
1500     canvas()->drawARGB(167, 0, 0, 0);
1501 }