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