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
31namespace content {
32
33const int Shell::kDefaultTestWindowWidthDip = 800;
34const int Shell::kDefaultTestWindowHeightDip = 600;
35
36std::vector<Shell*> Shell::windows_;
37base::Callback<void(Shell*)> Shell::shell_created_callback_;
38
39bool Shell::quit_message_loop_ = true;
40
41class 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
59Shell::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
77Shell::~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
95Shell* 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
115void 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
125void 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
131Shell* 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
142void Shell::Initialize() {
143  PlatformInitialize(
144      gfx::Size(kDefaultTestWindowWidthDip, kDefaultTestWindowHeightDip));
145}
146
147gfx::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
153Shell* 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
168void Shell::LoadURL(const GURL& url) {
169  LoadURLForFrame(url, std::string());
170}
171
172void 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
181void 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
193void 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
204void Shell::GoBackOrForward(int offset) {
205  web_contents_->GetController().GoToOffset(offset);
206  web_contents_->Focus();
207}
208
209void Shell::Reload() {
210  web_contents_->GetController().Reload(false);
211  web_contents_->Focus();
212}
213
214void Shell::Stop() {
215  web_contents_->Stop();
216  web_contents_->Focus();
217}
218
219void 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
229void Shell::ShowDevTools() {
230  InnerShowDevTools("", "");
231}
232
233void Shell::ShowDevToolsForElementAt(int x, int y) {
234  InnerShowDevTools("", "");
235  devtools_frontend_->InspectElementAt(x, y);
236}
237
238void Shell::ShowDevToolsForTest(const std::string& settings,
239                                const std::string& frontend_url) {
240  InnerShowDevTools(settings, frontend_url);
241}
242
243void Shell::CloseDevTools() {
244  if (!devtools_frontend_)
245    return;
246  devtools_observer_.reset();
247  devtools_frontend_->Close();
248  devtools_frontend_ = NULL;
249}
250
251gfx::NativeView Shell::GetContentView() {
252  if (!web_contents_)
253    return NULL;
254  return web_contents_->GetNativeView();
255}
256
257WebContents* 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
282void Shell::LoadingStateChanged(WebContents* source,
283    bool to_different_document) {
284  UpdateNavigationControls(to_different_document);
285  PlatformSetIsLoading(source->IsLoading());
286}
287
288void 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
301bool 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
309void Shell::RequestToLockMouse(WebContents* web_contents,
310                               bool user_gesture,
311                               bool last_unlocked_by_target) {
312  web_contents->GotResponseToLockMouseRequest(true);
313}
314
315void Shell::CloseContents(WebContents* source) {
316  Close();
317}
318
319bool Shell::CanOverscrollContent() const {
320#if defined(USE_AURA)
321  return true;
322#else
323  return false;
324#endif
325}
326
327void Shell::DidNavigateMainFramePostCommit(WebContents* web_contents) {
328  PlatformSetAddressBarURL(web_contents->GetLastCommittedURL());
329}
330
331JavaScriptDialogManager* Shell::GetJavaScriptDialogManager() {
332  if (!dialog_manager_)
333    dialog_manager_.reset(new ShellJavaScriptDialogManager());
334  return dialog_manager_.get();
335}
336
337bool 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
345void Shell::RendererUnresponsive(WebContents* source) {
346  if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree))
347    return;
348  WebKitTestController::Get()->RendererUnresponsive();
349}
350
351void Shell::ActivateContents(WebContents* contents) {
352  contents->GetRenderViewHost()->Focus();
353}
354
355void Shell::DeactivateContents(WebContents* contents) {
356  contents->GetRenderViewHost()->Blur();
357}
358
359void Shell::WorkerCrashed(WebContents* source) {
360  if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree))
361    return;
362  WebKitTestController::Get()->WorkerCrashed();
363}
364
365bool Shell::HandleContextMenu(const content::ContextMenuParams& params) {
366  return PlatformHandleContextMenu(params);
367}
368
369void Shell::WebContentsFocused(WebContents* contents) {
370#if defined(TOOLKIT_VIEWS)
371  PlatformWebContentsFocused(contents);
372#endif
373}
374
375void Shell::TitleWasSet(NavigationEntry* entry, bool explicit_set) {
376  if (entry)
377    PlatformSetTitle(entry->GetTitle());
378}
379
380void 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
393void Shell::OnDevToolsWebContentsDestroyed() {
394  devtools_observer_.reset();
395  devtools_frontend_ = NULL;
396}
397
398}  // namespace content
399