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_manager.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/browser/web_contents_view.h"
22 #include "content/public/common/renderer_preferences.h"
23 #include "content/shell/browser/notify_done_forwarder.h"
24 #include "content/shell/browser/shell_browser_main_parts.h"
25 #include "content/shell/browser/shell_content_browser_client.h"
26 #include "content/shell/browser/shell_devtools_frontend.h"
27 #include "content/shell/browser/shell_javascript_dialog_manager.h"
28 #include "content/shell/browser/webkit_test_controller.h"
29 #include "content/shell/common/shell_messages.h"
30 #include "content/shell/common/shell_switches.h"
31
32 #if defined(USE_AURA) && !defined(TOOLKIT_VIEWS)
33 #include "content/shell/browser/shell_aura.h"
34 #endif
35
36 namespace content {
37
38 const int Shell::kDefaultTestWindowWidthDip = 800;
39 const int Shell::kDefaultTestWindowHeightDip = 600;
40
41 std::vector<Shell*> Shell::windows_;
42 base::Callback<void(Shell*)> Shell::shell_created_callback_;
43
44 bool Shell::quit_message_loop_ = true;
45
46 class Shell::DevToolsWebContentsObserver : public WebContentsObserver {
47 public:
DevToolsWebContentsObserver(Shell * shell,WebContents * web_contents)48 DevToolsWebContentsObserver(Shell* shell, WebContents* web_contents)
49 : WebContentsObserver(web_contents),
50 shell_(shell) {
51 }
52
53 // WebContentsObserver
WebContentsDestroyed(WebContents * web_contents)54 virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE {
55 shell_->OnDevToolsWebContentsDestroyed();
56 }
57
58 private:
59 Shell* shell_;
60
61 DISALLOW_COPY_AND_ASSIGN(DevToolsWebContentsObserver);
62 };
63
Shell(WebContents * web_contents)64 Shell::Shell(WebContents* web_contents)
65 : WebContentsObserver(web_contents),
66 devtools_frontend_(NULL),
67 is_fullscreen_(false),
68 window_(NULL),
69 url_edit_view_(NULL),
70 #if defined(OS_WIN) && !defined(USE_AURA)
71 default_edit_wnd_proc_(0),
72 #endif
73 headless_(false) {
74 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
75 if (command_line.HasSwitch(switches::kDumpRenderTree))
76 headless_ = true;
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
~Shell()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
CreateShell(WebContents * web_contents,const gfx::Size & initial_size)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
CloseAllWindows()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 PlatformExit();
127 base::MessageLoop::current()->RunUntilIdle();
128 }
129
SetShellCreatedCallback(base::Callback<void (Shell *)> shell_created_callback)130 void Shell::SetShellCreatedCallback(
131 base::Callback<void(Shell*)> shell_created_callback) {
132 DCHECK(shell_created_callback_.is_null());
133 shell_created_callback_ = shell_created_callback;
134 }
135
FromRenderViewHost(RenderViewHost * rvh)136 Shell* Shell::FromRenderViewHost(RenderViewHost* rvh) {
137 for (size_t i = 0; i < windows_.size(); ++i) {
138 if (windows_[i]->web_contents() &&
139 windows_[i]->web_contents()->GetRenderViewHost() == rvh) {
140 return windows_[i];
141 }
142 }
143 return NULL;
144 }
145
146 // static
Initialize()147 void Shell::Initialize() {
148 PlatformInitialize(
149 gfx::Size(kDefaultTestWindowWidthDip, kDefaultTestWindowHeightDip));
150 }
151
AdjustWindowSize(const gfx::Size & initial_size)152 gfx::Size Shell::AdjustWindowSize(const gfx::Size& initial_size) {
153 if (!initial_size.IsEmpty())
154 return initial_size;
155 return gfx::Size(kDefaultTestWindowWidthDip, kDefaultTestWindowHeightDip);
156 }
157
CreateNewWindow(BrowserContext * browser_context,const GURL & url,SiteInstance * site_instance,int routing_id,const gfx::Size & initial_size)158 Shell* Shell::CreateNewWindow(BrowserContext* browser_context,
159 const GURL& url,
160 SiteInstance* site_instance,
161 int routing_id,
162 const gfx::Size& initial_size) {
163 WebContents::CreateParams create_params(browser_context, site_instance);
164 create_params.routing_id = routing_id;
165 create_params.initial_size = AdjustWindowSize(initial_size);
166 WebContents* web_contents = WebContents::Create(create_params);
167 Shell* shell = CreateShell(web_contents, create_params.initial_size);
168 if (!url.is_empty())
169 shell->LoadURL(url);
170 return shell;
171 }
172
LoadURL(const GURL & url)173 void Shell::LoadURL(const GURL& url) {
174 LoadURLForFrame(url, std::string());
175 }
176
LoadURLForFrame(const GURL & url,const std::string & frame_name)177 void Shell::LoadURLForFrame(const GURL& url, const std::string& frame_name) {
178 NavigationController::LoadURLParams params(url);
179 params.transition_type = PageTransitionFromInt(
180 PAGE_TRANSITION_TYPED | PAGE_TRANSITION_FROM_ADDRESS_BAR);
181 params.frame_name = frame_name;
182 web_contents_->GetController().LoadURLWithParams(params);
183 web_contents_->GetView()->Focus();
184 }
185
AddNewContents(WebContents * source,WebContents * new_contents,WindowOpenDisposition disposition,const gfx::Rect & initial_pos,bool user_gesture,bool * was_blocked)186 void Shell::AddNewContents(WebContents* source,
187 WebContents* new_contents,
188 WindowOpenDisposition disposition,
189 const gfx::Rect& initial_pos,
190 bool user_gesture,
191 bool* was_blocked) {
192 CreateShell(new_contents, AdjustWindowSize(initial_pos.size()));
193 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree))
194 NotifyDoneForwarder::CreateForWebContents(new_contents);
195 }
196
GoBackOrForward(int offset)197 void Shell::GoBackOrForward(int offset) {
198 web_contents_->GetController().GoToOffset(offset);
199 web_contents_->GetView()->Focus();
200 }
201
Reload()202 void Shell::Reload() {
203 web_contents_->GetController().Reload(false);
204 web_contents_->GetView()->Focus();
205 }
206
Stop()207 void Shell::Stop() {
208 web_contents_->Stop();
209 web_contents_->GetView()->Focus();
210 }
211
UpdateNavigationControls()212 void Shell::UpdateNavigationControls() {
213 int current_index = web_contents_->GetController().GetCurrentEntryIndex();
214 int max_index = web_contents_->GetController().GetEntryCount() - 1;
215
216 PlatformEnableUIControl(BACK_BUTTON, current_index > 0);
217 PlatformEnableUIControl(FORWARD_BUTTON, current_index < max_index);
218 PlatformEnableUIControl(STOP_BUTTON, web_contents_->IsLoading());
219 }
220
ShowDevTools()221 void Shell::ShowDevTools() {
222 if (devtools_frontend_) {
223 devtools_frontend_->Focus();
224 return;
225 }
226 devtools_frontend_ = ShellDevToolsFrontend::Show(web_contents());
227 devtools_observer_.reset(new DevToolsWebContentsObserver(
228 this, devtools_frontend_->frontend_shell()->web_contents()));
229 }
230
CloseDevTools()231 void Shell::CloseDevTools() {
232 if (!devtools_frontend_)
233 return;
234 devtools_observer_.reset();
235 devtools_frontend_->Close();
236 devtools_frontend_ = NULL;
237 }
238
GetContentView()239 gfx::NativeView Shell::GetContentView() {
240 if (!web_contents_)
241 return NULL;
242 return web_contents_->GetView()->GetNativeView();
243 }
244
OpenURLFromTab(WebContents * source,const OpenURLParams & params)245 WebContents* Shell::OpenURLFromTab(WebContents* source,
246 const OpenURLParams& params) {
247 // CURRENT_TAB is the only one we implement for now.
248 if (params.disposition != CURRENT_TAB)
249 return NULL;
250 NavigationController::LoadURLParams load_url_params(params.url);
251 load_url_params.referrer = params.referrer;
252 load_url_params.frame_tree_node_id = params.frame_tree_node_id;
253 load_url_params.transition_type = params.transition;
254 load_url_params.extra_headers = params.extra_headers;
255 load_url_params.should_replace_current_entry =
256 params.should_replace_current_entry;
257
258 if (params.transferred_global_request_id != GlobalRequestID()) {
259 load_url_params.is_renderer_initiated = params.is_renderer_initiated;
260 load_url_params.transferred_global_request_id =
261 params.transferred_global_request_id;
262 } else if (params.is_renderer_initiated) {
263 load_url_params.is_renderer_initiated = true;
264 }
265
266 source->GetController().LoadURLWithParams(load_url_params);
267 return source;
268 }
269
LoadingStateChanged(WebContents * source)270 void Shell::LoadingStateChanged(WebContents* source) {
271 UpdateNavigationControls();
272 PlatformSetIsLoading(source->IsLoading());
273 }
274
ToggleFullscreenModeForTab(WebContents * web_contents,bool enter_fullscreen)275 void Shell::ToggleFullscreenModeForTab(WebContents* web_contents,
276 bool enter_fullscreen) {
277 #if defined(OS_ANDROID)
278 PlatformToggleFullscreenModeForTab(web_contents, enter_fullscreen);
279 #endif
280 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree))
281 return;
282 if (is_fullscreen_ != enter_fullscreen) {
283 is_fullscreen_ = enter_fullscreen;
284 web_contents->GetRenderViewHost()->WasResized();
285 }
286 }
287
IsFullscreenForTabOrPending(const WebContents * web_contents) const288 bool Shell::IsFullscreenForTabOrPending(const WebContents* web_contents) const {
289 #if defined(OS_ANDROID)
290 return PlatformIsFullscreenForTabOrPending(web_contents);
291 #else
292 return is_fullscreen_;
293 #endif
294 }
295
RequestToLockMouse(WebContents * web_contents,bool user_gesture,bool last_unlocked_by_target)296 void Shell::RequestToLockMouse(WebContents* web_contents,
297 bool user_gesture,
298 bool last_unlocked_by_target) {
299 web_contents->GotResponseToLockMouseRequest(true);
300 }
301
CloseContents(WebContents * source)302 void Shell::CloseContents(WebContents* source) {
303 Close();
304 }
305
CanOverscrollContent() const306 bool Shell::CanOverscrollContent() const {
307 #if defined(USE_AURA)
308 return true;
309 #else
310 return false;
311 #endif
312 }
313
DidNavigateMainFramePostCommit(WebContents * web_contents)314 void Shell::DidNavigateMainFramePostCommit(WebContents* web_contents) {
315 PlatformSetAddressBarURL(web_contents->GetLastCommittedURL());
316 }
317
GetJavaScriptDialogManager()318 JavaScriptDialogManager* Shell::GetJavaScriptDialogManager() {
319 if (!dialog_manager_)
320 dialog_manager_.reset(new ShellJavaScriptDialogManager());
321 return dialog_manager_.get();
322 }
323
AddMessageToConsole(WebContents * source,int32 level,const base::string16 & message,int32 line_no,const base::string16 & source_id)324 bool Shell::AddMessageToConsole(WebContents* source,
325 int32 level,
326 const base::string16& message,
327 int32 line_no,
328 const base::string16& source_id) {
329 return CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree);
330 }
331
RendererUnresponsive(WebContents * source)332 void Shell::RendererUnresponsive(WebContents* source) {
333 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree))
334 return;
335 WebKitTestController::Get()->RendererUnresponsive();
336 }
337
ActivateContents(WebContents * contents)338 void Shell::ActivateContents(WebContents* contents) {
339 contents->GetRenderViewHost()->Focus();
340 }
341
DeactivateContents(WebContents * contents)342 void Shell::DeactivateContents(WebContents* contents) {
343 contents->GetRenderViewHost()->Blur();
344 }
345
WorkerCrashed(WebContents * source)346 void Shell::WorkerCrashed(WebContents* source) {
347 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree))
348 return;
349 WebKitTestController::Get()->WorkerCrashed();
350 }
351
TitleWasSet(NavigationEntry * entry,bool explicit_set)352 void Shell::TitleWasSet(NavigationEntry* entry, bool explicit_set) {
353 if (entry)
354 PlatformSetTitle(entry->GetTitle());
355 }
356
OnDevToolsWebContentsDestroyed()357 void Shell::OnDevToolsWebContentsDestroyed() {
358 devtools_observer_.reset();
359 devtools_frontend_ = NULL;
360 }
361
362 } // namespace content
363