| 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 |