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:
DevToolsWebContentsObserver(Shell * shell,WebContents * web_contents)43 DevToolsWebContentsObserver(Shell* shell, WebContents* web_contents)
44 : WebContentsObserver(web_contents),
45 shell_(shell) {
46 }
47
48 // WebContentsObserver
WebContentsDestroyed()49 virtual void WebContentsDestroyed() OVERRIDE {
50 shell_->OnDevToolsWebContentsDestroyed();
51 }
52
53 private:
54 Shell* shell_;
55
56 DISALLOW_COPY_AND_ASSIGN(DevToolsWebContentsObserver);
57 };
58
Shell(WebContents * web_contents)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
~Shell()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
CreateShell(WebContents * web_contents,const gfx::Size & initial_size)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
CloseAllWindows()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
SetShellCreatedCallback(base::Callback<void (Shell *)> shell_created_callback)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
FromRenderViewHost(RenderViewHost * rvh)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
Initialize()142 void Shell::Initialize() {
143 PlatformInitialize(
144 gfx::Size(kDefaultTestWindowWidthDip, kDefaultTestWindowHeightDip));
145 }
146
AdjustWindowSize(const gfx::Size & initial_size)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
CreateNewWindow(BrowserContext * browser_context,const GURL & url,SiteInstance * site_instance,int routing_id,const gfx::Size & initial_size)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
LoadURL(const GURL & url)168 void Shell::LoadURL(const GURL& url) {
169 LoadURLForFrame(url, std::string());
170 }
171
LoadURLForFrame(const GURL & url,const std::string & frame_name)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
LoadDataWithBaseURL(const GURL & url,const std::string & data,const GURL & base_url)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
AddNewContents(WebContents * source,WebContents * new_contents,WindowOpenDisposition disposition,const gfx::Rect & initial_pos,bool user_gesture,bool * was_blocked)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
GoBackOrForward(int offset)204 void Shell::GoBackOrForward(int offset) {
205 web_contents_->GetController().GoToOffset(offset);
206 web_contents_->Focus();
207 }
208
Reload()209 void Shell::Reload() {
210 web_contents_->GetController().Reload(false);
211 web_contents_->Focus();
212 }
213
Stop()214 void Shell::Stop() {
215 web_contents_->Stop();
216 web_contents_->Focus();
217 }
218
UpdateNavigationControls(bool to_different_document)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
ShowDevTools()229 void Shell::ShowDevTools() {
230 InnerShowDevTools("", "");
231 }
232
ShowDevToolsForElementAt(int x,int y)233 void Shell::ShowDevToolsForElementAt(int x, int y) {
234 InnerShowDevTools("", "");
235 devtools_frontend_->InspectElementAt(x, y);
236 }
237
ShowDevToolsForTest(const std::string & settings,const std::string & frontend_url)238 void Shell::ShowDevToolsForTest(const std::string& settings,
239 const std::string& frontend_url) {
240 InnerShowDevTools(settings, frontend_url);
241 }
242
CloseDevTools()243 void Shell::CloseDevTools() {
244 if (!devtools_frontend_)
245 return;
246 devtools_observer_.reset();
247 devtools_frontend_->Close();
248 devtools_frontend_ = NULL;
249 }
250
GetContentView()251 gfx::NativeView Shell::GetContentView() {
252 if (!web_contents_)
253 return NULL;
254 return web_contents_->GetNativeView();
255 }
256
OpenURLFromTab(WebContents * source,const OpenURLParams & params)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
LoadingStateChanged(WebContents * source,bool to_different_document)282 void Shell::LoadingStateChanged(WebContents* source,
283 bool to_different_document) {
284 UpdateNavigationControls(to_different_document);
285 PlatformSetIsLoading(source->IsLoading());
286 }
287
ToggleFullscreenModeForTab(WebContents * web_contents,bool enter_fullscreen)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
IsFullscreenForTabOrPending(const WebContents * web_contents) const301 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
RequestToLockMouse(WebContents * web_contents,bool user_gesture,bool last_unlocked_by_target)309 void Shell::RequestToLockMouse(WebContents* web_contents,
310 bool user_gesture,
311 bool last_unlocked_by_target) {
312 web_contents->GotResponseToLockMouseRequest(true);
313 }
314
CloseContents(WebContents * source)315 void Shell::CloseContents(WebContents* source) {
316 Close();
317 }
318
CanOverscrollContent() const319 bool Shell::CanOverscrollContent() const {
320 #if defined(USE_AURA)
321 return true;
322 #else
323 return false;
324 #endif
325 }
326
DidNavigateMainFramePostCommit(WebContents * web_contents)327 void Shell::DidNavigateMainFramePostCommit(WebContents* web_contents) {
328 PlatformSetAddressBarURL(web_contents->GetLastCommittedURL());
329 }
330
GetJavaScriptDialogManager()331 JavaScriptDialogManager* Shell::GetJavaScriptDialogManager() {
332 if (!dialog_manager_)
333 dialog_manager_.reset(new ShellJavaScriptDialogManager());
334 return dialog_manager_.get();
335 }
336
AddMessageToConsole(WebContents * source,int32 level,const base::string16 & message,int32 line_no,const base::string16 & source_id)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
RendererUnresponsive(WebContents * source)345 void Shell::RendererUnresponsive(WebContents* source) {
346 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree))
347 return;
348 WebKitTestController::Get()->RendererUnresponsive();
349 }
350
ActivateContents(WebContents * contents)351 void Shell::ActivateContents(WebContents* contents) {
352 contents->GetRenderViewHost()->Focus();
353 }
354
DeactivateContents(WebContents * contents)355 void Shell::DeactivateContents(WebContents* contents) {
356 contents->GetRenderViewHost()->Blur();
357 }
358
WorkerCrashed(WebContents * source)359 void Shell::WorkerCrashed(WebContents* source) {
360 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree))
361 return;
362 WebKitTestController::Get()->WorkerCrashed();
363 }
364
HandleContextMenu(const content::ContextMenuParams & params)365 bool Shell::HandleContextMenu(const content::ContextMenuParams& params) {
366 return PlatformHandleContextMenu(params);
367 }
368
WebContentsFocused(WebContents * contents)369 void Shell::WebContentsFocused(WebContents* contents) {
370 #if defined(TOOLKIT_VIEWS)
371 PlatformWebContentsFocused(contents);
372 #endif
373 }
374
TitleWasSet(NavigationEntry * entry,bool explicit_set)375 void Shell::TitleWasSet(NavigationEntry* entry, bool explicit_set) {
376 if (entry)
377 PlatformSetTitle(entry->GetTitle());
378 }
379
InnerShowDevTools(const std::string & settings,const std::string & frontend_url)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
OnDevToolsWebContentsDestroyed()393 void Shell::OnDevToolsWebContentsDestroyed() {
394 devtools_observer_.reset();
395 devtools_frontend_ = NULL;
396 }
397
398 } // namespace content
399