1 // Copyright 2013 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 "content/shell/browser/shell.h" 6 7 #include "base/auto_reset.h" 8 #include "base/command_line.h" 9 #include "base/message_loop/message_loop.h" 10 #include "base/path_service.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/string_util.h" 13 #include "base/strings/stringprintf.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "content/public/browser/devtools_agent_host.h" 16 #include "content/public/browser/navigation_controller.h" 17 #include "content/public/browser/navigation_entry.h" 18 #include "content/public/browser/render_view_host.h" 19 #include "content/public/browser/web_contents.h" 20 #include "content/public/browser/web_contents_observer.h" 21 #include "content/public/common/renderer_preferences.h" 22 #include "content/shell/browser/notify_done_forwarder.h" 23 #include "content/shell/browser/shell_browser_main_parts.h" 24 #include "content/shell/browser/shell_content_browser_client.h" 25 #include "content/shell/browser/shell_devtools_frontend.h" 26 #include "content/shell/browser/shell_javascript_dialog_manager.h" 27 #include "content/shell/browser/webkit_test_controller.h" 28 #include "content/shell/common/shell_messages.h" 29 #include "content/shell/common/shell_switches.h" 30 31 namespace content { 32 33 const int Shell::kDefaultTestWindowWidthDip = 800; 34 const int Shell::kDefaultTestWindowHeightDip = 600; 35 36 std::vector<Shell*> Shell::windows_; 37 base::Callback<void(Shell*)> Shell::shell_created_callback_; 38 39 bool Shell::quit_message_loop_ = true; 40 41 class Shell::DevToolsWebContentsObserver : public WebContentsObserver { 42 public: 43 DevToolsWebContentsObserver(Shell* shell, WebContents* web_contents) 44 : WebContentsObserver(web_contents), 45 shell_(shell) { 46 } 47 48 // WebContentsObserver 49 virtual void WebContentsDestroyed() OVERRIDE { 50 shell_->OnDevToolsWebContentsDestroyed(); 51 } 52 53 private: 54 Shell* shell_; 55 56 DISALLOW_COPY_AND_ASSIGN(DevToolsWebContentsObserver); 57 }; 58 59 Shell::Shell(WebContents* web_contents) 60 : WebContentsObserver(web_contents), 61 devtools_frontend_(NULL), 62 is_fullscreen_(false), 63 window_(NULL), 64 url_edit_view_(NULL), 65 headless_(false) { 66 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 67 if (command_line.HasSwitch(switches::kDumpRenderTree)) 68 headless_ = true; 69 windows_.push_back(this); 70 71 if (!shell_created_callback_.is_null()) { 72 shell_created_callback_.Run(this); 73 shell_created_callback_.Reset(); 74 } 75 } 76 77 Shell::~Shell() { 78 PlatformCleanUp(); 79 80 for (size_t i = 0; i < windows_.size(); ++i) { 81 if (windows_[i] == this) { 82 windows_.erase(windows_.begin() + i); 83 break; 84 } 85 } 86 87 if (windows_.empty() && quit_message_loop_) { 88 if (headless_) 89 PlatformExit(); 90 base::MessageLoop::current()->PostTask(FROM_HERE, 91 base::MessageLoop::QuitClosure()); 92 } 93 } 94 95 Shell* Shell::CreateShell(WebContents* web_contents, 96 const gfx::Size& initial_size) { 97 Shell* shell = new Shell(web_contents); 98 shell->PlatformCreateWindow(initial_size.width(), initial_size.height()); 99 100 shell->web_contents_.reset(web_contents); 101 web_contents->SetDelegate(shell); 102 103 shell->PlatformSetContents(); 104 105 shell->PlatformResizeSubViews(); 106 107 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) { 108 web_contents->GetMutableRendererPrefs()->use_custom_colors = false; 109 web_contents->GetRenderViewHost()->SyncRendererPrefs(); 110 } 111 112 return shell; 113 } 114 115 void Shell::CloseAllWindows() { 116 base::AutoReset<bool> auto_reset(&quit_message_loop_, false); 117 DevToolsAgentHost::DetachAllClients(); 118 std::vector<Shell*> open_windows(windows_); 119 for (size_t i = 0; i < open_windows.size(); ++i) 120 open_windows[i]->Close(); 121 PlatformExit(); 122 base::MessageLoop::current()->RunUntilIdle(); 123 } 124 125 void Shell::SetShellCreatedCallback( 126 base::Callback<void(Shell*)> shell_created_callback) { 127 DCHECK(shell_created_callback_.is_null()); 128 shell_created_callback_ = shell_created_callback; 129 } 130 131 Shell* Shell::FromRenderViewHost(RenderViewHost* rvh) { 132 for (size_t i = 0; i < windows_.size(); ++i) { 133 if (windows_[i]->web_contents() && 134 windows_[i]->web_contents()->GetRenderViewHost() == rvh) { 135 return windows_[i]; 136 } 137 } 138 return NULL; 139 } 140 141 // static 142 void Shell::Initialize() { 143 PlatformInitialize( 144 gfx::Size(kDefaultTestWindowWidthDip, kDefaultTestWindowHeightDip)); 145 } 146 147 gfx::Size Shell::AdjustWindowSize(const gfx::Size& initial_size) { 148 if (!initial_size.IsEmpty()) 149 return initial_size; 150 return gfx::Size(kDefaultTestWindowWidthDip, kDefaultTestWindowHeightDip); 151 } 152 153 Shell* Shell::CreateNewWindow(BrowserContext* browser_context, 154 const GURL& url, 155 SiteInstance* site_instance, 156 int routing_id, 157 const gfx::Size& initial_size) { 158 WebContents::CreateParams create_params(browser_context, site_instance); 159 create_params.routing_id = routing_id; 160 create_params.initial_size = AdjustWindowSize(initial_size); 161 WebContents* web_contents = WebContents::Create(create_params); 162 Shell* shell = CreateShell(web_contents, create_params.initial_size); 163 if (!url.is_empty()) 164 shell->LoadURL(url); 165 return shell; 166 } 167 168 void Shell::LoadURL(const GURL& url) { 169 LoadURLForFrame(url, std::string()); 170 } 171 172 void Shell::LoadURLForFrame(const GURL& url, const std::string& frame_name) { 173 NavigationController::LoadURLParams params(url); 174 params.transition_type = ui::PageTransitionFromInt( 175 ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR); 176 params.frame_name = frame_name; 177 web_contents_->GetController().LoadURLWithParams(params); 178 web_contents_->Focus(); 179 } 180 181 void Shell::LoadDataWithBaseURL(const GURL& url, const std::string& data, 182 const GURL& base_url) { 183 const GURL data_url = GURL("data:text/html;charset=utf-8," + data); 184 NavigationController::LoadURLParams params(data_url); 185 params.load_type = NavigationController::LOAD_TYPE_DATA; 186 params.base_url_for_data_url = base_url; 187 params.virtual_url_for_data_url = url; 188 params.override_user_agent = NavigationController::UA_OVERRIDE_FALSE; 189 web_contents_->GetController().LoadURLWithParams(params); 190 web_contents_->Focus(); 191 } 192 193 void Shell::AddNewContents(WebContents* source, 194 WebContents* new_contents, 195 WindowOpenDisposition disposition, 196 const gfx::Rect& initial_pos, 197 bool user_gesture, 198 bool* was_blocked) { 199 CreateShell(new_contents, AdjustWindowSize(initial_pos.size())); 200 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) 201 NotifyDoneForwarder::CreateForWebContents(new_contents); 202 } 203 204 void Shell::GoBackOrForward(int offset) { 205 web_contents_->GetController().GoToOffset(offset); 206 web_contents_->Focus(); 207 } 208 209 void Shell::Reload() { 210 web_contents_->GetController().Reload(false); 211 web_contents_->Focus(); 212 } 213 214 void Shell::Stop() { 215 web_contents_->Stop(); 216 web_contents_->Focus(); 217 } 218 219 void Shell::UpdateNavigationControls(bool to_different_document) { 220 int current_index = web_contents_->GetController().GetCurrentEntryIndex(); 221 int max_index = web_contents_->GetController().GetEntryCount() - 1; 222 223 PlatformEnableUIControl(BACK_BUTTON, current_index > 0); 224 PlatformEnableUIControl(FORWARD_BUTTON, current_index < max_index); 225 PlatformEnableUIControl(STOP_BUTTON, 226 to_different_document && web_contents_->IsLoading()); 227 } 228 229 void Shell::ShowDevTools() { 230 InnerShowDevTools("", ""); 231 } 232 233 void Shell::ShowDevToolsForElementAt(int x, int y) { 234 InnerShowDevTools("", ""); 235 devtools_frontend_->InspectElementAt(x, y); 236 } 237 238 void Shell::ShowDevToolsForTest(const std::string& settings, 239 const std::string& frontend_url) { 240 InnerShowDevTools(settings, frontend_url); 241 } 242 243 void Shell::CloseDevTools() { 244 if (!devtools_frontend_) 245 return; 246 devtools_observer_.reset(); 247 devtools_frontend_->Close(); 248 devtools_frontend_ = NULL; 249 } 250 251 gfx::NativeView Shell::GetContentView() { 252 if (!web_contents_) 253 return NULL; 254 return web_contents_->GetNativeView(); 255 } 256 257 WebContents* Shell::OpenURLFromTab(WebContents* source, 258 const OpenURLParams& params) { 259 // CURRENT_TAB is the only one we implement for now. 260 if (params.disposition != CURRENT_TAB) 261 return NULL; 262 NavigationController::LoadURLParams load_url_params(params.url); 263 load_url_params.referrer = params.referrer; 264 load_url_params.frame_tree_node_id = params.frame_tree_node_id; 265 load_url_params.transition_type = params.transition; 266 load_url_params.extra_headers = params.extra_headers; 267 load_url_params.should_replace_current_entry = 268 params.should_replace_current_entry; 269 270 if (params.transferred_global_request_id != GlobalRequestID()) { 271 load_url_params.is_renderer_initiated = params.is_renderer_initiated; 272 load_url_params.transferred_global_request_id = 273 params.transferred_global_request_id; 274 } else if (params.is_renderer_initiated) { 275 load_url_params.is_renderer_initiated = true; 276 } 277 278 source->GetController().LoadURLWithParams(load_url_params); 279 return source; 280 } 281 282 void Shell::LoadingStateChanged(WebContents* source, 283 bool to_different_document) { 284 UpdateNavigationControls(to_different_document); 285 PlatformSetIsLoading(source->IsLoading()); 286 } 287 288 void Shell::ToggleFullscreenModeForTab(WebContents* web_contents, 289 bool enter_fullscreen) { 290 #if defined(OS_ANDROID) 291 PlatformToggleFullscreenModeForTab(web_contents, enter_fullscreen); 292 #endif 293 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) 294 return; 295 if (is_fullscreen_ != enter_fullscreen) { 296 is_fullscreen_ = enter_fullscreen; 297 web_contents->GetRenderViewHost()->WasResized(); 298 } 299 } 300 301 bool Shell::IsFullscreenForTabOrPending(const WebContents* web_contents) const { 302 #if defined(OS_ANDROID) 303 return PlatformIsFullscreenForTabOrPending(web_contents); 304 #else 305 return is_fullscreen_; 306 #endif 307 } 308 309 void Shell::RequestToLockMouse(WebContents* web_contents, 310 bool user_gesture, 311 bool last_unlocked_by_target) { 312 web_contents->GotResponseToLockMouseRequest(true); 313 } 314 315 void Shell::CloseContents(WebContents* source) { 316 Close(); 317 } 318 319 bool Shell::CanOverscrollContent() const { 320 #if defined(USE_AURA) 321 return true; 322 #else 323 return false; 324 #endif 325 } 326 327 void Shell::DidNavigateMainFramePostCommit(WebContents* web_contents) { 328 PlatformSetAddressBarURL(web_contents->GetLastCommittedURL()); 329 } 330 331 JavaScriptDialogManager* Shell::GetJavaScriptDialogManager() { 332 if (!dialog_manager_) 333 dialog_manager_.reset(new ShellJavaScriptDialogManager()); 334 return dialog_manager_.get(); 335 } 336 337 bool Shell::AddMessageToConsole(WebContents* source, 338 int32 level, 339 const base::string16& message, 340 int32 line_no, 341 const base::string16& source_id) { 342 return CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree); 343 } 344 345 void Shell::RendererUnresponsive(WebContents* source) { 346 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) 347 return; 348 WebKitTestController::Get()->RendererUnresponsive(); 349 } 350 351 void Shell::ActivateContents(WebContents* contents) { 352 contents->GetRenderViewHost()->Focus(); 353 } 354 355 void Shell::DeactivateContents(WebContents* contents) { 356 contents->GetRenderViewHost()->Blur(); 357 } 358 359 void Shell::WorkerCrashed(WebContents* source) { 360 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree)) 361 return; 362 WebKitTestController::Get()->WorkerCrashed(); 363 } 364 365 bool Shell::HandleContextMenu(const content::ContextMenuParams& params) { 366 return PlatformHandleContextMenu(params); 367 } 368 369 void Shell::WebContentsFocused(WebContents* contents) { 370 #if defined(TOOLKIT_VIEWS) 371 PlatformWebContentsFocused(contents); 372 #endif 373 } 374 375 void Shell::TitleWasSet(NavigationEntry* entry, bool explicit_set) { 376 if (entry) 377 PlatformSetTitle(entry->GetTitle()); 378 } 379 380 void Shell::InnerShowDevTools(const std::string& settings, 381 const std::string& frontend_url) { 382 if (!devtools_frontend_) { 383 devtools_frontend_ = ShellDevToolsFrontend::Show( 384 web_contents(), settings, frontend_url); 385 devtools_observer_.reset(new DevToolsWebContentsObserver( 386 this, devtools_frontend_->frontend_shell()->web_contents())); 387 } 388 389 devtools_frontend_->Activate(); 390 devtools_frontend_->Focus(); 391 } 392 393 void Shell::OnDevToolsWebContentsDestroyed() { 394 devtools_observer_.reset(); 395 devtools_frontend_ = NULL; 396 } 397 398 } // namespace content 399