shell.cc revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
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/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/browser/notify_done_forwarder.h"
27#include "content/shell/browser/shell_browser_main_parts.h"
28#include "content/shell/browser/shell_content_browser_client.h"
29#include "content/shell/browser/shell_devtools_frontend.h"
30#include "content/shell/browser/shell_javascript_dialog_manager.h"
31#include "content/shell/browser/webkit_test_controller.h"
32#include "content/shell/common/shell_messages.h"
33#include "content/shell/common/shell_switches.h"
34
35namespace content {
36
37const int Shell::kDefaultTestWindowWidthDip = 800;
38const int Shell::kDefaultTestWindowHeightDip = 600;
39
40std::vector<Shell*> Shell::windows_;
41base::Callback<void(Shell*)> Shell::shell_created_callback_;
42
43bool Shell::quit_message_loop_ = true;
44
45class 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
63Shell::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
85Shell::~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
100Shell* 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
120void 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
129void 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
135Shell* 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
146void Shell::Initialize() {
147  PlatformInitialize(
148      gfx::Size(kDefaultTestWindowWidthDip, kDefaultTestWindowHeightDip));
149}
150
151Shell* 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
170void Shell::LoadURL(const GURL& url) {
171  LoadURLForFrame(url, std::string());
172}
173
174void 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
183void Shell::GoBackOrForward(int offset) {
184  web_contents_->GetController().GoToOffset(offset);
185  web_contents_->GetView()->Focus();
186}
187
188void Shell::Reload() {
189  web_contents_->GetController().Reload(false);
190  web_contents_->GetView()->Focus();
191}
192
193void Shell::Stop() {
194  web_contents_->Stop();
195  web_contents_->GetView()->Focus();
196}
197
198void 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
207void 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
217void Shell::CloseDevTools() {
218  if (!devtools_frontend_)
219    return;
220  devtools_observer_.reset();
221  devtools_frontend_->Close();
222  devtools_frontend_ = NULL;
223}
224
225gfx::NativeView Shell::GetContentView() {
226  if (!web_contents_)
227    return NULL;
228  return web_contents_->GetView()->GetNativeView();
229}
230
231WebContents* Shell::OpenURLFromTab(WebContents* source,
232                                   const OpenURLParams& params) {
233  // CURRENT_TAB is the only one we implement for now.
234  if (params.disposition != CURRENT_TAB)
235      return NULL;
236  NavigationController::LoadURLParams load_url_params(params.url);
237  load_url_params.referrer = params.referrer;
238  load_url_params.transition_type = params.transition;
239  load_url_params.extra_headers = params.extra_headers;
240  load_url_params.should_replace_current_entry =
241      params.should_replace_current_entry;
242
243  if (params.transferred_global_request_id != GlobalRequestID()) {
244    load_url_params.is_renderer_initiated = params.is_renderer_initiated;
245    load_url_params.transferred_global_request_id =
246        params.transferred_global_request_id;
247  } else if (params.is_renderer_initiated) {
248    load_url_params.is_renderer_initiated = true;
249  }
250
251  source->GetController().LoadURLWithParams(load_url_params);
252  return source;
253}
254
255void Shell::LoadingStateChanged(WebContents* source) {
256  UpdateNavigationControls();
257  PlatformSetIsLoading(source->IsLoading());
258}
259
260void Shell::ToggleFullscreenModeForTab(WebContents* web_contents,
261                                       bool enter_fullscreen) {
262#if defined(OS_ANDROID)
263  PlatformToggleFullscreenModeForTab(web_contents, enter_fullscreen);
264#endif
265  if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree))
266    return;
267  if (is_fullscreen_ != enter_fullscreen) {
268    is_fullscreen_ = enter_fullscreen;
269    web_contents->GetRenderViewHost()->WasResized();
270  }
271}
272
273bool Shell::IsFullscreenForTabOrPending(const WebContents* web_contents) const {
274#if defined(OS_ANDROID)
275  return PlatformIsFullscreenForTabOrPending(web_contents);
276#else
277  return is_fullscreen_;
278#endif
279}
280
281void Shell::RequestToLockMouse(WebContents* web_contents,
282                               bool user_gesture,
283                               bool last_unlocked_by_target) {
284  web_contents->GotResponseToLockMouseRequest(true);
285}
286
287void Shell::CloseContents(WebContents* source) {
288  Close();
289}
290
291bool Shell::CanOverscrollContent() const {
292#if defined(USE_AURA)
293  return true;
294#else
295  return false;
296#endif
297}
298
299void Shell::WebContentsCreated(WebContents* source_contents,
300                               int64 source_frame_id,
301                               const string16& frame_name,
302                               const GURL& target_url,
303                               WebContents* new_contents) {
304  CreateShell(new_contents, source_contents->GetView()->GetContainerSize());
305  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree))
306    NotifyDoneForwarder::CreateForWebContents(new_contents);
307}
308
309void Shell::DidNavigateMainFramePostCommit(WebContents* web_contents) {
310  PlatformSetAddressBarURL(web_contents->GetLastCommittedURL());
311}
312
313JavaScriptDialogManager* Shell::GetJavaScriptDialogManager() {
314  if (!dialog_manager_)
315    dialog_manager_.reset(new ShellJavaScriptDialogManager());
316  return dialog_manager_.get();
317}
318
319bool Shell::AddMessageToConsole(WebContents* source,
320                                int32 level,
321                                const string16& message,
322                                int32 line_no,
323                                const string16& source_id) {
324  return CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree);
325}
326
327void Shell::RendererUnresponsive(WebContents* source) {
328  if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree))
329    return;
330  WebKitTestController::Get()->RendererUnresponsive();
331}
332
333void Shell::ActivateContents(WebContents* contents) {
334  contents->GetRenderViewHost()->Focus();
335}
336
337void Shell::DeactivateContents(WebContents* contents) {
338  contents->GetRenderViewHost()->Blur();
339}
340
341void Shell::WorkerCrashed(WebContents* source) {
342  if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree))
343    return;
344  WebKitTestController::Get()->WorkerCrashed();
345}
346
347void Shell::Observe(int type,
348                    const NotificationSource& source,
349                    const NotificationDetails& details) {
350  if (type == NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED) {
351    std::pair<NavigationEntry*, bool>* title =
352        Details<std::pair<NavigationEntry*, bool> >(details).ptr();
353
354    if (title->first) {
355      string16 text = title->first->GetTitle();
356      PlatformSetTitle(text);
357    }
358  } else {
359    NOTREACHED();
360  }
361}
362
363void Shell::OnDevToolsWebContentsDestroyed() {
364  devtools_observer_.reset();
365  devtools_frontend_ = NULL;
366}
367
368}  // namespace content
369