Home | History | Annotate | Download | only in shell
      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