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