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