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