Home | History | Annotate | Download | only in devtools
      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 #ifndef CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_
      6 #define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_
      7 
      8 #include <string>
      9 #include <vector>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/memory/weak_ptr.h"
     14 #include "base/strings/string16.h"
     15 #include "chrome/browser/devtools/devtools_embedder_message_dispatcher.h"
     16 #include "chrome/browser/devtools/devtools_file_helper.h"
     17 #include "chrome/browser/devtools/devtools_file_system_indexer.h"
     18 #include "chrome/browser/devtools/devtools_toggle_action.h"
     19 #include "content/public/browser/devtools_client_host.h"
     20 #include "content/public/browser/devtools_frontend_host_delegate.h"
     21 #include "content/public/browser/notification_observer.h"
     22 #include "content/public/browser/notification_registrar.h"
     23 #include "content/public/browser/web_contents_delegate.h"
     24 
     25 class Browser;
     26 class BrowserWindow;
     27 class DevToolsControllerTest;
     28 class Profile;
     29 
     30 namespace base {
     31 class Value;
     32 }
     33 
     34 namespace content {
     35 class DevToolsAgentHost;
     36 class DevToolsClientHost;
     37 struct FileChooserParams;
     38 class RenderViewHost;
     39 class WebContents;
     40 }
     41 
     42 namespace IPC {
     43 class Message;
     44 }
     45 
     46 namespace user_prefs {
     47 class PrefRegistrySyncable;
     48 }
     49 
     50 enum DevToolsDockSide {
     51   DEVTOOLS_DOCK_SIDE_UNDOCKED = 0,
     52   DEVTOOLS_DOCK_SIDE_BOTTOM,
     53   DEVTOOLS_DOCK_SIDE_RIGHT,
     54   DEVTOOLS_DOCK_SIDE_MINIMIZED
     55 };
     56 
     57 class DevToolsWindow : private content::NotificationObserver,
     58                        private content::WebContentsDelegate,
     59                        private content::DevToolsFrontendHostDelegate,
     60                        private DevToolsEmbedderMessageDispatcher::Delegate {
     61  public:
     62   typedef base::Callback<void(bool)> InfoBarCallback;
     63 
     64   static const char kDevToolsApp[];
     65 
     66   virtual ~DevToolsWindow();
     67 
     68   static std::string GetDevToolsWindowPlacementPrefKey();
     69   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
     70   // Return the DevToolsWindow for the given RenderViewHost if one exists,
     71   // otherwise NULL.
     72   static DevToolsWindow* GetInstanceForInspectedRenderViewHost(
     73       content::RenderViewHost* inspected_rvh);
     74   static DevToolsWindow* GetDockedInstanceForInspectedTab(
     75       content::WebContents* inspected_tab);
     76   static bool IsDevToolsWindow(content::RenderViewHost* window_rvh);
     77   static DevToolsWindow* OpenDevToolsWindowForWorker(
     78       Profile* profile,
     79       content::DevToolsAgentHost* worker_agent);
     80   static DevToolsWindow* CreateDevToolsWindowForWorker(Profile* profile);
     81   static DevToolsWindow* OpenDevToolsWindow(
     82       content::RenderViewHost* inspected_rvh);
     83   static DevToolsWindow* ToggleDevToolsWindow(
     84       Browser* browser,
     85       const DevToolsToggleAction& action);
     86   static void OpenExternalFrontend(Profile* profile,
     87                                    const std::string& frontend_uri,
     88                                    content::DevToolsAgentHost* agent_host);
     89 
     90   // Exposed for testing, normal clients should not use this method.
     91   static DevToolsWindow* ToggleDevToolsWindow(
     92       content::RenderViewHost* inspected_rvh,
     93       bool force_open,
     94       const DevToolsToggleAction& action);
     95 
     96   static void InspectElement(
     97       content::RenderViewHost* inspected_rvh, int x, int y);
     98 
     99   static int GetMinimumWidth();
    100   static int GetMinimumHeight();
    101   static int GetMinimizedHeight();
    102 
    103   // content::DevToolsFrontendHostDelegate:
    104   virtual void InspectedContentsClosing() OVERRIDE;
    105 
    106   content::WebContents* web_contents() { return web_contents_; }
    107   Browser* browser() { return browser_; }  // For tests.
    108   DevToolsDockSide dock_side() const { return dock_side_; }
    109 
    110   content::RenderViewHost* GetRenderViewHost();
    111   content::DevToolsClientHost* GetDevToolsClientHostForTest();
    112 
    113   // Returns preferred devtools window width for given |container_width|. It
    114   // tries to use the saved window width, or, if none exists, 1/3 of the
    115   // container width, then clamps to try and ensure both devtools and content
    116   // are at least somewhat visible.
    117   // Called only for the case when devtools window is docked to the side.
    118   int GetWidth(int container_width);
    119 
    120   // Returns preferred devtools window height for given |container_height|.
    121   // Uses the same logic as GetWidth.
    122   // Called only for the case when devtools window is docked to bottom.
    123   int GetHeight(int container_height);
    124 
    125   // Stores preferred devtools window width for this instance.
    126   void SetWidth(int width);
    127 
    128   // Stores preferred devtools window height for this instance.
    129   void SetHeight(int height);
    130 
    131   void Show(const DevToolsToggleAction& action);
    132 
    133   // BeforeUnload interception ////////////////////////////////////////////////
    134 
    135   // In order to preserve any edits the user may have made in devtools, the
    136   // beforeunload event of the inspected page is hooked - devtools gets the
    137   // first shot at handling beforeunload and presents a dialog to the user. If
    138   // the user accepts the dialog then the script is given a chance to handle
    139   // it. This way 2 dialogs may be displayed: one from the devtools asking the
    140   // user to confirm that they're ok with their devtools edits going away and
    141   // another from the webpage as the result of its beforeunload handler.
    142   // The following set of methods handle beforeunload event flow through
    143   // devtools window. When the |contents| with devtools opened on them are
    144   // getting closed, the following sequence of calls takes place:
    145   // 1. |DevToolsWindow::InterceptPageBeforeUnload| is called and indicates
    146   //    whether devtools intercept the beforeunload event.
    147   //    If InterceptPageBeforeUnload() returns true then the following steps
    148   //    will take place; otherwise only step 4 will be reached and none of the
    149   //    corresponding functions in steps 2 & 3 will get called.
    150   // 2. |DevToolsWindow::InterceptPageBeforeUnload| fires beforeunload event
    151   //    for devtools frontend, which will asynchronously call
    152   //    |WebContentsDelegate::BeforeUnloadFired| method.
    153   //    In case of docked devtools window, devtools are set as a delegate for
    154   //    its frontend, so method |DevToolsWindow::BeforeUnloadFired| will be
    155   //    called directly.
    156   //    If devtools window is undocked it's not set as the delegate so the call
    157   //    to BeforeUnloadFired is proxied through HandleBeforeUnload() rather
    158   //    than getting called directly.
    159   // 3a. If |DevToolsWindow::BeforeUnloadFired| is called with |proceed|=false
    160   //     it calls throught to the content's BeforeUnloadFired(), which from the
    161   //     WebContents perspective looks the same as the |content|'s own
    162   //     beforeunload dialog having had it's 'stay on this page' button clicked.
    163   // 3b. If |proceed| = true, then it fires beforeunload event on |contents|
    164   //     and everything proceeds as it normally would without the Devtools
    165   //     interception.
    166   // 4. If the user cancels the dialog put up by either the WebContents or
    167   //    devtools frontend, then |contents|'s |BeforeUnloadFired| callback is
    168   //    called with the proceed argument set to false, this causes
    169   //    |DevToolsWindow::OnPageCloseCancelled| to be called.
    170 
    171   // Devtools window in undocked state is not set as a delegate of
    172   // its frontend. Instead, an instance of browser is set as the delegate, and
    173   // thus beforeunload event callback from devtools frontend is not delivered
    174   // to the instance of devtools window, which is solely responsible for
    175   // managing custom beforeunload event flow.
    176   // This is a helper method to route callback from
    177   // |Browser::BeforeUnloadFired| back to |DevToolsWindow::BeforeUnloadFired|.
    178   // * |proceed| - true if the user clicked 'ok' in the beforeunload dialog,
    179   //   false otherwise.
    180   // * |proceed_to_fire_unload| - output parameter, whether we should continue
    181   //   to fire the unload event or stop things here.
    182   // Returns true if devtools window is in a state of intercepting beforeunload
    183   // event and if it will manage unload process on its own.
    184   static bool HandleBeforeUnload(content::WebContents* contents,
    185                                  bool proceed,
    186                                  bool* proceed_to_fire_unload);
    187 
    188   // Returns true if this contents beforeunload event was intercepted by
    189   // devtools and false otherwise. If the event was intercepted, caller should
    190   // not fire beforeunlaod event on |contents| itself as devtools window will
    191   // take care of it, otherwise caller should continue handling the event as
    192   // usual.
    193   static bool InterceptPageBeforeUnload(content::WebContents* contents);
    194 
    195   // Returns true if devtools browser has already fired its beforeunload event
    196   // as a result of beforeunload event interception.
    197   static bool HasFiredBeforeUnloadEventForDevToolsBrowser(Browser* browser);
    198 
    199   // Returns true if devtools window would like to hook beforeunload event
    200   // of this |contents|.
    201   static bool NeedsToInterceptBeforeUnload(content::WebContents* contents);
    202 
    203   // Notify devtools window that closing of |contents| was cancelled
    204   // by user.
    205   static void OnPageCloseCanceled(content::WebContents* contents);
    206 
    207   void SetDockSideForTest(DevToolsDockSide dock_side);
    208 
    209  private:
    210   friend class DevToolsControllerTest;
    211 
    212   DevToolsWindow(Profile* profile,
    213                  const GURL& frontend_url,
    214                  content::RenderViewHost* inspected_rvh,
    215                  DevToolsDockSide dock_side);
    216 
    217   static DevToolsWindow* Create(Profile* profile,
    218                                 const GURL& frontend_url,
    219                                 content::RenderViewHost* inspected_rvh,
    220                                 DevToolsDockSide dock_side,
    221                                 bool shared_worker_frontend,
    222                                 bool external_frontend,
    223                                 bool can_dock);
    224   static GURL GetDevToolsURL(Profile* profile,
    225                              const GURL& base_url,
    226                              DevToolsDockSide dock_side,
    227                              bool shared_worker_frontend,
    228                              bool external_frontend,
    229                              bool can_dock);
    230   static DevToolsWindow* FindDevToolsWindow(content::DevToolsAgentHost*);
    231   static DevToolsWindow* AsDevToolsWindow(content::RenderViewHost*);
    232   static DevToolsDockSide GetDockSideFromPrefs(Profile* profile);
    233   static std::string SideToString(DevToolsDockSide dock_side);
    234   static DevToolsDockSide SideFromString(const std::string& dock_side);
    235   static bool FindInspectedBrowserAndTabIndex(
    236       content::WebContents* inspected_web_contents, Browser**, int* tab);
    237 
    238   // content::NotificationObserver:
    239   virtual void Observe(int type,
    240                        const content::NotificationSource& source,
    241                        const content::NotificationDetails& details) OVERRIDE;
    242 
    243   // content::WebContentsDelegate:
    244   virtual content::WebContents* OpenURLFromTab(
    245       content::WebContents* source,
    246       const content::OpenURLParams& params) OVERRIDE;
    247   virtual void AddNewContents(content::WebContents* source,
    248                               content::WebContents* new_contents,
    249                               WindowOpenDisposition disposition,
    250                               const gfx::Rect& initial_pos,
    251                               bool user_gesture,
    252                               bool* was_blocked) OVERRIDE;
    253   virtual void CloseContents(content::WebContents* source) OVERRIDE;
    254   virtual void BeforeUnloadFired(content::WebContents* tab,
    255                                  bool proceed,
    256                                  bool* proceed_to_fire_unload) OVERRIDE;
    257   virtual bool PreHandleKeyboardEvent(
    258       content::WebContents* source,
    259       const content::NativeWebKeyboardEvent& event,
    260       bool* is_keyboard_shortcut) OVERRIDE;
    261   virtual void HandleKeyboardEvent(
    262       content::WebContents* source,
    263       const content::NativeWebKeyboardEvent& event) OVERRIDE;
    264   virtual content::JavaScriptDialogManager*
    265       GetJavaScriptDialogManager() OVERRIDE;
    266   virtual content::ColorChooser* OpenColorChooser(
    267       content::WebContents* web_contents,
    268       SkColor color,
    269       const std::vector<content::ColorSuggestion>& suggestions) OVERRIDE;
    270   virtual void RunFileChooser(
    271       content::WebContents* web_contents,
    272       const content::FileChooserParams& params) OVERRIDE;
    273   virtual void WebContentsFocused(content::WebContents* contents) OVERRIDE;
    274 
    275   // content::DevToolsFrontendHostDelegate override:
    276   virtual void DispatchOnEmbedder(const std::string& message) OVERRIDE;
    277 
    278   // DevToolsEmbedderMessageDispatcher::Delegate overrides:
    279   virtual void ActivateWindow() OVERRIDE;
    280   virtual void ActivateContents(content::WebContents* contents) OVERRIDE;
    281   virtual void CloseWindow() OVERRIDE;
    282   virtual void SetWindowBounds(int x, int y, int width, int height) OVERRIDE;
    283   virtual void MoveWindow(int x, int y) OVERRIDE;
    284   virtual void SetDockSide(const std::string& side) OVERRIDE;
    285   virtual void OpenInNewTab(const std::string& url) OVERRIDE;
    286   virtual void SaveToFile(const std::string& url,
    287                           const std::string& content,
    288                           bool save_as) OVERRIDE;
    289   virtual void AppendToFile(const std::string& url,
    290                             const std::string& content) OVERRIDE;
    291   virtual void RequestFileSystems() OVERRIDE;
    292   virtual void AddFileSystem() OVERRIDE;
    293   virtual void RemoveFileSystem(const std::string& file_system_path) OVERRIDE;
    294   virtual void UpgradeDraggedFileSystemPermissions(
    295       const std::string& file_system_url) OVERRIDE;
    296   virtual void IndexPath(int request_id,
    297                          const std::string& file_system_path) OVERRIDE;
    298   virtual void StopIndexing(int request_id) OVERRIDE;
    299   virtual void SearchInPath(int request_id,
    300                             const std::string& file_system_path,
    301                             const std::string& query) OVERRIDE;
    302 
    303   // DevToolsFileHelper callbacks.
    304   void FileSavedAs(const std::string& url);
    305   void CanceledFileSaveAs(const std::string& url);
    306   void AppendedTo(const std::string& url);
    307   void FileSystemsLoaded(
    308       const std::vector<DevToolsFileHelper::FileSystem>& file_systems);
    309   void FileSystemAdded(const DevToolsFileHelper::FileSystem& file_system);
    310   void IndexingTotalWorkCalculated(int request_id,
    311                                    const std::string& file_system_path,
    312                                    int total_work);
    313   void IndexingWorked(int request_id,
    314                       const std::string& file_system_path,
    315                       int worked);
    316   void IndexingDone(int request_id, const std::string& file_system_path);
    317   void SearchCompleted(int request_id,
    318                        const std::string& file_system_path,
    319                        const std::vector<std::string>& file_paths);
    320   void ShowDevToolsConfirmInfoBar(const base::string16& message,
    321                                   const InfoBarCallback& callback);
    322 
    323   void CreateDevToolsBrowser();
    324   BrowserWindow* GetInspectedBrowserWindow();
    325   bool IsInspectedBrowserPopup();
    326   void UpdateFrontendDockSide();
    327   void ScheduleAction(const DevToolsToggleAction& action);
    328   void DoAction();
    329   void UpdateTheme();
    330   void AddDevToolsExtensionsToClient();
    331   void CallClientFunction(const std::string& function_name,
    332                           const base::Value* arg1,
    333                           const base::Value* arg2,
    334                           const base::Value* arg3);
    335   void UpdateBrowserToolbar();
    336   bool IsDocked();
    337   void Restore();
    338   content::WebContents* GetInspectedWebContents();
    339   void DocumentOnLoadCompletedInMainFrame();
    340 
    341   class InspectedWebContentsObserver;
    342   scoped_ptr<InspectedWebContentsObserver> inspected_contents_observer_;
    343   class FrontendWebContentsObserver;
    344   friend class FrontendWebContentsObserver;
    345   scoped_ptr<FrontendWebContentsObserver> frontend_contents_observer_;
    346 
    347   Profile* profile_;
    348   content::WebContents* web_contents_;
    349   Browser* browser_;
    350   DevToolsDockSide dock_side_;
    351   bool is_loaded_;
    352   DevToolsToggleAction action_on_load_;
    353   content::NotificationRegistrar registrar_;
    354   scoped_ptr<content::DevToolsClientHost> frontend_host_;
    355   scoped_ptr<DevToolsFileHelper> file_helper_;
    356   scoped_refptr<DevToolsFileSystemIndexer> file_system_indexer_;
    357   typedef std::map<
    358       int,
    359       scoped_refptr<DevToolsFileSystemIndexer::FileSystemIndexingJob> >
    360       IndexingJobsMap;
    361   IndexingJobsMap indexing_jobs_;
    362   int width_;
    363   int height_;
    364   DevToolsDockSide dock_side_before_minimized_;
    365   // True if we're in the process of handling a beforeunload event originating
    366   // from the inspected webcontents, see InterceptPageBeforeUnload for details.
    367   bool intercepted_page_beforeunload_;
    368 
    369   scoped_ptr<DevToolsEmbedderMessageDispatcher> embedder_message_dispatcher_;
    370   base::WeakPtrFactory<DevToolsWindow> weak_factory_;
    371   DISALLOW_COPY_AND_ASSIGN(DevToolsWindow);
    372 };
    373 
    374 #endif  // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_
    375