OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "webkit/tools/test_shell/test_shell.h" | |
6 | |
7 #include <errno.h> | |
8 #include <fcntl.h> | |
9 #include <fontconfig/fontconfig.h> | |
10 #include <gtk/gtk.h> | |
11 #include <signal.h> | |
12 #include <unistd.h> | |
13 | |
14 #include "base/file_util.h" | |
15 #include "base/files/file_path.h" | |
16 #include "base/message_loop.h" | |
17 #include "base/path_service.h" | |
18 #include "base/string16.h" | |
19 #include "base/strings/string_piece.h" | |
20 #include "base/utf_string_conversions.h" | |
21 #include "grit/test_shell_resources.h" | |
22 #include "grit/webkit_resources.h" | |
23 #include "net/base/mime_util.h" | |
24 #include "net/base/net_util.h" | |
25 #include "third_party/WebKit/Source/Platform/chromium/public/WebPoint.h" | |
26 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | |
27 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" | |
28 #include "ui/base/gtk/gtk_compat.h" | |
29 #include "ui/base/layout.h" | |
30 #include "ui/base/resource/resource_bundle.h" | |
31 #include "webkit/glue/resource_loader_bridge.h" | |
32 #include "webkit/glue/webkit_glue.h" | |
33 #include "webkit/glue/webpreferences.h" | |
34 #include "webkit/plugins/npapi/plugin_list.h" | |
35 #include "webkit/tools/test_shell/test_navigation_controller.h" | |
36 #include "webkit/tools/test_shell/test_shell_webkit_init.h" | |
37 #include "webkit/tools/test_shell/test_webview_delegate.h" | |
38 | |
39 using WebKit::WebPoint; | |
40 using WebKit::WebWidget; | |
41 | |
42 namespace { | |
43 | |
44 // Convert a base::FilePath into an FcChar* (used by fontconfig). | |
45 // The pointer only lives for the duration for the expression. | |
46 const FcChar8* FilePathAsFcChar(const base::FilePath& path) { | |
47 return reinterpret_cast<const FcChar8*>(path.value().c_str()); | |
48 } | |
49 | |
50 void TerminationSignalHandler(int signatl) { | |
51 TestShell::ShutdownTestShell(); | |
52 exit(0); | |
53 } | |
54 | |
55 // GTK callbacks ------------------------------------------------------ | |
56 | |
57 // Callback for when the main window is destroyed. | |
58 gboolean MainWindowDestroyed(GtkWindow* window, TestShell* shell) { | |
59 TestShell::RemoveWindowFromList(window); | |
60 | |
61 if (TestShell::windowList()->empty() || shell->is_modal()) { | |
62 MessageLoop::current()->PostTask(FROM_HERE, | |
63 MessageLoop::QuitClosure()); | |
64 } | |
65 | |
66 delete shell; | |
67 | |
68 return FALSE; // Don't stop this message. | |
69 } | |
70 | |
71 gboolean MainWindowLostFocus(GtkWidget* widget, GdkEventFocus* event, | |
72 TestShell* shell) { | |
73 shell->ClosePopup(); | |
74 return FALSE; | |
75 } | |
76 | |
77 // Callback for when you click the back button. | |
78 void BackButtonClicked(GtkButton* button, TestShell* shell) { | |
79 shell->GoBackOrForward(-1); | |
80 } | |
81 | |
82 // Callback for when you click the forward button. | |
83 void ForwardButtonClicked(GtkButton* button, TestShell* shell) { | |
84 shell->GoBackOrForward(1); | |
85 } | |
86 | |
87 // Callback for when you click the stop button. | |
88 void StopButtonClicked(GtkButton* button, TestShell* shell) { | |
89 shell->webView()->mainFrame()->stopLoading(); | |
90 } | |
91 | |
92 // Callback for when you click the reload button. | |
93 void ReloadButtonClicked(GtkButton* button, TestShell* shell) { | |
94 shell->Reload(); | |
95 } | |
96 | |
97 // Callback for when you press enter in the URL box. | |
98 void URLEntryActivate(GtkEntry* entry, TestShell* shell) { | |
99 const gchar* url = gtk_entry_get_text(entry); | |
100 shell->LoadURL(GURL(url)); | |
101 } | |
102 | |
103 // Callback for Debug > Dump body text... menu item. | |
104 gboolean DumpBodyTextActivated(GtkWidget* widget, TestShell* shell) { | |
105 shell->DumpDocumentText(); | |
106 return FALSE; // Don't stop this message. | |
107 } | |
108 | |
109 // Callback for Debug > Dump render tree... menu item. | |
110 gboolean DumpRenderTreeActivated(GtkWidget* widget, TestShell* shell) { | |
111 shell->DumpRenderTree(); | |
112 return FALSE; // Don't stop this message. | |
113 } | |
114 | |
115 // Callback for Debug > Show web inspector... menu item. | |
116 gboolean ShowWebInspectorActivated(GtkWidget* widget, TestShell* shell) { | |
117 shell->webView()->inspectElementAt(WebPoint()); | |
118 return FALSE; // Don't stop this message. | |
119 } | |
120 | |
121 // GTK utility functions ---------------------------------------------- | |
122 | |
123 GtkWidget* AddMenuEntry(GtkWidget* menu_widget, const char* text, | |
124 GCallback callback, TestShell* shell) { | |
125 GtkWidget* entry = gtk_menu_item_new_with_label(text); | |
126 g_signal_connect(entry, "activate", callback, shell); | |
127 gtk_menu_shell_append(GTK_MENU_SHELL(menu_widget), entry); | |
128 return entry; | |
129 } | |
130 | |
131 GtkWidget* CreateMenu(GtkWidget* menu_bar, const char* text) { | |
132 GtkWidget* menu_widget = gtk_menu_new(); | |
133 GtkWidget* menu_header = gtk_menu_item_new_with_label(text); | |
134 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_header), menu_widget); | |
135 gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), menu_header); | |
136 return menu_widget; | |
137 } | |
138 | |
139 GtkWidget* CreateMenuBar(TestShell* shell) { | |
140 GtkWidget* menu_bar = gtk_menu_bar_new(); | |
141 GtkWidget* debug_menu = CreateMenu(menu_bar, "Debug"); | |
142 AddMenuEntry(debug_menu, "Dump body text...", | |
143 G_CALLBACK(DumpBodyTextActivated), shell); | |
144 AddMenuEntry(debug_menu, "Dump render tree...", | |
145 G_CALLBACK(DumpRenderTreeActivated), shell); | |
146 AddMenuEntry(debug_menu, "Show web inspector...", | |
147 G_CALLBACK(ShowWebInspectorActivated), shell); | |
148 return menu_bar; | |
149 } | |
150 | |
151 } // namespace | |
152 | |
153 // static | |
154 void TestShell::InitializeTestShell(bool layout_test_mode, | |
155 bool allow_external_pages) { | |
156 window_list_ = new WindowList; | |
157 layout_test_mode_ = layout_test_mode; | |
158 allow_external_pages_ = allow_external_pages; | |
159 | |
160 web_prefs_ = new WebPreferences; | |
161 | |
162 base::FilePath data_path; | |
163 PathService::Get(base::DIR_EXE, &data_path); | |
164 data_path = data_path.Append("test_shell.pak"); | |
165 ResourceBundle::InitSharedInstanceWithPakPath(data_path); | |
166 | |
167 base::FilePath resources_dir; | |
168 PathService::Get(base::DIR_SOURCE_ROOT, &resources_dir); | |
169 resources_dir = resources_dir.Append("webkit/tools/test_shell/resources"); | |
170 | |
171 ResetWebPreferences(); | |
172 | |
173 // We wish to make the layout tests reproducable with respect to fonts. Skia | |
174 // uses fontconfig to resolve font family names from WebKit into actual font | |
175 // files found on the current system. This means that fonts vary based on the | |
176 // system and also on the fontconfig configuration. | |
177 // | |
178 // To avoid this we initialise fontconfig here and install a configuration | |
179 // which only knows about a few, select, fonts. | |
180 | |
181 // We have fontconfig parse a config file from our resources file. This | |
182 // sets a number of aliases ("sans"->"Arial" etc), but doesn't include any | |
183 // font directories. | |
184 if (layout_test_mode) { | |
185 FcInit(); | |
186 | |
187 FcConfig* fontcfg = FcConfigCreate(); | |
188 base::FilePath fontconfig_path = resources_dir.Append("fonts.conf"); | |
189 if (!FcConfigParseAndLoad(fontcfg, FilePathAsFcChar(fontconfig_path), | |
190 true)) { | |
191 LOG(FATAL) << "Failed to parse fontconfig config file"; | |
192 } | |
193 | |
194 // This is the list of fonts that fontconfig will know about. It | |
195 // will try its best to match based only on the fonts here in. The | |
196 // paths are where these fonts are found on our Ubuntu boxes. | |
197 static const char *const fonts[] = { | |
198 "/usr/share/fonts/truetype/kochi/kochi-gothic.ttf", | |
199 "/usr/share/fonts/truetype/kochi/kochi-mincho.ttf", | |
200 "/usr/share/fonts/truetype/msttcorefonts/Arial.ttf", | |
201 "/usr/share/fonts/truetype/msttcorefonts/Arial_Bold.ttf", | |
202 "/usr/share/fonts/truetype/msttcorefonts/Arial_Bold_Italic.ttf", | |
203 "/usr/share/fonts/truetype/msttcorefonts/Arial_Italic.ttf", | |
204 "/usr/share/fonts/truetype/msttcorefonts/Comic_Sans_MS.ttf", | |
205 "/usr/share/fonts/truetype/msttcorefonts/Comic_Sans_MS_Bold.ttf", | |
206 "/usr/share/fonts/truetype/msttcorefonts/Courier_New.ttf", | |
207 "/usr/share/fonts/truetype/msttcorefonts/Courier_New_Bold.ttf", | |
208 "/usr/share/fonts/truetype/msttcorefonts/Courier_New_Bold_Italic.ttf", | |
209 "/usr/share/fonts/truetype/msttcorefonts/Courier_New_Italic.ttf", | |
210 "/usr/share/fonts/truetype/msttcorefonts/Georgia.ttf", | |
211 "/usr/share/fonts/truetype/msttcorefonts/Georgia_Bold.ttf", | |
212 "/usr/share/fonts/truetype/msttcorefonts/Georgia_Bold_Italic.ttf", | |
213 "/usr/share/fonts/truetype/msttcorefonts/Georgia_Italic.ttf", | |
214 "/usr/share/fonts/truetype/msttcorefonts/Impact.ttf", | |
215 "/usr/share/fonts/truetype/msttcorefonts/Trebuchet_MS.ttf", | |
216 "/usr/share/fonts/truetype/msttcorefonts/Trebuchet_MS_Bold.ttf", | |
217 "/usr/share/fonts/truetype/msttcorefonts/Trebuchet_MS_Bold_Italic.ttf", | |
218 "/usr/share/fonts/truetype/msttcorefonts/Trebuchet_MS_Italic.ttf", | |
219 "/usr/share/fonts/truetype/msttcorefonts/Times_New_Roman.ttf", | |
220 "/usr/share/fonts/truetype/msttcorefonts/Times_New_Roman_Bold.ttf", | |
221 "/usr/share/fonts/truetype/msttcorefonts/Times_New_Roman_Bold_Italic.ttf", | |
222 "/usr/share/fonts/truetype/msttcorefonts/Times_New_Roman_Italic.ttf", | |
223 "/usr/share/fonts/truetype/msttcorefonts/Verdana.ttf", | |
224 "/usr/share/fonts/truetype/msttcorefonts/Verdana_Bold.ttf", | |
225 "/usr/share/fonts/truetype/msttcorefonts/Verdana_Bold_Italic.ttf", | |
226 "/usr/share/fonts/truetype/msttcorefonts/Verdana_Italic.ttf", | |
227 // The DejaVuSans font is used by the css2.1 tests. | |
228 "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", | |
229 "/usr/share/fonts/truetype/ttf-indic-fonts-core/lohit_ta.ttf", | |
230 "/usr/share/fonts/truetype/ttf-indic-fonts-core/MuktiNarrow.ttf", | |
231 }; | |
232 for (size_t i = 0; i < arraysize(fonts); ++i) { | |
233 if (access(fonts[i], R_OK)) { | |
234 LOG(FATAL) << "You are missing " << fonts[i] << ". " | |
235 << "Try installing msttcorefonts. Also see " | |
236 << "http://code.google.com/p/chromium/wiki/" | |
237 << "LinuxBuildInstructions"; | |
238 } | |
239 if (!FcConfigAppFontAddFile(fontcfg, (FcChar8 *) fonts[i])) | |
240 LOG(FATAL) << "Failed to load font " << fonts[i]; | |
241 } | |
242 | |
243 // We special case these fonts because they're only needed in a | |
244 // few layout tests. | |
245 static const char* const optional_fonts[] = { | |
246 "/usr/share/fonts/truetype/ttf-lucida/LucidaSansRegular.ttf", | |
247 | |
248 // This font changed paths across Ubuntu releases. | |
249 "/usr/share/fonts/truetype/ttf-indic-fonts-core/lohit_pa.ttf", | |
250 "/usr/share/fonts/truetype/ttf-punjabi-fonts/lohit_pa.ttf", | |
251 }; | |
252 for (size_t i = 0; i < arraysize(optional_fonts); ++i) { | |
253 const char* font = optional_fonts[i]; | |
254 if (access(font, R_OK) < 0) { | |
255 LOG(WARNING) << "You are missing " << font << ". " | |
256 << "Without this, some layout tests will fail. " | |
257 << "See http://code.google.com/p/chromium/wiki/" | |
258 << "LinuxBuildInstructionsPrerequisites for more."; | |
259 } else { | |
260 if (!FcConfigAppFontAddFile(fontcfg, (FcChar8 *) font)) | |
261 LOG(FATAL) << "Failed to load font " << font; | |
262 } | |
263 } | |
264 | |
265 // Also load the layout-test-specific "Ahem" font. | |
266 base::FilePath ahem_path = resources_dir.Append("AHEM____.TTF"); | |
267 if (!FcConfigAppFontAddFile(fontcfg, FilePathAsFcChar(ahem_path))) { | |
268 LOG(FATAL) << "Failed to load font " << ahem_path.value().c_str(); | |
269 } | |
270 | |
271 if (!FcConfigSetCurrent(fontcfg)) | |
272 LOG(FATAL) << "Failed to set the default font configuration"; | |
273 } | |
274 | |
275 // Install an signal handler so we clean up after ourselves. | |
276 signal(SIGINT, TerminationSignalHandler); | |
277 signal(SIGTERM, TerminationSignalHandler); | |
278 } | |
279 | |
280 void TestShell::PlatformShutdown() { | |
281 ResourceBundle::CleanupSharedInstance(); | |
282 } | |
283 | |
284 void TestShell::PlatformCleanUp() { | |
285 if (m_mainWnd) { | |
286 // Disconnect our MainWindowDestroyed handler so that we don't go through | |
287 // the shutdown process more than once. | |
288 g_signal_handlers_disconnect_by_func(m_mainWnd, | |
289 reinterpret_cast<gpointer>(MainWindowDestroyed), this); | |
290 gtk_widget_destroy(GTK_WIDGET(m_mainWnd)); | |
291 } | |
292 } | |
293 | |
294 void TestShell::EnableUIControl(UIControl control, bool is_enabled) { | |
295 // TODO(darin): Implement me. | |
296 } | |
297 | |
298 bool TestShell::Initialize(const GURL& starting_url) { | |
299 m_mainWnd = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); | |
300 gtk_window_set_title(m_mainWnd, "Test Shell"); | |
301 // Null out m_mainWnd when it is destroyed so we don't destroy it twice. | |
302 g_signal_connect(G_OBJECT(m_mainWnd), "destroy", | |
303 G_CALLBACK(gtk_widget_destroyed), &m_mainWnd); | |
304 g_signal_connect(G_OBJECT(m_mainWnd), "destroy", | |
305 G_CALLBACK(MainWindowDestroyed), this); | |
306 g_signal_connect(G_OBJECT(m_mainWnd), "focus-out-event", | |
307 G_CALLBACK(MainWindowLostFocus), this); | |
308 g_object_set_data(G_OBJECT(m_mainWnd), "test-shell", this); | |
309 | |
310 GtkWidget* vbox = gtk_vbox_new(FALSE, 0); | |
311 | |
312 GtkWidget* menu_bar = CreateMenuBar(this); | |
313 | |
314 gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 0); | |
315 | |
316 GtkWidget* toolbar = gtk_toolbar_new(); | |
317 // Turn off the labels on the toolbar buttons. | |
318 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS); | |
319 | |
320 GtkToolItem* back = gtk_tool_button_new_from_stock(GTK_STOCK_GO_BACK); | |
321 g_signal_connect(back, "clicked", | |
322 G_CALLBACK(BackButtonClicked), this); | |
323 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), back, -1 /* append */); | |
324 | |
325 GtkToolItem* forward = gtk_tool_button_new_from_stock(GTK_STOCK_GO_FORWARD); | |
326 g_signal_connect(forward, "clicked", | |
327 G_CALLBACK(ForwardButtonClicked), this); | |
328 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), forward, -1 /* append */); | |
329 | |
330 GtkToolItem* reload = gtk_tool_button_new_from_stock(GTK_STOCK_REFRESH); | |
331 g_signal_connect(reload, "clicked", | |
332 G_CALLBACK(ReloadButtonClicked), this); | |
333 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), reload, -1 /* append */); | |
334 | |
335 GtkToolItem* stop = gtk_tool_button_new_from_stock(GTK_STOCK_STOP); | |
336 g_signal_connect(stop, "clicked", | |
337 G_CALLBACK(StopButtonClicked), this); | |
338 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), stop, -1 /* append */); | |
339 | |
340 m_editWnd = gtk_entry_new(); | |
341 g_signal_connect(G_OBJECT(m_editWnd), "activate", | |
342 G_CALLBACK(URLEntryActivate), this); | |
343 gtk_entry_set_text(GTK_ENTRY(m_editWnd), starting_url.spec().c_str()); | |
344 | |
345 GtkToolItem* tool_item = gtk_tool_item_new(); | |
346 gtk_container_add(GTK_CONTAINER(tool_item), m_editWnd); | |
347 gtk_tool_item_set_expand(tool_item, TRUE); | |
348 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tool_item, -1 /* append */); | |
349 | |
350 gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); | |
351 m_webViewHost.reset( | |
352 WebViewHost::Create(vbox, delegate_.get(), 0, *TestShell::web_prefs_)); | |
353 | |
354 gtk_container_add(GTK_CONTAINER(m_mainWnd), vbox); | |
355 gtk_widget_show_all(GTK_WIDGET(m_mainWnd)); | |
356 | |
357 // LoadURL will do a resize, so make sure we don't call LoadURL | |
358 // until we've completed all of our GTK setup. | |
359 if (starting_url.is_valid()) | |
360 LoadURL(starting_url); | |
361 | |
362 if (IsSVGTestURL(starting_url)) | |
363 SizeToSVG(); | |
364 else | |
365 SizeToDefault(); | |
366 | |
367 return true; | |
368 } | |
369 | |
370 void TestShell::SizeTo(int width, int height) { | |
371 GtkWidget* widget = m_webViewHost->view_handle(); | |
372 | |
373 GtkAllocation allocation; | |
374 gtk_widget_get_allocation(widget, &allocation); | |
375 if (allocation.width == width && allocation.height == height) { | |
376 // Nothing to do. | |
377 return; | |
378 } | |
379 | |
380 gtk_widget_set_size_request(widget, width, height); | |
381 if (allocation.width > width || allocation.height > height) { | |
382 // We've been sized smaller. Shrink the window so it snaps back to the | |
383 // appropriate size. | |
384 gtk_window_resize(GTK_WINDOW(m_mainWnd), 1, 1); | |
385 } | |
386 | |
387 // We've been asked to size the content area to a particular size. | |
388 // GTK works asynchronously: you request a size and then it | |
389 // eventually becomes that size. But layout tests need to be sure | |
390 // the resize has gone into WebKit by the time SizeTo() returns. | |
391 // Force the webkit resize to happen now. | |
392 m_webViewHost->Resize(gfx::Size(width, height)); | |
393 } | |
394 | |
395 void TestShell::InteractiveSetFocus(WebWidgetHost* host, bool enable) { | |
396 GtkWidget* widget = GTK_WIDGET(host->view_handle()); | |
397 | |
398 if (enable) { | |
399 gtk_widget_grab_focus(widget); | |
400 } else if (gtk_widget_is_focus(widget)) { | |
401 GtkWidget *toplevel = gtk_widget_get_toplevel(widget); | |
402 if (gtk_widget_is_toplevel(toplevel)) | |
403 gtk_window_set_focus(GTK_WINDOW(toplevel), NULL); | |
404 } | |
405 } | |
406 | |
407 void TestShell::DestroyWindow(gfx::NativeWindow windowHandle) { | |
408 RemoveWindowFromList(windowHandle); | |
409 gtk_widget_destroy(GTK_WIDGET(windowHandle)); | |
410 } | |
411 | |
412 WebWidget* TestShell::CreatePopupWidget() { | |
413 GtkWidget* popupwindow = gtk_window_new(GTK_WINDOW_POPUP); | |
414 GtkWidget* vbox = gtk_vbox_new(FALSE, 0); | |
415 WebWidgetHost* host = WebWidgetHost::Create(vbox, popup_delegate_.get()); | |
416 gtk_container_add(GTK_CONTAINER(popupwindow), vbox); | |
417 m_popupHost = host; | |
418 | |
419 // Grab all input to the test shell and funnel it to the popup. | |
420 // The popup will detect if mouseclicks are outside its bounds and destroy | |
421 // itself if so. Clicks that are outside the test_shell window will destroy | |
422 // the popup by taking focus away from the main window. | |
423 gtk_grab_add(host->view_handle()); | |
424 | |
425 return host->webwidget(); | |
426 } | |
427 | |
428 void TestShell::ClosePopup() { | |
429 if (!m_popupHost) | |
430 return; | |
431 GtkWidget* drawing_area = m_popupHost->view_handle(); | |
432 // gtk_widget_destroy will recursively call ClosePopup, so to avoid GTK | |
433 // warnings set m_popupHost to null here before making the call. | |
434 m_popupHost = NULL; | |
435 GtkWidget* window = | |
436 gtk_widget_get_parent(gtk_widget_get_parent(drawing_area)); | |
437 gtk_widget_destroy(window); | |
438 } | |
439 | |
440 void TestShell::ResizeSubViews() { | |
441 // This function is used on Windows to re-layout the window on a resize. | |
442 // GTK manages layout for us so we do nothing. | |
443 } | |
444 | |
445 /* static */ void TestShell::DumpAllBackForwardLists(base::string16* result) { | |
446 result->clear(); | |
447 for (WindowList::iterator iter = TestShell::windowList()->begin(); | |
448 iter != TestShell::windowList()->end(); iter++) { | |
449 GtkWindow* window = *iter; | |
450 TestShell* shell = | |
451 static_cast<TestShell*>(g_object_get_data(G_OBJECT(window), | |
452 "test-shell")); | |
453 shell->DumpBackForwardList(result); | |
454 } | |
455 } | |
456 | |
457 void TestShell::LoadURLForFrame(const GURL& url, | |
458 const base::string16& frame_name) { | |
459 if (!url.is_valid()) | |
460 return; | |
461 | |
462 if (IsSVGTestURL(url)) { | |
463 SizeToSVG(); | |
464 } else { | |
465 // only resize back to the default when running tests | |
466 if (layout_test_mode()) | |
467 SizeToDefault(); | |
468 } | |
469 | |
470 navigation_controller_->LoadEntry( | |
471 new TestNavigationEntry(-1, url, frame_name)); | |
472 } | |
473 | |
474 bool TestShell::PromptForSaveFile(const wchar_t* prompt_title, | |
475 base::FilePath* result) { | |
476 GtkWidget* dialog; | |
477 dialog = gtk_file_chooser_dialog_new(WideToUTF8(prompt_title).c_str(), | |
478 GTK_WINDOW(m_mainWnd), | |
479 GTK_FILE_CHOOSER_ACTION_SAVE, | |
480 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
481 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, | |
482 NULL); // Terminate (button, id) pairs. | |
483 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), | |
484 TRUE); | |
485 int dialog_result = gtk_dialog_run(GTK_DIALOG(dialog)); | |
486 if (dialog_result != GTK_RESPONSE_ACCEPT) { | |
487 gtk_widget_destroy(dialog); | |
488 return false; | |
489 } | |
490 char* path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); | |
491 gtk_widget_destroy(dialog); | |
492 *result = base::FilePath(path); | |
493 g_free(path); | |
494 return true; | |
495 } | |
496 | |
497 // static | |
498 std::string TestShell::RewriteLocalUrl(const std::string& url) { | |
499 // Convert file:///tmp/LayoutTests urls to the actual location on disk. | |
500 const char kPrefix[] = "file:///tmp/LayoutTests/"; | |
501 const int kPrefixLen = arraysize(kPrefix) - 1; | |
502 | |
503 std::string new_url(url); | |
504 if (url.compare(0, kPrefixLen, kPrefix, kPrefixLen) == 0) { | |
505 base::FilePath replace_path; | |
506 PathService::Get(base::DIR_SOURCE_ROOT, &replace_path); | |
507 replace_path = replace_path.Append( | |
508 "third_party/WebKit/LayoutTests/"); | |
509 new_url = std::string("file://") + replace_path.value() + | |
510 url.substr(kPrefixLen); | |
511 } | |
512 | |
513 return new_url; | |
514 } | |
515 | |
516 // static | |
517 void TestShell::ShowStartupDebuggingDialog() { | |
518 GtkWidget* dialog = gtk_message_dialog_new( | |
519 NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "attach to me?"); | |
520 gtk_window_set_title(GTK_WINDOW(dialog), "test_shell"); | |
521 gtk_dialog_run(GTK_DIALOG(dialog)); // Runs a nested message loop. | |
522 gtk_widget_destroy(dialog); | |
523 } | |
524 | |
525 // static | |
526 base::StringPiece TestShell::ResourceProvider(int key) { | |
527 return ResourceBundle::GetSharedInstance().GetRawDataResource(key); | |
528 } | |
529 | |
530 //----------------------------------------------------------------------------- | |
531 | |
532 base::string16 TestShellWebKitInit::GetLocalizedString(int message_id) { | |
533 return ResourceBundle::GetSharedInstance().GetLocalizedString(message_id); | |
534 } | |
535 | |
536 base::StringPiece TestShellWebKitInit::GetDataResource( | |
537 int resource_id, | |
538 ui::ScaleFactor scale_factor) { | |
539 switch (resource_id) { | |
540 case IDR_BROKENIMAGE: | |
541 resource_id = IDR_BROKENIMAGE_TESTSHELL; | |
542 break; | |
543 case IDR_TEXTAREA_RESIZER: | |
544 resource_id = IDR_TEXTAREA_RESIZER_TESTSHELL; | |
545 break; | |
546 } | |
547 // TODO(flackr): Pass scale_factor. | |
548 return TestShell::ResourceProvider(resource_id); | |
549 } | |
OLD | NEW |