2011-01-07 James Robinson <jamesr@chromium.org>
[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 #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->createNewWindow(WebURL())->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     m_spellcheck.spellCheckWord(text, &misspelledOffset, &misspelledLength);
414 }
415
416 WebString WebViewHost::autoCorrectWord(const WebString&)
417 {
418     // Returns an empty string as Mac WebKit ('WebKitSupport/WebEditorClient.mm')
419     // does. (If this function returns a non-empty string, WebKit replaces the
420     // given misspelled string with the result one. This process executes some
421     // editor commands and causes layout-test failures.)
422     return WebString();
423 }
424
425 void WebViewHost::runModalAlertDialog(WebFrame*, const WebString& message)
426 {
427     printf("ALERT: %s\n", message.utf8().data());
428 }
429
430 bool WebViewHost::runModalConfirmDialog(WebFrame*, const WebString& message)
431 {
432     printf("CONFIRM: %s\n", message.utf8().data());
433     return true;
434 }
435
436 bool WebViewHost::runModalPromptDialog(WebFrame* frame, const WebString& message,
437                                        const WebString& defaultValue, WebString*)
438 {
439     printf("PROMPT: %s, default text: %s\n", message.utf8().data(), defaultValue.utf8().data());
440     return true;
441 }
442
443 bool WebViewHost::runModalBeforeUnloadDialog(WebFrame*, const WebString&)
444 {
445     return true; // Allow window closure.
446 }
447
448 void WebViewHost::showContextMenu(WebFrame*, const WebContextMenuData& contextMenuData)
449 {
450     m_lastContextMenuData = adoptPtr(new WebContextMenuData(contextMenuData));
451 }
452
453 void WebViewHost::clearContextMenuData()
454 {
455     m_lastContextMenuData.clear();
456 }
457
458 WebContextMenuData* WebViewHost::lastContextMenuData() const
459 {
460     return m_lastContextMenuData.get();
461 }
462
463 void WebViewHost::setStatusText(const WebString& text)
464 {
465     if (!layoutTestController()->shouldDumpStatusCallbacks())
466         return;
467     // When running tests, write to stdout.
468     printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", text.utf8().data());
469 }
470
471 void WebViewHost::startDragging(const WebDragData& data, WebDragOperationsMask mask, const WebImage&, const WebPoint&)
472 {
473     WebDragData mutableDragData = data;
474     if (layoutTestController()->shouldAddFileToPasteboard()) {
475         // Add a file called DRTFakeFile to the drag&drop clipboard.
476         addDRTFakeFileToDataObject(&mutableDragData);
477     }
478
479     // When running a test, we need to fake a drag drop operation otherwise
480     // Windows waits for real mouse events to know when the drag is over.
481     m_shell->eventSender()->doDragDrop(mutableDragData, mask);
482 }
483
484 void WebViewHost::navigateBackForwardSoon(int offset)
485 {
486     navigationController()->goToOffset(offset);
487 }
488
489 int WebViewHost::historyBackListCount()
490 {
491     return navigationController()->lastCommittedEntryIndex();
492 }
493
494 int WebViewHost::historyForwardListCount()
495 {
496     int currentIndex =navigationController()->lastCommittedEntryIndex();
497     return navigationController()->entryCount() - currentIndex - 1;
498 }
499
500 void WebViewHost::postAccessibilityNotification(const WebAccessibilityObject& obj, WebAccessibilityNotification notification)
501 {
502     if (notification == WebAccessibilityNotificationFocusedUIElementChanged)
503         m_shell->accessibilityController()->setFocusedElement(obj);
504
505     if (m_shell->accessibilityController()->shouldDumpAccessibilityNotifications()) {
506         printf("AccessibilityNotification - ");
507
508         switch (notification) {
509         case WebAccessibilityNotificationActiveDescendantChanged:
510             printf("ActiveDescendantChanged");
511             break;
512         case WebAccessibilityNotificationCheckedStateChanged:
513             printf("CheckedStateChanged");
514             break;
515         case WebAccessibilityNotificationChildrenChanged:
516             printf("ChildrenChanged");
517             break;
518         case WebAccessibilityNotificationFocusedUIElementChanged:
519             printf("FocusedUIElementChanged");
520             break;
521         case WebAccessibilityNotificationLayoutComplete:
522             printf("LayoutComplete");
523             break;
524         case WebAccessibilityNotificationLoadComplete:
525             printf("LoadComplete");
526             break;
527         case WebAccessibilityNotificationSelectedChildrenChanged:
528             printf("SelectedChildrenChanged");
529             break;
530         case WebAccessibilityNotificationSelectedTextChanged:
531             printf("SelectedTextChanged");
532             break;
533         case WebAccessibilityNotificationValueChanged:
534             printf("ValueChanged");
535             break;
536         case WebAccessibilityNotificationScrolledToAnchor:
537             printf("ScrolledToAnchor");
538             break;
539         case WebAccessibilityNotificationLiveRegionChanged:
540             printf("LiveRegionChanged");
541             break;
542         case WebAccessibilityNotificationMenuListValueChanged:
543             printf("MenuListValueChanged");
544             break;
545         case WebAccessibilityNotificationRowCountChanged:
546             printf("RowCountChanged");
547             break;
548         case WebAccessibilityNotificationRowCollapsed:
549             printf("RowCollapsed");
550             break;
551         case WebAccessibilityNotificationRowExpanded:
552             printf("RowExpanded");
553             break;
554         default:
555             break;
556         }
557
558         WebKit::WebNode node = obj.node();
559         if (!node.isNull() && node.isElementNode()) {
560             WebKit::WebElement element = node.to<WebKit::WebElement>();
561             if (element.hasAttribute("id"))
562                 printf(" - id:%s", element.getAttribute("id").utf8().data());
563         }
564
565         printf("\n");
566     }
567 }
568
569 WebNotificationPresenter* WebViewHost::notificationPresenter()
570 {
571     return m_shell->notificationPresenter();
572 }
573
574 #if ENABLE(CLIENT_BASED_GEOLOCATION)
575 WebKit::WebGeolocationClient* WebViewHost::geolocationClient()
576 {
577     return geolocationClientMock();
578 }
579
580 WebKit::WebGeolocationClientMock* WebViewHost::geolocationClientMock()
581 {
582     if (!m_geolocationClientMock)
583         m_geolocationClientMock.set(WebGeolocationClientMock::create());
584     return m_geolocationClientMock.get();
585 }
586 #else
587 WebKit::WebGeolocationService* WebViewHost::geolocationService()
588 {
589     if (!m_geolocationServiceMock)
590         m_geolocationServiceMock.set(WebGeolocationServiceMock::createWebGeolocationServiceMock());
591     return m_geolocationServiceMock.get();
592 }
593 #endif
594
595 WebSpeechInputController* WebViewHost::speechInputController(WebKit::WebSpeechInputListener* listener)
596 {
597     if (!m_speechInputControllerMock)
598         m_speechInputControllerMock.set(WebSpeechInputControllerMock::create(listener));
599     return m_speechInputControllerMock.get();
600 }
601
602 WebDeviceOrientationClientMock* WebViewHost::deviceOrientationClientMock()
603 {
604     if (!m_deviceOrientationClientMock.get())
605         m_deviceOrientationClientMock.set(WebDeviceOrientationClientMock::create());
606     return m_deviceOrientationClientMock.get();
607 }
608
609 MockSpellCheck* WebViewHost::mockSpellCheck()
610 {
611     return &m_spellcheck;
612 }
613
614 WebDeviceOrientationClient* WebViewHost::deviceOrientationClient()
615 {
616     return deviceOrientationClientMock();
617 }
618
619 // WebWidgetClient -----------------------------------------------------------
620
621 void WebViewHost::didInvalidateRect(const WebRect& rect)
622 {
623     updatePaintRect(rect);
624 }
625
626 void WebViewHost::didScrollRect(int, int, const WebRect& clipRect)
627 {
628     // This is used for optimizing painting when the renderer is scrolled. We're
629     // currently not doing any optimizations so just invalidate the region.
630     didInvalidateRect(clipRect);
631 }
632
633 void WebViewHost::scheduleComposite()
634 {
635     WebSize widgetSize = webWidget()->size();
636     WebRect clientRect(0, 0, widgetSize.width, widgetSize.height);
637     didInvalidateRect(clientRect);
638 }
639
640 void WebViewHost::didFocus()
641 {
642     m_shell->setFocus(webWidget(), true);
643 }
644
645 void WebViewHost::didBlur()
646 {
647     m_shell->setFocus(webWidget(), false);
648 }
649
650 WebScreenInfo WebViewHost::screenInfo()
651 {
652     // We don't need to set actual values.
653     WebScreenInfo info;
654     info.depth = 24;
655     info.depthPerComponent = 8;
656     info.isMonochrome = false;
657     info.rect = WebRect(0, 0, screenWidth, screenHeight);
658     // Use values different from info.rect for testing.
659     info.availableRect = WebRect(screenUnavailableBorder, screenUnavailableBorder,
660                                  screenWidth - screenUnavailableBorder * 2,
661                                  screenHeight - screenUnavailableBorder * 2);
662     return info;
663 }
664
665 void WebViewHost::show(WebNavigationPolicy)
666 {
667     m_hasWindow = true;
668     WebSize size = webWidget()->size();
669     updatePaintRect(WebRect(0, 0, size.width, size.height));
670 }
671
672
673
674 void WebViewHost::closeWidget()
675 {
676     m_hasWindow = false;
677     m_shell->closeWindow(this);
678     // No more code here, we should be deleted at this point.
679 }
680
681 static void invokeCloseWidget(void* context)
682 {
683     WebViewHost* wvh = static_cast<WebViewHost*>(context);
684     wvh->closeWidget();
685 }
686
687 void WebViewHost::closeWidgetSoon()
688 {
689     webkit_support::PostDelayedTask(invokeCloseWidget, static_cast<void*>(this), 0);
690 }
691
692 void WebViewHost::didChangeCursor(const WebCursorInfo& cursorInfo)
693 {
694     if (!hasWindow())
695         return;
696     m_currentCursor = cursorInfo;
697 }
698
699 WebRect WebViewHost::windowRect()
700 {
701     return m_windowRect;
702 }
703
704 void WebViewHost::setWindowRect(const WebRect& rect)
705 {
706     m_windowRect = rect;
707     const int border2 = TestShell::virtualWindowBorder * 2;
708     if (m_windowRect.width <= border2)
709         m_windowRect.width = 1 + border2;
710     if (m_windowRect.height <= border2)
711         m_windowRect.height = 1 + border2;
712     int width = m_windowRect.width - border2;
713     int height = m_windowRect.height - border2;
714     discardBackingStore();
715     webWidget()->resize(WebSize(width, height));
716     updatePaintRect(WebRect(0, 0, width, height));
717 }
718
719 WebRect WebViewHost::rootWindowRect()
720 {
721     return windowRect();
722 }
723
724 WebRect WebViewHost::windowResizerRect()
725 {
726     // Not necessary.
727     return WebRect();
728 }
729
730 void WebViewHost::runModal()
731 {
732     bool oldState = webkit_support::MessageLoopNestableTasksAllowed();
733     webkit_support::MessageLoopSetNestableTasksAllowed(true);
734     m_inModalLoop = true;
735     webkit_support::RunMessageLoop();
736     webkit_support::MessageLoopSetNestableTasksAllowed(oldState);
737 }
738
739 // WebFrameClient ------------------------------------------------------------
740
741 WebPlugin* WebViewHost::createPlugin(WebFrame* frame, const WebPluginParams& params)
742 {
743     return webkit_support::CreateWebPlugin(frame, params);
744 }
745
746 WebWorker* WebViewHost::createWorker(WebFrame*, WebWorkerClient*)
747 {
748     return new TestWebWorker();
749 }
750
751 WebMediaPlayer* WebViewHost::createMediaPlayer(WebFrame* frame, WebMediaPlayerClient* client)
752 {
753     return webkit_support::CreateMediaPlayer(frame, client);
754 }
755
756 WebApplicationCacheHost* WebViewHost::createApplicationCacheHost(WebFrame* frame, WebApplicationCacheHostClient* client)
757 {
758     return webkit_support::CreateApplicationCacheHost(frame, client);
759 }
760
761 bool WebViewHost::allowPlugins(WebFrame* frame, bool enabledPerSettings)
762 {
763     return enabledPerSettings;
764 }
765
766 bool WebViewHost::allowImages(WebFrame* frame, bool enabledPerSettings)
767 {
768     return enabledPerSettings;
769 }
770
771 void WebViewHost::loadURLExternally(WebFrame*, const WebURLRequest& request, WebNavigationPolicy policy)
772 {
773     ASSERT(policy !=  WebKit::WebNavigationPolicyCurrentTab);
774     WebViewHost* another = m_shell->createNewWindow(request.url());
775     if (another)
776         another->show(policy);
777 }
778
779 WebNavigationPolicy WebViewHost::decidePolicyForNavigation(
780     WebFrame*, const WebURLRequest& request,
781     WebNavigationType type, const WebNode& originatingNode,
782     WebNavigationPolicy defaultPolicy, bool isRedirect)
783 {
784     WebNavigationPolicy result;
785     if (!m_policyDelegateEnabled)
786         return defaultPolicy;
787
788     printf("Policy delegate: attempt to load %s with navigation type '%s'",
789            URLDescription(request.url()).c_str(), webNavigationTypeToString(type));
790     if (!originatingNode.isNull()) {
791         fputs(" originating from ", stdout);
792         printNodeDescription(originatingNode, 0);
793     }
794     fputs("\n", stdout);
795     if (m_policyDelegateIsPermissive)
796         result = WebKit::WebNavigationPolicyCurrentTab;
797     else
798         result = WebKit::WebNavigationPolicyIgnore;
799
800     if (m_policyDelegateShouldNotifyDone)
801         layoutTestController()->policyDelegateDone();
802     return result;
803 }
804
805 bool WebViewHost::canHandleRequest(WebFrame*, const WebURLRequest& request)
806 {
807     GURL url = request.url();
808     // Just reject the scheme used in
809     // LayoutTests/http/tests/misc/redirect-to-external-url.html
810     return !url.SchemeIs("spaceballs");
811 }
812
813 WebURLError WebViewHost::cannotHandleRequestError(WebFrame*, const WebURLRequest& request)
814 {
815     WebURLError error;
816     // A WebKit layout test expects the following values.
817     // unableToImplementPolicyWithError() below prints them.
818     error.domain = WebString::fromUTF8("WebKitErrorDomain");
819     error.reason = 101;
820     error.unreachableURL = request.url();
821     return error;
822 }
823
824 WebURLError WebViewHost::cancelledError(WebFrame*, const WebURLRequest& request)
825 {
826     return webkit_support::CreateCancelledError(request);
827 }
828
829 void WebViewHost::unableToImplementPolicyWithError(WebFrame* frame, const WebURLError& error)
830 {
831     printf("Policy delegate: unable to implement policy with error domain '%s', "
832            "error code %d, in frame '%s'\n",
833            error.domain.utf8().data(), error.reason, frame->name().utf8().data());
834 }
835
836 void WebViewHost::willPerformClientRedirect(WebFrame* frame, const WebURL& from, const WebURL& to,
837                                             double interval, double fire_time)
838 {
839     if (!m_shell->shouldDumpFrameLoadCallbacks())
840         return;
841     printFrameDescription(frame);
842     printf(" - willPerformClientRedirectToURL: %s \n", to.spec().data());
843 }
844
845 void WebViewHost::didCancelClientRedirect(WebFrame* frame)
846 {
847     if (!m_shell->shouldDumpFrameLoadCallbacks())
848         return;
849     printFrameDescription(frame);
850     fputs(" - didCancelClientRedirectForFrame\n", stdout);
851 }
852
853 void WebViewHost::didCreateDataSource(WebFrame*, WebDataSource* ds)
854 {
855     ds->setExtraData(m_pendingExtraData.leakPtr());
856     if (!layoutTestController()->deferMainResourceDataLoad())
857         ds->setDeferMainResourceDataLoad(false);
858 }
859
860 void WebViewHost::didStartProvisionalLoad(WebFrame* frame)
861 {
862     if (m_shell->shouldDumpUserGestureInFrameLoadCallbacks())
863         printFrameUserGestureStatus(frame, " - in didStartProvisionalLoadForFrame\n");
864     if (m_shell->shouldDumpFrameLoadCallbacks()) {
865         printFrameDescription(frame);
866         fputs(" - didStartProvisionalLoadForFrame\n", stdout);
867     }
868
869     if (!m_topLoadingFrame)
870         m_topLoadingFrame = frame;
871
872     if (layoutTestController()->stopProvisionalFrameLoads()) {
873         printFrameDescription(frame);
874         fputs(" - stopping load in didStartProvisionalLoadForFrame callback\n", stdout);
875         frame->stopLoading();
876     }
877     updateAddressBar(frame->view());
878 }
879
880 void WebViewHost::didReceiveServerRedirectForProvisionalLoad(WebFrame* frame)
881 {
882     if (m_shell->shouldDumpFrameLoadCallbacks()) {
883         printFrameDescription(frame);
884         fputs(" - didReceiveServerRedirectForProvisionalLoadForFrame\n", stdout);
885     }
886     updateAddressBar(frame->view());
887 }
888
889 void WebViewHost::didFailProvisionalLoad(WebFrame* frame, const WebURLError& error)
890 {
891     if (m_shell->shouldDumpFrameLoadCallbacks()) {
892         printFrameDescription(frame);
893         fputs(" - didFailProvisionalLoadWithError\n", stdout);
894     }
895
896     locationChangeDone(frame);
897
898     // Don't display an error page if we're running layout tests, because
899     // DumpRenderTree doesn't.
900 }
901
902 void WebViewHost::didCommitProvisionalLoad(WebFrame* frame, bool isNewNavigation)
903 {
904     if (m_shell->shouldDumpFrameLoadCallbacks()) {
905         printFrameDescription(frame);
906         fputs(" - didCommitLoadForFrame\n", stdout);
907     }
908     updateForCommittedLoad(frame, isNewNavigation);
909 }
910
911 void WebViewHost::didClearWindowObject(WebFrame* frame)
912 {
913     m_shell->bindJSObjectsToWindow(frame);
914 }
915
916 void WebViewHost::didReceiveTitle(WebFrame* frame, const WebString& title)
917 {
918     WebCString title8 = title.utf8();
919
920     if (m_shell->shouldDumpFrameLoadCallbacks()) {
921         printFrameDescription(frame);
922         printf(" - didReceiveTitle: %s\n", title8.data());
923     }
924
925     if (layoutTestController()->shouldDumpTitleChanges())
926         printf("TITLE CHANGED: %s\n", title8.data());
927
928     setPageTitle(title);
929 }
930
931 void WebViewHost::didFinishDocumentLoad(WebFrame* frame)
932 {
933     if (m_shell->shouldDumpFrameLoadCallbacks()) {
934         printFrameDescription(frame);
935         fputs(" - didFinishDocumentLoadForFrame\n", stdout);
936     } else {
937         unsigned pendingUnloadEvents = frame->unloadListenerCount();
938         if (pendingUnloadEvents) {
939             printFrameDescription(frame);
940             printf(" - has %u onunload handler(s)\n", pendingUnloadEvents);
941         }
942     }
943 }
944
945 void WebViewHost::didHandleOnloadEvents(WebFrame* frame)
946 {
947     if (m_shell->shouldDumpFrameLoadCallbacks()) {
948         printFrameDescription(frame);
949         fputs(" - didHandleOnloadEventsForFrame\n", stdout);
950     }
951 }
952
953 void WebViewHost::didFailLoad(WebFrame* frame, const WebURLError& error)
954 {
955     if (m_shell->shouldDumpFrameLoadCallbacks()) {
956         printFrameDescription(frame);
957         fputs(" - didFailLoadWithError\n", stdout);
958     }
959     locationChangeDone(frame);
960 }
961
962 void WebViewHost::didFinishLoad(WebFrame* frame)
963 {
964     if (m_shell->shouldDumpFrameLoadCallbacks()) {
965         printFrameDescription(frame);
966         fputs(" - didFinishLoadForFrame\n", stdout);
967     }
968     updateAddressBar(frame->view());
969     locationChangeDone(frame);
970 }
971
972 void WebViewHost::didNavigateWithinPage(WebFrame* frame, bool isNewNavigation)
973 {
974     frame->dataSource()->setExtraData(m_pendingExtraData.leakPtr());
975
976     updateForCommittedLoad(frame, isNewNavigation);
977 }
978
979 void WebViewHost::didChangeLocationWithinPage(WebFrame* frame)
980 {
981     if (m_shell->shouldDumpFrameLoadCallbacks()) {
982         printFrameDescription(frame);
983         fputs(" - didChangeLocationWithinPageForFrame\n", stdout);
984     }
985 }
986
987 void WebViewHost::assignIdentifierToRequest(WebFrame*, unsigned identifier, const WebURLRequest& request)
988 {
989      if (!m_shell->shouldDumpResourceLoadCallbacks())
990         return;
991     ASSERT(!m_resourceIdentifierMap.contains(identifier));
992     m_resourceIdentifierMap.set(identifier, descriptionSuitableForTestResult(request.url().spec()));
993 }
994
995 void WebViewHost::removeIdentifierForRequest(unsigned identifier)
996 {
997     m_resourceIdentifierMap.remove(identifier);
998 }
999
1000 void WebViewHost::willSendRequest(WebFrame*, unsigned identifier, WebURLRequest& request, const WebURLResponse& redirectResponse)
1001 {
1002     // Need to use GURL for host() and SchemeIs()
1003     GURL url = request.url();
1004     string requestURL = url.possibly_invalid_spec();
1005
1006     if (layoutTestController()->shouldDumpResourceLoadCallbacks()) {
1007         GURL mainDocumentURL = request.firstPartyForCookies();
1008         printResourceDescription(identifier);
1009         printf(" - willSendRequest <NSURLRequest URL %s, main document URL %s,"
1010                " http method %s> redirectResponse ",
1011                descriptionSuitableForTestResult(requestURL).c_str(),
1012                URLDescription(mainDocumentURL).c_str(),
1013                request.httpMethod().utf8().data());
1014         printResponseDescription(redirectResponse);
1015         fputs("\n", stdout);
1016     }
1017
1018     if (!redirectResponse.isNull() && m_blocksRedirects) {
1019         fputs("Returning null for this redirect\n", stdout);
1020         // To block the request, we set its URL to an empty one.
1021         request.setURL(WebURL());
1022         return;
1023     }
1024
1025     if (m_requestReturnNull) {
1026         // To block the request, we set its URL to an empty one.
1027         request.setURL(WebURL());
1028         return;
1029     }
1030
1031     string host = url.host();
1032     // 255.255.255.255 is used in some tests that expect to get back an error.
1033     if (!host.empty() && (url.SchemeIs("http") || url.SchemeIs("https"))
1034         && host != "127.0.0.1"
1035         && host != "255.255.255.255"
1036         && host != "localhost"
1037         && !m_shell->allowExternalPages()) {
1038         printf("Blocked access to external URL %s\n", requestURL.c_str());
1039
1040         // To block the request, we set its URL to an empty one.
1041         request.setURL(WebURL());
1042         return;
1043     }
1044
1045     HashSet<String>::const_iterator end = m_clearHeaders.end();
1046     for (HashSet<String>::const_iterator header = m_clearHeaders.begin(); header != end; ++header)
1047         request.clearHTTPHeaderField(WebString(header->characters(), header->length()));
1048
1049     // Set the new substituted URL.
1050     request.setURL(webkit_support::RewriteLayoutTestsURL(request.url().spec()));
1051 }
1052
1053 void WebViewHost::didReceiveResponse(WebFrame*, unsigned identifier, const WebURLResponse& response)
1054 {
1055     if (m_shell->shouldDumpResourceLoadCallbacks()) {
1056         printResourceDescription(identifier);
1057         fputs(" - didReceiveResponse ", stdout);
1058         printResponseDescription(response);
1059         fputs("\n", stdout);
1060     }
1061     if (m_shell->shouldDumpResourceResponseMIMETypes()) {
1062         GURL url = response.url();
1063         WebString mimeType = response.mimeType();
1064         printf("%s has MIME type %s\n",
1065             url.ExtractFileName().c_str(),
1066             // Simulate NSURLResponse's mapping of empty/unknown MIME types to application/octet-stream
1067             mimeType.isEmpty() ? "application/octet-stream" : mimeType.utf8().data());
1068     }
1069 }
1070
1071 void WebViewHost::didFinishResourceLoad(WebFrame*, unsigned identifier)
1072 {
1073     if (m_shell->shouldDumpResourceLoadCallbacks()) {
1074         printResourceDescription(identifier);
1075         fputs(" - didFinishLoading\n", stdout);
1076     }
1077     removeIdentifierForRequest(identifier);
1078 }
1079
1080 void WebViewHost::didFailResourceLoad(WebFrame*, unsigned identifier, const WebURLError& error)
1081 {
1082     if (m_shell->shouldDumpResourceLoadCallbacks()) {
1083         printResourceDescription(identifier);
1084         fputs(" - didFailLoadingWithError: ", stdout);
1085         fputs(webkit_support::MakeURLErrorDescription(error).c_str(), stdout);
1086         fputs("\n", stdout);
1087     }
1088     removeIdentifierForRequest(identifier);
1089 }
1090
1091 void WebViewHost::didDisplayInsecureContent(WebFrame*)
1092 {
1093     if (m_shell->shouldDumpFrameLoadCallbacks())
1094         fputs("didDisplayInsecureContent\n", stdout);
1095 }
1096
1097 void WebViewHost::didRunInsecureContent(WebFrame*, const WebSecurityOrigin& origin)
1098 {
1099     if (m_shell->shouldDumpFrameLoadCallbacks())
1100         fputs("didRunInsecureContent\n", stdout);
1101 }
1102
1103 bool WebViewHost::allowScript(WebFrame*, bool enabledPerSettings)
1104 {
1105     return enabledPerSettings;
1106 }
1107
1108 void WebViewHost::openFileSystem(WebFrame* frame, WebFileSystem::Type type, long long size, bool create, WebFileSystemCallbacks* callbacks)
1109 {
1110     webkit_support::OpenFileSystem(frame, type, size, create, callbacks);
1111 }
1112
1113 // Public functions -----------------------------------------------------------
1114
1115 WebViewHost::WebViewHost(TestShell* shell)
1116     : m_shell(shell)
1117     , m_webWidget(0)
1118 {
1119     reset();
1120 }
1121
1122 WebViewHost::~WebViewHost()
1123 {
1124     // DevTools frontend page is supposed to be navigated only once and
1125     // loading another URL in that Page is an error.
1126     if (m_shell->devToolsWebView() != this) {
1127         // Navigate to an empty page to fire all the destruction logic for the
1128         // current page.
1129         loadURLForFrame(GURL("about:blank"), WebString());
1130     }
1131
1132     webWidget()->close();
1133
1134     if (m_inModalLoop)
1135         webkit_support::QuitMessageLoop();
1136 }
1137
1138 WebView* WebViewHost::webView() const
1139 {
1140     ASSERT(m_webWidget);
1141     // DRT does not support popup widgets. So m_webWidget is always a WebView.
1142     return static_cast<WebView*>(m_webWidget);
1143 }
1144
1145 WebWidget* WebViewHost::webWidget() const
1146 {
1147     ASSERT(m_webWidget);
1148     return m_webWidget;
1149 }
1150
1151 void WebViewHost::reset()
1152 {
1153     m_policyDelegateEnabled = false;
1154     m_policyDelegateIsPermissive = false;
1155     m_policyDelegateShouldNotifyDone = false;
1156     m_topLoadingFrame = 0;
1157     m_pageId = -1;
1158     m_lastPageIdUpdated = -1;
1159     m_hasWindow = false;
1160     m_inModalLoop = false;
1161     m_smartInsertDeleteEnabled = true;
1162 #if OS(WINDOWS)
1163     m_selectTrailingWhitespaceEnabled = true;
1164 #else
1165     m_selectTrailingWhitespaceEnabled = false;
1166 #endif
1167     m_blocksRedirects = false;
1168     m_requestReturnNull = false;
1169     m_isPainting = false;
1170     m_canvas.clear();
1171
1172     m_navigationController.set(new TestNavigationController(this));
1173
1174     m_pendingExtraData.clear();
1175     m_resourceIdentifierMap.clear();
1176     m_clearHeaders.clear();
1177     m_editCommandName.clear();
1178     m_editCommandValue.clear();
1179
1180 #if ENABLE(CLIENT_BASED_GEOLOCATION)
1181     if (m_geolocationClientMock.get())
1182         m_geolocationClientMock->resetMock();
1183 #else
1184     m_geolocationServiceMock.clear();
1185 #endif
1186
1187     if (m_speechInputControllerMock.get())
1188         m_speechInputControllerMock->clearResults();
1189
1190     m_currentCursor = WebCursorInfo();
1191     m_windowRect = WebRect();
1192     m_paintRect = WebRect();
1193
1194     if (m_webWidget)
1195         webView()->mainFrame()->setName(WebString());
1196 }
1197
1198 void WebViewHost::setSelectTrailingWhitespaceEnabled(bool enabled)
1199 {
1200     m_selectTrailingWhitespaceEnabled = enabled;
1201     // In upstream WebKit, smart insert/delete is mutually exclusive with select
1202     // trailing whitespace, however, we allow both because Chromium on Windows
1203     // allows both.
1204 }
1205
1206 void WebViewHost::setSmartInsertDeleteEnabled(bool enabled)
1207 {
1208     m_smartInsertDeleteEnabled = 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::setCustomPolicyDelegate(bool isCustom, bool isPermissive)
1215 {
1216     m_policyDelegateEnabled = isCustom;
1217     m_policyDelegateIsPermissive = isPermissive;
1218 }
1219
1220 void WebViewHost::waitForPolicyDelegate()
1221 {
1222     m_policyDelegateEnabled = true;
1223     m_policyDelegateShouldNotifyDone = true;
1224 }
1225
1226 void WebViewHost::setEditCommand(const string& name, const string& value)
1227 {
1228     m_editCommandName = name;
1229     m_editCommandValue = value;
1230 }
1231
1232 void WebViewHost::clearEditCommand()
1233 {
1234     m_editCommandName.clear();
1235     m_editCommandValue.clear();
1236 }
1237
1238 void WebViewHost::loadURLForFrame(const WebURL& url, const WebString& frameName)
1239 {
1240     if (!url.isValid())
1241         return;
1242     TestShell::resizeWindowForTest(this, url);
1243     navigationController()->loadEntry(TestNavigationEntry::create(-1, url, WebString(), frameName).get());
1244 }
1245
1246 bool WebViewHost::navigate(const TestNavigationEntry& entry, bool reload)
1247 {
1248     // Get the right target frame for the entry.
1249     WebFrame* frame = webView()->mainFrame();
1250     if (!entry.targetFrame().isEmpty())
1251         frame = webView()->findFrameByName(entry.targetFrame());
1252
1253     // TODO(mpcomplete): should we clear the target frame, or should
1254     // back/forward navigations maintain the target frame?
1255
1256     // A navigation resulting from loading a javascript URL should not be
1257     // treated as a browser initiated event.  Instead, we want it to look as if
1258     // the page initiated any load resulting from JS execution.
1259     if (!GURL(entry.URL()).SchemeIs("javascript"))
1260         setPendingExtraData(new TestShellExtraData(entry.pageID()));
1261
1262     // If we are reloading, then WebKit will use the state of the current page.
1263     // Otherwise, we give it the state to navigate to.
1264     if (reload) {
1265         frame->reload(false);
1266     } else if (!entry.contentState().isNull()) {
1267         ASSERT(entry.pageID() != -1);
1268         frame->loadHistoryItem(entry.contentState());
1269     } else {
1270         ASSERT(entry.pageID() == -1);
1271         frame->loadRequest(WebURLRequest(entry.URL()));
1272     }
1273
1274     // In case LoadRequest failed before DidCreateDataSource was called.
1275     setPendingExtraData(0);
1276
1277     // Restore focus to the main frame prior to loading new request.
1278     // This makes sure that we don't have a focused iframe. Otherwise, that
1279     // iframe would keep focus when the SetFocus called immediately after
1280     // LoadRequest, thus making some tests fail (see http://b/issue?id=845337
1281     // for more details).
1282     webView()->setFocusedFrame(frame);
1283     m_shell->setFocus(webView(), true);
1284
1285     return true;
1286 }
1287
1288 // Private functions ----------------------------------------------------------
1289
1290 LayoutTestController* WebViewHost::layoutTestController() const
1291 {
1292     return m_shell->layoutTestController();
1293 }
1294
1295 void WebViewHost::updateAddressBar(WebView* webView)
1296 {
1297     WebFrame* mainFrame = webView->mainFrame();
1298     WebDataSource* dataSource = mainFrame->dataSource();
1299     if (!dataSource)
1300         dataSource = mainFrame->provisionalDataSource();
1301     if (!dataSource)
1302         return;
1303
1304     setAddressBarURL(dataSource->request().url());
1305 }
1306
1307 void WebViewHost::locationChangeDone(WebFrame* frame)
1308 {
1309     if (frame != m_topLoadingFrame)
1310         return;
1311     m_topLoadingFrame = 0;
1312     layoutTestController()->locationChangeDone();
1313 }
1314
1315 void WebViewHost::updateForCommittedLoad(WebFrame* frame, bool isNewNavigation)
1316 {
1317     // Code duplicated from RenderView::DidCommitLoadForFrame.
1318     TestShellExtraData* extraData = static_cast<TestShellExtraData*>(frame->dataSource()->extraData());
1319
1320     if (isNewNavigation) {
1321         // New navigation.
1322         updateSessionHistory(frame);
1323         m_pageId = nextPageID++;
1324     } else if (extraData && extraData->pendingPageID != -1 && !extraData->requestCommitted) {
1325         // This is a successful session history navigation!
1326         updateSessionHistory(frame);
1327         m_pageId = extraData->pendingPageID;
1328     }
1329
1330     // Don't update session history multiple times.
1331     if (extraData)
1332         extraData->requestCommitted = true;
1333
1334     updateURL(frame);
1335 }
1336
1337 void WebViewHost::updateURL(WebFrame* frame)
1338 {
1339     WebDataSource* ds = frame->dataSource();
1340     ASSERT(ds);
1341     const WebURLRequest& request = ds->request();
1342     RefPtr<TestNavigationEntry> entry(TestNavigationEntry::create());
1343
1344     // The referrer will be empty on https->http transitions. It
1345     // would be nice if we could get the real referrer from somewhere.
1346     entry->setPageID(m_pageId);
1347     if (ds->hasUnreachableURL())
1348         entry->setURL(ds->unreachableURL());
1349     else
1350         entry->setURL(request.url());
1351
1352     const WebHistoryItem& historyItem = frame->currentHistoryItem();
1353     if (!historyItem.isNull())
1354         entry->setContentState(historyItem);
1355
1356     navigationController()->didNavigateToEntry(entry.get());
1357     updateAddressBar(frame->view());
1358     m_lastPageIdUpdated = max(m_lastPageIdUpdated, m_pageId);
1359 }
1360
1361 void WebViewHost::updateSessionHistory(WebFrame* frame)
1362 {
1363     // If we have a valid page ID at this point, then it corresponds to the page
1364     // we are navigating away from.  Otherwise, this is the first navigation, so
1365     // there is no past session history to record.
1366     if (m_pageId == -1)
1367         return;
1368
1369     TestNavigationEntry* entry = navigationController()->entryWithPageID(m_pageId);
1370     if (!entry)
1371         return;
1372
1373     const WebHistoryItem& historyItem = webView()->mainFrame()->previousHistoryItem();
1374     if (historyItem.isNull())
1375         return;
1376
1377     entry->setContentState(historyItem);
1378 }
1379
1380 void WebViewHost::printFrameDescription(WebFrame* webframe)
1381 {
1382     string name8 = webframe->name().utf8();
1383     if (webframe == webView()->mainFrame()) {
1384         if (!name8.length()) {
1385             fputs("main frame", stdout);
1386             return;
1387         }
1388         printf("main frame \"%s\"", name8.c_str());
1389         return;
1390     }
1391     if (!name8.length()) {
1392         fputs("frame (anonymous)", stdout);
1393         return;
1394     }
1395     printf("frame \"%s\"", name8.c_str());
1396 }
1397
1398 void WebViewHost::printFrameUserGestureStatus(WebFrame* webframe, const char* msg)
1399 {
1400     bool isUserGesture = webframe->isProcessingUserGesture();
1401     printf("Frame with user gesture \"%s\"%s", isUserGesture ? "true" : "false", msg);
1402 }
1403
1404 void WebViewHost::printResourceDescription(unsigned identifier)
1405 {
1406     ResourceMap::iterator it = m_resourceIdentifierMap.find(identifier);
1407     printf("%s", it != m_resourceIdentifierMap.end() ? it->second.c_str() : "<unknown>");
1408 }
1409
1410 void WebViewHost::setPendingExtraData(TestShellExtraData* extraData)
1411 {
1412     m_pendingExtraData.set(extraData);
1413 }
1414
1415 void WebViewHost::setPageTitle(const WebString&)
1416 {
1417     // Nothing to do in layout test.
1418 }
1419
1420 void WebViewHost::setAddressBarURL(const WebURL&)
1421 {
1422     // Nothing to do in layout test.
1423 }
1424
1425 // Painting functions ---------------------------------------------------------
1426
1427 void WebViewHost::updatePaintRect(const WebRect& rect)
1428 {
1429     // m_paintRect = m_paintRect U rect
1430     if (rect.isEmpty())
1431         return;
1432     if (m_paintRect.isEmpty()) {
1433         m_paintRect = rect;
1434         return;
1435     }
1436     int left = min(m_paintRect.x, rect.x);
1437     int top = min(m_paintRect.y, rect.y);
1438     int right = max(m_paintRect.x + m_paintRect.width, rect.x + rect.width);
1439     int bottom = max(m_paintRect.y + m_paintRect.height, rect.y + rect.height);
1440     m_paintRect = WebRect(left, top, right - left, bottom - top);
1441 }
1442
1443 void WebViewHost::paintRect(const WebRect& rect)
1444 {
1445     ASSERT(!m_isPainting);
1446     ASSERT(canvas());
1447     m_isPainting = true;
1448 #if PLATFORM(CG)
1449     webWidget()->paint(canvas()->getTopPlatformDevice().GetBitmapContext(), rect);
1450 #else
1451     webWidget()->paint(canvas(), rect);
1452 #endif
1453     m_isPainting = false;
1454 }
1455
1456 void WebViewHost::paintInvalidatedRegion()
1457 {
1458     webWidget()->layout();
1459     WebSize widgetSize = webWidget()->size();
1460     WebRect clientRect(0, 0, widgetSize.width, widgetSize.height);
1461
1462     // Paint the canvas if necessary.  Allow painting to generate extra rects
1463     // for the first two calls. This is necessary because some WebCore rendering
1464     // objects update their layout only when painted.
1465     // Store the total area painted in total_paint. Then tell the gdk window
1466     // to update that area after we're done painting it.
1467     for (int i = 0; i < 3; ++i) {
1468         // m_paintRect = intersect(m_paintRect , clientRect)
1469         int left = max(m_paintRect.x, clientRect.x);
1470         int top = max(m_paintRect.y, clientRect.y);
1471         int right = min(m_paintRect.x + m_paintRect.width, clientRect.x + clientRect.width);
1472         int bottom = min(m_paintRect.y + m_paintRect.height, clientRect.y + clientRect.height);
1473         if (left >= right || top >= bottom)
1474             m_paintRect = WebRect();
1475         else
1476             m_paintRect = WebRect(left, top, right - left, bottom - top);
1477
1478         if (m_paintRect.isEmpty())
1479             continue;
1480         WebRect rect(m_paintRect);
1481         m_paintRect = WebRect();
1482         paintRect(rect);
1483     }
1484     ASSERT(m_paintRect.isEmpty());
1485 }
1486
1487 PlatformCanvas* WebViewHost::canvas()
1488 {
1489     if (m_canvas)
1490         return m_canvas.get();
1491     WebSize widgetSize = webWidget()->size();
1492     resetScrollRect();
1493     m_canvas.set(new PlatformCanvas(widgetSize.width, widgetSize.height, true));
1494     return m_canvas.get();
1495 }
1496
1497 void WebViewHost::resetScrollRect()
1498 {
1499 }
1500
1501 void WebViewHost::discardBackingStore()
1502 {
1503     m_canvas.clear();
1504 }
1505
1506 // Paints the entire canvas a semi-transparent black (grayish). This is used
1507 // by the layout tests in fast/repaint. The alpha value matches upstream.
1508 void WebViewHost::displayRepaintMask()
1509 {
1510     canvas()->drawARGB(167, 0, 0, 0);
1511 }