• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "TestShell.h"
33 
34 #include "DRTDevToolsAgent.h"
35 #include "DRTDevToolsClient.h"
36 #include "LayoutTestController.h"
37 #include "WebDataSource.h"
38 #include "WebDocument.h"
39 #include "WebElement.h"
40 #include "WebFrame.h"
41 #include "WebHistoryItem.h"
42 #include "WebKit.h"
43 #include "WebRuntimeFeatures.h"
44 #include "WebScriptController.h"
45 #include "WebSettings.h"
46 #include "WebSize.h"
47 #include "WebSpeechInputControllerMock.h"
48 #include "WebString.h"
49 #include "WebURLRequest.h"
50 #include "WebURLResponse.h"
51 #include "WebView.h"
52 #include "WebViewHost.h"
53 #include "skia/ext/platform_canvas.h"
54 #include "webkit/support/webkit_support.h"
55 #include "webkit/support/webkit_support_gfx.h"
56 #include <algorithm>
57 #include <cctype>
58 #include <vector>
59 #include <wtf/MD5.h>
60 
61 using namespace WebKit;
62 using namespace std;
63 
64 // Content area size for newly created windows.
65 static const int testWindowWidth = 800;
66 static const int testWindowHeight = 600;
67 
68 // The W3C SVG layout tests use a different size than the other layout tests.
69 static const int SVGTestWindowWidth = 480;
70 static const int SVGTestWindowHeight = 360;
71 
72 static const char layoutTestsPattern[] = "/LayoutTests/";
73 static const string::size_type layoutTestsPatternSize = sizeof(layoutTestsPattern) - 1;
74 static const char fileUrlPattern[] = "file:/";
75 static const char fileTestPrefix[] = "(file test):";
76 static const char dataUrlPattern[] = "data:";
77 static const string::size_type dataUrlPatternSize = sizeof(dataUrlPattern) - 1;
78 
79 // FIXME: Move this to a common place so that it can be shared with
80 // WebCore::TransparencyWin::makeLayerOpaque().
makeCanvasOpaque(SkCanvas * canvas)81 static void makeCanvasOpaque(SkCanvas* canvas)
82 {
83     const SkBitmap& bitmap = canvas->getTopDevice()->accessBitmap(true);
84     ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
85 
86     SkAutoLockPixels lock(bitmap);
87     for (int y = 0; y < bitmap.height(); y++) {
88         uint32_t* row = bitmap.getAddr32(0, y);
89         for (int x = 0; x < bitmap.width(); x++)
90             row[x] |= 0xFF000000; // Set alpha bits to 1.
91     }
92 }
93 
TestShell(bool testShellMode)94 TestShell::TestShell(bool testShellMode)
95     : m_testIsPending(false)
96     , m_testIsPreparing(false)
97     , m_focusedWidget(0)
98     , m_testShellMode(testShellMode)
99     , m_devTools(0)
100     , m_allowExternalPages(false)
101     , m_acceleratedCompositingEnabled(false)
102     , m_forceCompositingMode(false)
103     , m_accelerated2dCanvasEnabled(false)
104     , m_stressOpt(false)
105     , m_stressDeopt(false)
106     , m_dumpWhenFinished(true)
107 {
108     WebRuntimeFeatures::enableDataTransferItems(true);
109     WebRuntimeFeatures::enableGeolocation(true);
110     WebRuntimeFeatures::enableIndexedDatabase(true);
111     WebRuntimeFeatures::enableFileSystem(true);
112     WebRuntimeFeatures::enableJavaScriptI18NAPI(true);
113     m_accessibilityController.set(new AccessibilityController(this));
114     m_layoutTestController.set(new LayoutTestController(this));
115     m_eventSender.set(new EventSender(this));
116     m_plainTextController.set(new PlainTextController());
117     m_textInputController.set(new TextInputController(this));
118     m_notificationPresenter.set(new NotificationPresenter(this));
119     m_printer.set(m_testShellMode ? TestEventPrinter::createTestShellPrinter() : TestEventPrinter::createDRTPrinter());
120 
121     // 30 second is the same as the value in Mac DRT.
122     // If we use a value smaller than the timeout value of
123     // (new-)run-webkit-tests, (new-)run-webkit-tests misunderstands that a
124     // timed-out DRT process was crashed.
125     m_timeout = 30 * 1000;
126 
127     createMainWindow();
128 }
129 
createMainWindow()130 void TestShell::createMainWindow()
131 {
132     m_drtDevToolsAgent.set(new DRTDevToolsAgent);
133     m_webViewHost = createNewWindow(WebURL(), m_drtDevToolsAgent.get());
134     m_webView = m_webViewHost->webView();
135     m_drtDevToolsAgent->setWebView(m_webView);
136 }
137 
~TestShell()138 TestShell::~TestShell()
139 {
140     // Note: DevTools are closed together with all the other windows in the
141     // windows list.
142 
143     // Destroy the WebView before its WebViewHost.
144     m_drtDevToolsAgent->setWebView(0);
145 }
146 
createDRTDevToolsClient(DRTDevToolsAgent * agent)147 void TestShell::createDRTDevToolsClient(DRTDevToolsAgent* agent)
148 {
149     m_drtDevToolsClient.set(new DRTDevToolsClient(agent, m_devTools->webView()));
150 }
151 
showDevTools()152 void TestShell::showDevTools()
153 {
154     if (!m_devTools) {
155         WebURL url = webkit_support::GetDevToolsPathAsURL();
156         if (!url.isValid()) {
157             ASSERT(false);
158             return;
159         }
160         m_devTools = createNewWindow(url);
161         ASSERT(m_devTools);
162         createDRTDevToolsClient(m_drtDevToolsAgent.get());
163     }
164     m_devTools->show(WebKit::WebNavigationPolicyNewWindow);
165 }
166 
closeDevTools()167 void TestShell::closeDevTools()
168 {
169     if (m_devTools) {
170         m_drtDevToolsAgent->reset();
171         m_drtDevToolsClient.clear();
172         closeWindow(m_devTools);
173         m_devTools = 0;
174     }
175 }
176 
resetWebSettings(WebView & webView)177 void TestShell::resetWebSettings(WebView& webView)
178 {
179     m_prefs.reset();
180     m_prefs.acceleratedCompositingEnabled = m_acceleratedCompositingEnabled;
181     m_prefs.forceCompositingMode = m_forceCompositingMode;
182     m_prefs.accelerated2dCanvasEnabled = m_accelerated2dCanvasEnabled;
183     m_prefs.applyTo(&webView);
184 }
185 
runFileTest(const TestParams & params)186 void TestShell::runFileTest(const TestParams& params)
187 {
188     ASSERT(params.testUrl.isValid());
189     m_testIsPreparing = true;
190     m_params = params;
191     string testUrl = m_params.testUrl.spec();
192 
193     if (testUrl.find("loading/") != string::npos
194         || testUrl.find("loading\\") != string::npos)
195         m_layoutTestController->setShouldDumpFrameLoadCallbacks(true);
196 
197     if (testUrl.find("/dumpAsText/") != string::npos
198         || testUrl.find("\\dumpAsText\\") != string::npos) {
199         m_layoutTestController->setShouldDumpAsText(true);
200         m_layoutTestController->setShouldGeneratePixelResults(false);
201     }
202 
203     if (testUrl.find("/inspector/") != string::npos
204         || testUrl.find("\\inspector\\") != string::npos)
205         showDevTools();
206 
207     if (m_params.debugLayerTree)
208         m_layoutTestController->setShowDebugLayerTree(true);
209 
210     if (m_dumpWhenFinished)
211         m_printer->handleTestHeader(testUrl.c_str());
212     loadURL(m_params.testUrl);
213 
214     m_testIsPreparing = false;
215     waitTestFinished();
216 }
217 
isSVGTestURL(const WebURL & url)218 static inline bool isSVGTestURL(const WebURL& url)
219 {
220     return url.isValid() && string(url.spec()).find("W3C-SVG-1.1") != string::npos;
221 }
222 
resizeWindowForTest(WebViewHost * window,const WebURL & url)223 void TestShell::resizeWindowForTest(WebViewHost* window, const WebURL& url)
224 {
225     int width, height;
226     if (isSVGTestURL(url)) {
227         width = SVGTestWindowWidth;
228         height = SVGTestWindowHeight;
229     } else {
230         width = testWindowWidth;
231         height = testWindowHeight;
232     }
233     window->setWindowRect(WebRect(0, 0, width + virtualWindowBorder * 2, height + virtualWindowBorder * 2));
234 }
235 
resetTestController()236 void TestShell::resetTestController()
237 {
238     resetWebSettings(*webView());
239     m_accessibilityController->reset();
240     m_layoutTestController->reset();
241     m_eventSender->reset();
242     m_webViewHost->reset();
243     m_notificationPresenter->reset();
244     m_drtDevToolsAgent->reset();
245     if (m_drtDevToolsClient)
246         m_drtDevToolsClient->reset();
247     webView()->mainFrame()->clearOpener();
248 }
249 
loadURL(const WebURL & url)250 void TestShell::loadURL(const WebURL& url)
251 {
252     m_webViewHost->loadURLForFrame(url, WebString());
253 }
254 
reload()255 void TestShell::reload()
256 {
257     m_webViewHost->navigationController()->reload();
258 }
259 
goToOffset(int offset)260 void TestShell::goToOffset(int offset)
261 {
262      m_webViewHost->navigationController()->goToOffset(offset);
263 }
264 
navigationEntryCount() const265 int TestShell::navigationEntryCount() const
266 {
267     return m_webViewHost->navigationController()->entryCount();
268 }
269 
callJSGC()270 void TestShell::callJSGC()
271 {
272     m_webView->mainFrame()->collectGarbage();
273 }
274 
setFocus(WebWidget * widget,bool enable)275 void TestShell::setFocus(WebWidget* widget, bool enable)
276 {
277     // Simulate the effects of InteractiveSetFocus(), which includes calling
278     // both setFocus() and setIsActive().
279     if (enable) {
280         if (m_focusedWidget != widget) {
281             if (m_focusedWidget)
282                 m_focusedWidget->setFocus(false);
283             webView()->setIsActive(enable);
284             widget->setFocus(enable);
285             m_focusedWidget = widget;
286         }
287     } else {
288         if (m_focusedWidget == widget) {
289             widget->setFocus(enable);
290             webView()->setIsActive(enable);
291             m_focusedWidget = 0;
292         }
293     }
294 }
295 
testFinished()296 void TestShell::testFinished()
297 {
298     if (!m_testIsPending)
299         return;
300     m_testIsPending = false;
301     if (m_dumpWhenFinished)
302         dump();
303     webkit_support::QuitMessageLoop();
304 }
305 
testTimedOut()306 void TestShell::testTimedOut()
307 {
308     m_printer->handleTimedOut();
309     testFinished();
310 }
311 
dumpDocumentText(WebFrame * frame)312 static string dumpDocumentText(WebFrame* frame)
313 {
314     // We use the document element's text instead of the body text here because
315     // not all documents have a body, such as XML documents.
316     WebElement documentElement = frame->document().documentElement();
317     if (documentElement.isNull())
318         return string();
319     return documentElement.innerText().utf8();
320 }
321 
dumpFramesAsText(WebFrame * frame,bool recursive)322 static string dumpFramesAsText(WebFrame* frame, bool recursive)
323 {
324     string result;
325 
326     // Add header for all but the main frame. Skip empty frames.
327     if (frame->parent() && !frame->document().documentElement().isNull()) {
328         result.append("\n--------\nFrame: '");
329         result.append(frame->name().utf8().data());
330         result.append("'\n--------\n");
331     }
332 
333     result.append(dumpDocumentText(frame));
334     result.append("\n");
335 
336     if (recursive) {
337         for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling())
338             result.append(dumpFramesAsText(child, recursive));
339     }
340 
341     return result;
342 }
343 
dumpFrameScrollPosition(WebFrame * frame,bool recursive)344 static void dumpFrameScrollPosition(WebFrame* frame, bool recursive)
345 {
346     WebSize offset = frame->scrollOffset();
347     if (offset.width > 0 || offset.height > 0) {
348         if (frame->parent())
349             printf("frame '%s' ", frame->name().utf8().data());
350         printf("scrolled to %d,%d\n", offset.width, offset.height);
351     }
352 
353     if (!recursive)
354         return;
355     for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling())
356         dumpFrameScrollPosition(child, recursive);
357 }
358 
359 struct ToLower {
operator ()ToLower360     char16 operator()(char16 c) { return tolower(c); }
361 };
362 
363 // FIXME: Eliminate std::transform(), std::vector, and std::sort().
364 
365 // Returns True if item1 < item2.
HistoryItemCompareLess(const WebHistoryItem & item1,const WebHistoryItem & item2)366 static bool HistoryItemCompareLess(const WebHistoryItem& item1, const WebHistoryItem& item2)
367 {
368     string16 target1 = item1.target();
369     string16 target2 = item2.target();
370     std::transform(target1.begin(), target1.end(), target1.begin(), ToLower());
371     std::transform(target2.begin(), target2.end(), target2.begin(), ToLower());
372     return target1 < target2;
373 }
374 
dumpHistoryItem(const WebHistoryItem & item,int indent,bool isCurrent)375 static string dumpHistoryItem(const WebHistoryItem& item, int indent, bool isCurrent)
376 {
377     string result;
378 
379     if (isCurrent) {
380         result.append("curr->");
381         result.append(indent - 6, ' '); // 6 == "curr->".length()
382     } else {
383         result.append(indent, ' ');
384     }
385 
386     string url = item.urlString().utf8();
387     size_t pos;
388     if (!url.find(fileUrlPattern) && ((pos = url.find(layoutTestsPattern)) != string::npos)) {
389         // adjust file URLs to match upstream results.
390         url.replace(0, pos + layoutTestsPatternSize, fileTestPrefix);
391     } else if (!url.find(dataUrlPattern)) {
392         // URL-escape data URLs to match results upstream.
393         string path = webkit_support::EscapePath(url.substr(dataUrlPatternSize));
394         url.replace(dataUrlPatternSize, url.length(), path);
395     }
396 
397     result.append(url);
398     if (!item.target().isEmpty()) {
399         result.append(" (in frame \"");
400         result.append(item.target().utf8());
401         result.append("\")");
402     }
403     if (item.isTargetItem())
404         result.append("  **nav target**");
405     result.append("\n");
406 
407     const WebVector<WebHistoryItem>& children = item.children();
408     if (!children.isEmpty()) {
409         // Must sort to eliminate arbitrary result ordering which defeats
410         // reproducible testing.
411         // FIXME: WebVector should probably just be a std::vector!!
412         std::vector<WebHistoryItem> sortedChildren;
413         for (size_t i = 0; i < children.size(); ++i)
414             sortedChildren.push_back(children[i]);
415         std::sort(sortedChildren.begin(), sortedChildren.end(), HistoryItemCompareLess);
416         for (size_t i = 0; i < sortedChildren.size(); ++i)
417             result += dumpHistoryItem(sortedChildren[i], indent + 4, false);
418     }
419 
420     return result;
421 }
422 
dumpBackForwardList(const TestNavigationController & navigationController,string & result)423 static void dumpBackForwardList(const TestNavigationController& navigationController, string& result)
424 {
425     result.append("\n============== Back Forward List ==============\n");
426     for (int index = 0; index < navigationController.entryCount(); ++index) {
427         int currentIndex = navigationController.lastCommittedEntryIndex();
428         WebHistoryItem historyItem = navigationController.entryAtIndex(index)->contentState();
429         if (historyItem.isNull()) {
430             historyItem.initialize();
431             historyItem.setURLString(navigationController.entryAtIndex(index)->URL().spec().utf16());
432         }
433         result.append(dumpHistoryItem(historyItem, 8, index == currentIndex));
434     }
435     result.append("===============================================\n");
436 }
437 
dumpAllBackForwardLists()438 string TestShell::dumpAllBackForwardLists()
439 {
440     string result;
441     for (unsigned i = 0; i < m_windowList.size(); ++i)
442         dumpBackForwardList(*m_windowList[i]->navigationController(), result);
443     return result;
444 }
445 
dump()446 void TestShell::dump()
447 {
448     WebScriptController::flushConsoleMessages();
449 
450     // Dump the requested representation.
451     WebFrame* frame = m_webView->mainFrame();
452     if (!frame)
453         return;
454     bool shouldDumpAsText = m_layoutTestController->shouldDumpAsText();
455     bool shouldGeneratePixelResults = m_layoutTestController->shouldGeneratePixelResults();
456     bool dumpedAnything = false;
457     if (m_params.dumpTree) {
458         dumpedAnything = true;
459         m_printer->handleTextHeader();
460         // Text output: the test page can request different types of output
461         // which we handle here.
462         if (!shouldDumpAsText) {
463             // Plain text pages should be dumped as text
464             string mimeType = frame->dataSource()->response().mimeType().utf8();
465             if (mimeType == "text/plain") {
466                 shouldDumpAsText = true;
467                 shouldGeneratePixelResults = false;
468             }
469         }
470         if (shouldDumpAsText) {
471             bool recursive = m_layoutTestController->shouldDumpChildFramesAsText();
472             string dataUtf8 = dumpFramesAsText(frame, recursive);
473             if (fwrite(dataUtf8.c_str(), 1, dataUtf8.size(), stdout) != dataUtf8.size())
474                 FATAL("Short write to stdout, disk full?\n");
475         } else {
476             printf("%s", frame->renderTreeAsText(m_params.debugRenderTree).utf8().data());
477             bool recursive = m_layoutTestController->shouldDumpChildFrameScrollPositions();
478             dumpFrameScrollPosition(frame, recursive);
479         }
480         if (m_layoutTestController->shouldDumpBackForwardList())
481             printf("%s", dumpAllBackForwardLists().c_str());
482     }
483     if (dumpedAnything && m_params.printSeparators)
484         m_printer->handleTextFooter();
485 
486     if (m_params.dumpPixels && shouldGeneratePixelResults) {
487         // Image output: we write the image data to the file given on the
488         // command line (for the dump pixels argument), and the MD5 sum to
489         // stdout.
490         dumpedAnything = true;
491         m_webView->layout();
492         if (m_layoutTestController->testRepaint()) {
493             WebSize viewSize = m_webView->size();
494             int width = viewSize.width;
495             int height = viewSize.height;
496             if (m_layoutTestController->sweepHorizontally()) {
497                 for (WebRect column(0, 0, 1, height); column.x < width; column.x++)
498                     m_webViewHost->paintRect(column);
499             } else {
500                 for (WebRect line(0, 0, width, 1); line.y < height; line.y++)
501                     m_webViewHost->paintRect(line);
502             }
503         } else
504             m_webViewHost->paintInvalidatedRegion();
505 
506         // See if we need to draw the selection bounds rect. Selection bounds
507         // rect is the rect enclosing the (possibly transformed) selection.
508         // The rect should be drawn after everything is laid out and painted.
509         if (m_layoutTestController->shouldDumpSelectionRect()) {
510             // If there is a selection rect - draw a red 1px border enclosing rect
511             WebRect wr = frame->selectionBoundsRect();
512             if (!wr.isEmpty()) {
513                 // Render a red rectangle bounding selection rect
514                 SkPaint paint;
515                 paint.setColor(0xFFFF0000); // Fully opaque red
516                 paint.setStyle(SkPaint::kStroke_Style);
517                 paint.setFlags(SkPaint::kAntiAlias_Flag);
518                 paint.setStrokeWidth(1.0f);
519                 SkIRect rect; // Bounding rect
520                 rect.set(wr.x, wr.y, wr.x + wr.width, wr.y + wr.height);
521                 m_webViewHost->canvas()->drawIRect(rect, paint);
522             }
523         }
524 
525         dumpImage(m_webViewHost->canvas());
526     }
527     m_printer->handleImageFooter();
528     m_printer->handleTestFooter(dumpedAnything);
529     fflush(stdout);
530     fflush(stderr);
531 }
532 
dumpImage(SkCanvas * canvas) const533 void TestShell::dumpImage(SkCanvas* canvas) const
534 {
535     // Fix the alpha. The expected PNGs on Mac have an alpha channel, so we want
536     // to keep it. On Windows, the alpha channel is wrong since text/form control
537     // drawing may have erased it in a few places. So on Windows we force it to
538     // opaque and also don't write the alpha channel for the reference. Linux
539     // doesn't have the wrong alpha like Windows, but we match Windows.
540 #if OS(MAC_OS_X)
541     bool discardTransparency = false;
542 #else
543     bool discardTransparency = true;
544     makeCanvasOpaque(canvas);
545 #endif
546 
547     const SkBitmap& sourceBitmap = canvas->getTopDevice()->accessBitmap(false);
548     SkAutoLockPixels sourceBitmapLock(sourceBitmap);
549 
550     // Compute MD5 sum.
551     MD5 digester;
552     Vector<uint8_t, 16> digestValue;
553     digester.addBytes(reinterpret_cast<const uint8_t*>(sourceBitmap.getPixels()), sourceBitmap.getSize());
554     digester.checksum(digestValue);
555     string md5hash;
556     md5hash.reserve(16 * 2);
557     for (unsigned i = 0; i < 16; ++i) {
558         char hex[3];
559         // Use "x", not "X". The string must be lowercased.
560         sprintf(hex, "%02x", digestValue[i]);
561         md5hash.append(hex);
562     }
563 
564     // Only encode and dump the png if the hashes don't match. Encoding the
565     // image is really expensive.
566     if (md5hash.compare(m_params.pixelHash)) {
567         std::vector<unsigned char> png;
568         webkit_support::EncodeBGRAPNGWithChecksum(reinterpret_cast<const unsigned char*>(sourceBitmap.getPixels()), sourceBitmap.width(),
569             sourceBitmap.height(), static_cast<int>(sourceBitmap.rowBytes()), discardTransparency, md5hash, &png);
570 
571         m_printer->handleImage(md5hash.c_str(), m_params.pixelHash.c_str(), &png[0], png.size(), m_params.pixelFileName.c_str());
572     } else
573         m_printer->handleImage(md5hash.c_str(), m_params.pixelHash.c_str(), 0, 0, m_params.pixelFileName.c_str());
574 }
575 
bindJSObjectsToWindow(WebFrame * frame)576 void TestShell::bindJSObjectsToWindow(WebFrame* frame)
577 {
578     m_accessibilityController->bindToJavascript(frame, WebString::fromUTF8("accessibilityController"));
579     m_layoutTestController->bindToJavascript(frame, WebString::fromUTF8("layoutTestController"));
580     m_eventSender->bindToJavascript(frame, WebString::fromUTF8("eventSender"));
581     m_plainTextController->bindToJavascript(frame, WebString::fromUTF8("plainText"));
582     m_textInputController->bindToJavascript(frame, WebString::fromUTF8("textInputController"));
583 }
584 
createNewWindow(const WebKit::WebURL & url)585 WebViewHost* TestShell::createNewWindow(const WebKit::WebURL& url)
586 {
587     return createNewWindow(url, 0);
588 }
589 
createNewWindow(const WebKit::WebURL & url,DRTDevToolsAgent * devToolsAgent)590 WebViewHost* TestShell::createNewWindow(const WebKit::WebURL& url, DRTDevToolsAgent* devToolsAgent)
591 {
592     WebViewHost* host = new WebViewHost(this);
593     WebView* view = WebView::create(host);
594     view->setDevToolsAgentClient(devToolsAgent);
595     host->setWebWidget(view);
596     m_prefs.applyTo(view);
597     view->initializeMainFrame(host);
598     m_windowList.append(host);
599     host->loadURLForFrame(url, WebString());
600     return host;
601 }
602 
closeWindow(WebViewHost * window)603 void TestShell::closeWindow(WebViewHost* window)
604 {
605     size_t i = m_windowList.find(window);
606     if (i == notFound) {
607         ASSERT_NOT_REACHED();
608         return;
609     }
610     m_windowList.remove(i);
611     WebWidget* focusedWidget = m_focusedWidget;
612     if (window->webWidget() == m_focusedWidget)
613         focusedWidget = 0;
614 
615     delete window;
616     // We set the focused widget after deleting the web view host because it
617     // can change the focus.
618     m_focusedWidget = focusedWidget;
619     if (m_focusedWidget) {
620         webView()->setIsActive(true);
621         m_focusedWidget->setFocus(true);
622     }
623 }
624 
closeRemainingWindows()625 void TestShell::closeRemainingWindows()
626 {
627     // Just close devTools window manually because we have custom deinitialization code for it.
628     closeDevTools();
629 
630     // Iterate through the window list and close everything except the main
631     // window. We don't want to delete elements as we're iterating, so we copy
632     // to a temp vector first.
633     Vector<WebViewHost*> windowsToDelete;
634     for (unsigned i = 0; i < m_windowList.size(); ++i) {
635         if (m_windowList[i] != webViewHost())
636             windowsToDelete.append(m_windowList[i]);
637     }
638     ASSERT(windowsToDelete.size() + 1 == m_windowList.size());
639     for (unsigned i = 0; i < windowsToDelete.size(); ++i)
640         closeWindow(windowsToDelete[i]);
641     ASSERT(m_windowList.size() == 1);
642 }
643 
windowCount()644 int TestShell::windowCount()
645 {
646     return m_windowList.size();
647 }
648