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 "chrome/browser/devtools/devtools_contents_resizing_strategy.h"
      9 #include "chrome/browser/devtools/devtools_toggle_action.h"
     10 #include "chrome/browser/devtools/devtools_ui_bindings.h"
     11 #include "content/public/browser/web_contents_delegate.h"
     12 
     13 class Browser;
     14 class BrowserWindow;
     15 class DevToolsControllerTest;
     16 class DevToolsEventForwarder;
     17 
     18 namespace content {
     19 class DevToolsAgentHost;
     20 struct NativeWebKeyboardEvent;
     21 class RenderViewHost;
     22 }
     23 
     24 namespace user_prefs {
     25 class PrefRegistrySyncable;
     26 }
     27 
     28 class DevToolsWindow : public DevToolsUIBindings::Delegate,
     29                        public content::WebContentsDelegate {
     30  public:
     31   static const char kDevToolsApp[];
     32 
     33   virtual ~DevToolsWindow();
     34 
     35   static std::string GetDevToolsWindowPlacementPrefKey();
     36   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
     37 
     38   // Return the DevToolsWindow for the given RenderViewHost if one exists,
     39   // otherwise NULL.
     40   static DevToolsWindow* GetInstanceForInspectedRenderViewHost(
     41       content::RenderViewHost* inspected_rvh);
     42 
     43   // Return the DevToolsWindow for the given WebContents if one exists,
     44   // otherwise NULL.
     45   static DevToolsWindow* GetInstanceForInspectedWebContents(
     46       content::WebContents* inspected_web_contents);
     47 
     48   // Return the docked DevTools WebContents for the given inspected WebContents
     49   // if one exists and should be shown in browser window, otherwise NULL.
     50   // This method will return only fully initialized window ready to be
     51   // presented in UI.
     52   // If |out_strategy| is not NULL, it will contain resizing strategy.
     53   // For immediately-ready-to-use but maybe not yet fully initialized DevTools
     54   // use |GetInstanceForInspectedRenderViewHost| instead.
     55   static content::WebContents* GetInTabWebContents(
     56       content::WebContents* inspected_tab,
     57       DevToolsContentsResizingStrategy* out_strategy);
     58 
     59   static bool IsDevToolsWindow(content::RenderViewHost* window_rvh);
     60 
     61   // Open or reveal DevTools window, and perform the specified action.
     62   static DevToolsWindow* OpenDevToolsWindow(
     63       content::RenderViewHost* inspected_rvh,
     64       const DevToolsToggleAction& action);
     65 
     66   // Open or reveal DevTools window, with no special action.
     67   static DevToolsWindow* OpenDevToolsWindow(
     68       content::RenderViewHost* inspected_rvh);
     69 
     70   static DevToolsWindow* OpenDevToolsWindowForTest(
     71       content::RenderViewHost* inspected_rvh, bool is_docked);
     72   static DevToolsWindow* OpenDevToolsWindowForTest(
     73       Browser* browser, bool is_docked);
     74 
     75   // Perform specified action for current WebContents inside a |browser|.
     76   // This may close currently open DevTools window.
     77   static DevToolsWindow* ToggleDevToolsWindow(
     78       Browser* browser,
     79       const DevToolsToggleAction& action);
     80 
     81   // External frontend is always undocked.
     82   static void OpenExternalFrontend(
     83       Profile* profile,
     84       const std::string& frontend_uri,
     85       content::DevToolsAgentHost* agent_host);
     86 
     87   // Worker frontend is always undocked.
     88   static DevToolsWindow* OpenDevToolsWindowForWorker(
     89       Profile* profile,
     90       content::DevToolsAgentHost* worker_agent);
     91 
     92   static void InspectElement(
     93       content::RenderViewHost* inspected_rvh, int x, int y);
     94 
     95   Browser* browser_for_test() { return browser_; }
     96   content::WebContents* web_contents_for_test() { return main_web_contents_; }
     97 
     98   // Sets closure to be called after load is done. If already loaded, calls
     99   // closure immediately.
    100   void SetLoadCompletedCallback(const base::Closure& closure);
    101 
    102   // Forwards an unhandled keyboard event to the DevTools frontend.
    103   bool ForwardKeyboardEvent(const content::NativeWebKeyboardEvent& event);
    104 
    105   // BeforeUnload interception ////////////////////////////////////////////////
    106 
    107   // In order to preserve any edits the user may have made in devtools, the
    108   // beforeunload event of the inspected page is hooked - devtools gets the
    109   // first shot at handling beforeunload and presents a dialog to the user. If
    110   // the user accepts the dialog then the script is given a chance to handle
    111   // it. This way 2 dialogs may be displayed: one from the devtools asking the
    112   // user to confirm that they're ok with their devtools edits going away and
    113   // another from the webpage as the result of its beforeunload handler.
    114   // The following set of methods handle beforeunload event flow through
    115   // devtools window. When the |contents| with devtools opened on them are
    116   // getting closed, the following sequence of calls takes place:
    117   // 1. |DevToolsWindow::InterceptPageBeforeUnload| is called and indicates
    118   //    whether devtools intercept the beforeunload event.
    119   //    If InterceptPageBeforeUnload() returns true then the following steps
    120   //    will take place; otherwise only step 4 will be reached and none of the
    121   //    corresponding functions in steps 2 & 3 will get called.
    122   // 2. |DevToolsWindow::InterceptPageBeforeUnload| fires beforeunload event
    123   //    for devtools frontend, which will asynchronously call
    124   //    |WebContentsDelegate::BeforeUnloadFired| method.
    125   //    In case of docked devtools window, devtools are set as a delegate for
    126   //    its frontend, so method |DevToolsWindow::BeforeUnloadFired| will be
    127   //    called directly.
    128   //    If devtools window is undocked it's not set as the delegate so the call
    129   //    to BeforeUnloadFired is proxied through HandleBeforeUnload() rather
    130   //    than getting called directly.
    131   // 3a. If |DevToolsWindow::BeforeUnloadFired| is called with |proceed|=false
    132   //     it calls throught to the content's BeforeUnloadFired(), which from the
    133   //     WebContents perspective looks the same as the |content|'s own
    134   //     beforeunload dialog having had it's 'stay on this page' button clicked.
    135   // 3b. If |proceed| = true, then it fires beforeunload event on |contents|
    136   //     and everything proceeds as it normally would without the Devtools
    137   //     interception.
    138   // 4. If the user cancels the dialog put up by either the WebContents or
    139   //    devtools frontend, then |contents|'s |BeforeUnloadFired| callback is
    140   //    called with the proceed argument set to false, this causes
    141   //    |DevToolsWindow::OnPageCloseCancelled| to be called.
    142 
    143   // Devtools window in undocked state is not set as a delegate of
    144   // its frontend. Instead, an instance of browser is set as the delegate, and
    145   // thus beforeunload event callback from devtools frontend is not delivered
    146   // to the instance of devtools window, which is solely responsible for
    147   // managing custom beforeunload event flow.
    148   // This is a helper method to route callback from
    149   // |Browser::BeforeUnloadFired| back to |DevToolsWindow::BeforeUnloadFired|.
    150   // * |proceed| - true if the user clicked 'ok' in the beforeunload dialog,
    151   //   false otherwise.
    152   // * |proceed_to_fire_unload| - output parameter, whether we should continue
    153   //   to fire the unload event or stop things here.
    154   // Returns true if devtools window is in a state of intercepting beforeunload
    155   // event and if it will manage unload process on its own.
    156   static bool HandleBeforeUnload(content::WebContents* contents,
    157                                  bool proceed,
    158                                  bool* proceed_to_fire_unload);
    159 
    160   // Returns true if this contents beforeunload event was intercepted by
    161   // devtools and false otherwise. If the event was intercepted, caller should
    162   // not fire beforeunlaod event on |contents| itself as devtools window will
    163   // take care of it, otherwise caller should continue handling the event as
    164   // usual.
    165   static bool InterceptPageBeforeUnload(content::WebContents* contents);
    166 
    167   // Returns true if devtools browser has already fired its beforeunload event
    168   // as a result of beforeunload event interception.
    169   static bool HasFiredBeforeUnloadEventForDevToolsBrowser(Browser* browser);
    170 
    171   // Returns true if devtools window would like to hook beforeunload event
    172   // of this |contents|.
    173   static bool NeedsToInterceptBeforeUnload(content::WebContents* contents);
    174 
    175   // Notify devtools window that closing of |contents| was cancelled
    176   // by user.
    177   static void OnPageCloseCanceled(content::WebContents* contents);
    178 
    179  private:
    180   friend class DevToolsControllerTest;
    181   friend class DevToolsSanityTest;
    182   friend class BrowserWindowControllerTest;
    183 
    184   // DevTools initialization typically follows this way:
    185   // - Toggle/Open: client call;
    186   // - Create;
    187   // - ScheduleShow: setup window to be functional, but not yet show;
    188   // - DocumentOnLoadCompletedInMainFrame: frontend loaded;
    189   // - SetIsDocked: frontend decided on docking state;
    190   // - OnLoadCompleted: ready to present frontend;
    191   // - Show: actually placing frontend WebContents to a Browser or docked place;
    192   // - DoAction: perform action passed in Toggle/Open.
    193   enum LoadState {
    194     kNotLoaded,
    195     kOnLoadFired, // Implies SetIsDocked was not yet called.
    196     kIsDockedSet, // Implies DocumentOnLoadCompleted was not yet called.
    197     kLoadCompleted
    198   };
    199 
    200   DevToolsWindow(Profile* profile,
    201                  const GURL& frontend_url,
    202                  content::RenderViewHost* inspected_rvh,
    203                  bool can_dock);
    204 
    205   static DevToolsWindow* Create(Profile* profile,
    206                                 const GURL& frontend_url,
    207                                 content::RenderViewHost* inspected_rvh,
    208                                 bool shared_worker_frontend,
    209                                 bool external_frontend,
    210                                 bool can_dock);
    211   static GURL GetDevToolsURL(Profile* profile,
    212                              const GURL& base_url,
    213                              bool shared_worker_frontend,
    214                              bool external_frontend,
    215                              bool can_dock);
    216   static DevToolsWindow* FindDevToolsWindow(content::DevToolsAgentHost*);
    217   static DevToolsWindow* AsDevToolsWindow(content::RenderViewHost*);
    218   static DevToolsWindow* CreateDevToolsWindowForWorker(Profile* profile);
    219   static bool FindInspectedBrowserAndTabIndex(
    220       content::WebContents* inspected_web_contents, Browser**, int* tab);
    221   static DevToolsWindow* ToggleDevToolsWindow(
    222       content::RenderViewHost* inspected_rvh,
    223       bool force_open,
    224       const DevToolsToggleAction& action);
    225 
    226   // content::WebContentsDelegate:
    227   virtual content::WebContents* OpenURLFromTab(
    228       content::WebContents* source,
    229       const content::OpenURLParams& params) OVERRIDE;
    230   virtual void ActivateContents(content::WebContents* contents) OVERRIDE;
    231   virtual void AddNewContents(content::WebContents* source,
    232                               content::WebContents* new_contents,
    233                               WindowOpenDisposition disposition,
    234                               const gfx::Rect& initial_pos,
    235                               bool user_gesture,
    236                               bool* was_blocked) OVERRIDE;
    237   virtual void WebContentsCreated(content::WebContents* source_contents,
    238                                   int opener_render_frame_id,
    239                                   const base::string16& frame_name,
    240                                   const GURL& target_url,
    241                                   content::WebContents* new_contents) OVERRIDE;
    242   virtual void CloseContents(content::WebContents* source) OVERRIDE;
    243   virtual void ContentsZoomChange(bool zoom_in) OVERRIDE;
    244   virtual void BeforeUnloadFired(content::WebContents* tab,
    245                                  bool proceed,
    246                                  bool* proceed_to_fire_unload) OVERRIDE;
    247   virtual bool PreHandleKeyboardEvent(
    248       content::WebContents* source,
    249       const content::NativeWebKeyboardEvent& event,
    250       bool* is_keyboard_shortcut) OVERRIDE;
    251   virtual void HandleKeyboardEvent(
    252       content::WebContents* source,
    253       const content::NativeWebKeyboardEvent& event) OVERRIDE;
    254   virtual content::JavaScriptDialogManager*
    255       GetJavaScriptDialogManager() OVERRIDE;
    256   virtual content::ColorChooser* OpenColorChooser(
    257       content::WebContents* web_contents,
    258       SkColor color,
    259       const std::vector<content::ColorSuggestion>& suggestions) OVERRIDE;
    260   virtual void RunFileChooser(
    261       content::WebContents* web_contents,
    262       const content::FileChooserParams& params) OVERRIDE;
    263   virtual void WebContentsFocused(content::WebContents* contents) OVERRIDE;
    264   virtual bool PreHandleGestureEvent(
    265       content::WebContents* source,
    266       const blink::WebGestureEvent& event) OVERRIDE;
    267 
    268   // content::DevToolsUIBindings::Delegate overrides
    269   virtual void ActivateWindow() OVERRIDE;
    270   virtual void CloseWindow() OVERRIDE;
    271   virtual void SetInspectedPageBounds(const gfx::Rect& rect) OVERRIDE;
    272   virtual void SetContentsResizingStrategy(
    273       const gfx::Insets& insets, const gfx::Size& min_size) OVERRIDE;
    274   virtual void InspectElementCompleted() OVERRIDE;
    275   virtual void MoveWindow(int x, int y) OVERRIDE;
    276   virtual void SetIsDocked(bool is_docked) OVERRIDE;
    277   virtual void OpenInNewTab(const std::string& url) OVERRIDE;
    278   virtual void SetWhitelistedShortcuts(const std::string& message) OVERRIDE;
    279   virtual void InspectedContentsClosing() OVERRIDE;
    280   virtual void OnLoadCompleted() OVERRIDE;
    281   virtual InfoBarService* GetInfoBarService() OVERRIDE;
    282   virtual void RenderProcessGone() OVERRIDE;
    283 
    284   void CreateDevToolsBrowser();
    285   BrowserWindow* GetInspectedBrowserWindow();
    286   void ScheduleShow(const DevToolsToggleAction& action);
    287   void Show(const DevToolsToggleAction& action);
    288   void DoAction(const DevToolsToggleAction& action);
    289   void LoadCompleted();
    290   void SetIsDockedAndShowImmediatelyForTest(bool is_docked);
    291   void UpdateBrowserToolbar();
    292   void UpdateBrowserWindow();
    293   content::WebContents* GetInspectedWebContents();
    294 
    295   class InspectedWebContentsObserver;
    296   scoped_ptr<InspectedWebContentsObserver> inspected_contents_observer_;
    297 
    298   Profile* profile_;
    299   content::WebContents* main_web_contents_;
    300   content::WebContents* toolbox_web_contents_;
    301   DevToolsUIBindings* bindings_;
    302   Browser* browser_;
    303   bool is_docked_;
    304   const bool can_dock_;
    305   LoadState load_state_;
    306   DevToolsToggleAction action_on_load_;
    307   bool ignore_set_is_docked_;
    308   DevToolsContentsResizingStrategy contents_resizing_strategy_;
    309   // True if we're in the process of handling a beforeunload event originating
    310   // from the inspected webcontents, see InterceptPageBeforeUnload for details.
    311   bool intercepted_page_beforeunload_;
    312   base::Closure load_completed_callback_;
    313 
    314   base::TimeTicks inspect_element_start_time_;
    315   scoped_ptr<DevToolsEventForwarder> event_forwarder_;
    316 
    317   friend class DevToolsEventForwarder;
    318   DISALLOW_COPY_AND_ASSIGN(DevToolsWindow);
    319 };
    320 
    321 #endif  // CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_
    322