1// Copyright 2014 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 EXTENSIONS_BROWSER_APP_WINDOW_APP_WINDOW_H_
6#define EXTENSIONS_BROWSER_APP_WINDOW_APP_WINDOW_H_
7
8#include <string>
9#include <vector>
10
11#include "base/memory/scoped_ptr.h"
12#include "base/memory/weak_ptr.h"
13#include "components/sessions/session_id.h"
14#include "components/web_modal/popup_manager.h"
15#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
16#include "content/public/browser/notification_observer.h"
17#include "content/public/browser/notification_registrar.h"
18#include "content/public/browser/web_contents_delegate.h"
19#include "content/public/browser/web_contents_observer.h"
20#include "extensions/browser/extension_icon_image.h"
21#include "ui/base/ui_base_types.h"  // WindowShowState
22#include "ui/gfx/image/image.h"
23#include "ui/gfx/rect.h"
24
25class GURL;
26class SkRegion;
27
28namespace base {
29class DictionaryValue;
30}
31
32namespace content {
33class BrowserContext;
34class WebContents;
35}
36
37namespace ui {
38class BaseWindow;
39}
40
41namespace extensions {
42
43class AppDelegate;
44class AppWebContentsHelper;
45class Extension;
46class NativeAppWindow;
47class PlatformAppBrowserTest;
48class WindowController;
49
50struct DraggableRegion;
51
52// Manages the web contents for app windows. The implementation for this
53// class should create and maintain the WebContents for the window, and handle
54// any message passing between the web contents and the extension system or
55// native window.
56class AppWindowContents {
57 public:
58  AppWindowContents() {}
59  virtual ~AppWindowContents() {}
60
61  // Called to initialize the WebContents, before the app window is created.
62  virtual void Initialize(content::BrowserContext* context,
63                          const GURL& url) = 0;
64
65  // Called to load the contents, after the app window is created.
66  virtual void LoadContents(int32 creator_process_id) = 0;
67
68  // Called when the native window changes.
69  virtual void NativeWindowChanged(NativeAppWindow* native_app_window) = 0;
70
71  // Called when the native window closes.
72  virtual void NativeWindowClosed() = 0;
73
74  // Called in tests when the window is shown
75  virtual void DispatchWindowShownForTests() const = 0;
76
77  virtual content::WebContents* GetWebContents() const = 0;
78
79 private:
80  DISALLOW_COPY_AND_ASSIGN(AppWindowContents);
81};
82
83// AppWindow is the type of window used by platform apps. App windows
84// have a WebContents but none of the chrome of normal browser windows.
85class AppWindow : public content::NotificationObserver,
86                  public content::WebContentsDelegate,
87                  public content::WebContentsObserver,
88                  public web_modal::WebContentsModalDialogManagerDelegate,
89                  public IconImage::Observer {
90 public:
91  enum WindowType {
92    WINDOW_TYPE_DEFAULT = 1 << 0,   // Default app window.
93    WINDOW_TYPE_PANEL = 1 << 1,     // OS controlled panel window (Ash only).
94    WINDOW_TYPE_V1_PANEL = 1 << 2,  // For apps v1 support in Ash; deprecate
95                                    // with v1 apps.
96  };
97
98  enum Frame {
99    FRAME_CHROME,  // Chrome-style window frame.
100    FRAME_NONE,    // Frameless window.
101  };
102
103  enum FullscreenType {
104    // Not fullscreen.
105    FULLSCREEN_TYPE_NONE = 0,
106
107    // Fullscreen entered by the app.window api.
108    FULLSCREEN_TYPE_WINDOW_API = 1 << 0,
109
110    // Fullscreen entered by HTML requestFullscreen().
111    FULLSCREEN_TYPE_HTML_API = 1 << 1,
112
113    // Fullscreen entered by the OS. ChromeOS uses this type of fullscreen to
114    // enter immersive fullscreen when the user hits the <F4> key.
115    FULLSCREEN_TYPE_OS = 1 << 2,
116
117    // Fullscreen mode that could not be exited by the user. ChromeOS uses
118    // this type of fullscreen to run an app in kiosk mode.
119    FULLSCREEN_TYPE_FORCED = 1 << 3,
120  };
121
122  struct BoundsSpecification {
123    // INT_MIN represents an unspecified position component.
124    static const int kUnspecifiedPosition;
125
126    BoundsSpecification();
127    ~BoundsSpecification();
128
129    // INT_MIN designates 'unspecified' for the position components, and 0
130    // designates 'unspecified' for the size components. When unspecified,
131    // they should be replaced with a default value.
132    gfx::Rect bounds;
133
134    gfx::Size minimum_size;
135    gfx::Size maximum_size;
136
137    // Reset the bounds fields to their 'unspecified' values. The minimum and
138    // maximum size constraints remain unchanged.
139    void ResetBounds();
140  };
141
142  struct CreateParams {
143    CreateParams();
144    ~CreateParams();
145
146    WindowType window_type;
147    Frame frame;
148
149    bool has_frame_color;
150    SkColor active_frame_color;
151    SkColor inactive_frame_color;
152    bool alpha_enabled;
153
154    // The initial content/inner bounds specification (excluding any window
155    // decorations).
156    BoundsSpecification content_spec;
157
158    // The initial window/outer bounds specification (including window
159    // decorations).
160    BoundsSpecification window_spec;
161
162    std::string window_key;
163
164    // The process ID of the process that requested the create.
165    int32 creator_process_id;
166
167    // Initial state of the window.
168    ui::WindowShowState state;
169
170    // If true, don't show the window after creation.
171    bool hidden;
172
173    // If true, the window will be resizable by the user. Defaults to true.
174    bool resizable;
175
176    // If true, the window will be focused on creation. Defaults to true.
177    bool focused;
178
179    // If true, the window will stay on top of other windows that are not
180    // configured to be always on top. Defaults to false.
181    bool always_on_top;
182
183    // If true, the window will be visible on all workspaces. Defaults to false.
184    bool visible_on_all_workspaces;
185
186    // The API enables developers to specify content or window bounds. This
187    // function combines them into a single, constrained window size.
188    gfx::Rect GetInitialWindowBounds(const gfx::Insets& frame_insets) const;
189
190    // The API enables developers to specify content or window size constraints.
191    // These functions combine them so that we can work with one set of
192    // constraints.
193    gfx::Size GetContentMinimumSize(const gfx::Insets& frame_insets) const;
194    gfx::Size GetContentMaximumSize(const gfx::Insets& frame_insets) const;
195    gfx::Size GetWindowMinimumSize(const gfx::Insets& frame_insets) const;
196    gfx::Size GetWindowMaximumSize(const gfx::Insets& frame_insets) const;
197  };
198
199  // Convert draggable regions in raw format to SkRegion format. Caller is
200  // responsible for deleting the returned SkRegion instance.
201  static SkRegion* RawDraggableRegionsToSkRegion(
202      const std::vector<DraggableRegion>& regions);
203
204  // The constructor and Init methods are public for constructing a AppWindow
205  // with a non-standard render interface (e.g. v1 apps using Ash Panels).
206  // Normally AppWindow::Create should be used.
207  // Takes ownership of |app_delegate| and |delegate|.
208  AppWindow(content::BrowserContext* context,
209            AppDelegate* app_delegate,
210            const Extension* extension);
211
212  // Initializes the render interface, web contents, and native window.
213  // |app_window_contents| will become owned by AppWindow.
214  void Init(const GURL& url,
215            AppWindowContents* app_window_contents,
216            const CreateParams& params);
217
218  const std::string& window_key() const { return window_key_; }
219  const SessionID& session_id() const { return session_id_; }
220  const std::string& extension_id() const { return extension_id_; }
221  content::WebContents* web_contents() const;
222  WindowType window_type() const { return window_type_; }
223  bool window_type_is_panel() const {
224    return (window_type_ == WINDOW_TYPE_PANEL ||
225            window_type_ == WINDOW_TYPE_V1_PANEL);
226  }
227  content::BrowserContext* browser_context() const { return browser_context_; }
228  const gfx::Image& app_icon() const { return app_icon_; }
229  const GURL& app_icon_url() const { return app_icon_url_; }
230  const gfx::Image& badge_icon() const { return badge_icon_; }
231  const GURL& badge_icon_url() const { return badge_icon_url_; }
232  bool is_hidden() const { return is_hidden_; }
233
234  const Extension* GetExtension() const;
235  NativeAppWindow* GetBaseWindow();
236  gfx::NativeWindow GetNativeWindow();
237
238  // Returns the bounds that should be reported to the renderer.
239  gfx::Rect GetClientBounds() const;
240
241  // NativeAppWindows should call this to determine what the window's title
242  // is on startup and from within UpdateWindowTitle().
243  base::string16 GetTitle() const;
244
245  // Call to notify ShellRegistry and delete the window. Subclasses should
246  // invoke this method instead of using "delete this".
247  void OnNativeClose();
248
249  // Should be called by native implementations when the window size, position,
250  // or minimized/maximized state has changed.
251  void OnNativeWindowChanged();
252
253  // Should be called by native implementations when the window is activated.
254  void OnNativeWindowActivated();
255
256  // Specifies a url for the launcher icon.
257  void SetAppIconUrl(const GURL& icon_url);
258
259  // Specifies a url for the window badge.
260  void SetBadgeIconUrl(const GURL& icon_url);
261
262  // Clear the current badge.
263  void ClearBadge();
264
265  // Set the window shape. Passing a NULL |region| sets the default shape.
266  void UpdateShape(scoped_ptr<SkRegion> region);
267
268  // Called from the render interface to modify the draggable regions.
269  void UpdateDraggableRegions(const std::vector<DraggableRegion>& regions);
270
271  // Updates the app image to |image|. Called internally from the image loader
272  // callback. Also called externally for v1 apps using Ash Panels.
273  void UpdateAppIcon(const gfx::Image& image);
274
275  // Enable or disable fullscreen mode. |type| specifies which type of
276  // fullscreen mode to change (note that disabling one type of fullscreen may
277  // not exit fullscreen mode because a window may have a different type of
278  // fullscreen enabled). If |type| is not FORCED, checks that the extension has
279  // the required permission.
280  void SetFullscreen(FullscreenType type, bool enable);
281
282  // Returns true if the app window is in a fullscreen state.
283  bool IsFullscreen() const;
284
285  // Returns true if the app window is in a forced fullscreen state (one that
286  // cannot be exited by the user).
287  bool IsForcedFullscreen() const;
288
289  // Returns true if the app window is in a fullscreen state entered from an
290  // HTML API request.
291  bool IsHtmlApiFullscreen() const;
292
293  // Transitions window into fullscreen, maximized, minimized or restores based
294  // on chrome.app.window API.
295  void Fullscreen();
296  void Maximize();
297  void Minimize();
298  void Restore();
299
300  // Transitions to OS fullscreen. See FULLSCREEN_TYPE_OS for more details.
301  void OSFullscreen();
302
303  // Transitions to forced fullscreen. See FULLSCREEN_TYPE_FORCED for more
304  // details.
305  void ForcedFullscreen();
306
307  // Set the minimum and maximum size of the content bounds.
308  void SetContentSizeConstraints(const gfx::Size& min_size,
309                                 const gfx::Size& max_size);
310
311  enum ShowType { SHOW_ACTIVE, SHOW_INACTIVE };
312
313  // Shows the window if its contents have been painted; otherwise flags the
314  // window to be shown as soon as its contents are painted for the first time.
315  void Show(ShowType show_type);
316
317  // Hides the window. If the window was previously flagged to be shown on
318  // first paint, it will be unflagged.
319  void Hide();
320
321  AppWindowContents* app_window_contents_for_test() {
322    return app_window_contents_.get();
323  }
324
325  int fullscreen_types_for_test() {
326    return fullscreen_types_;
327  }
328
329  // Set whether the window should stay above other windows which are not
330  // configured to be always-on-top.
331  void SetAlwaysOnTop(bool always_on_top);
332
333  // Whether the always-on-top property has been set by the chrome.app.window
334  // API. Note that the actual value of this property in the native app window
335  // may be false if the bit is silently switched off for security reasons.
336  bool IsAlwaysOnTop() const;
337
338  // Retrieve the current state of the app window as a dictionary, to pass to
339  // the renderer.
340  void GetSerializedState(base::DictionaryValue* properties) const;
341
342  // Called by the window API when events can be sent to the window for this
343  // app.
344  void WindowEventsReady();
345
346  // Whether the app window wants to be alpha enabled.
347  bool requested_alpha_enabled() const { return requested_alpha_enabled_; }
348
349  void SetAppWindowContentsForTesting(scoped_ptr<AppWindowContents> contents) {
350    app_window_contents_ = contents.Pass();
351  }
352
353 protected:
354  virtual ~AppWindow();
355
356 private:
357  // PlatformAppBrowserTest needs access to web_contents()
358  friend class PlatformAppBrowserTest;
359
360  // content::WebContentsDelegate implementation.
361  virtual void CloseContents(content::WebContents* contents) OVERRIDE;
362  virtual bool ShouldSuppressDialogs() OVERRIDE;
363  virtual content::ColorChooser* OpenColorChooser(
364      content::WebContents* web_contents,
365      SkColor color,
366      const std::vector<content::ColorSuggestion>& suggestions) OVERRIDE;
367  virtual void RunFileChooser(content::WebContents* tab,
368                              const content::FileChooserParams& params)
369      OVERRIDE;
370  virtual bool IsPopupOrPanel(const content::WebContents* source)
371      const OVERRIDE;
372  virtual void MoveContents(content::WebContents* source,
373                            const gfx::Rect& pos) OVERRIDE;
374  virtual void NavigationStateChanged(
375      const content::WebContents* source,
376      content::InvalidateTypes changed_flags) OVERRIDE;
377  virtual void ToggleFullscreenModeForTab(content::WebContents* source,
378                                          bool enter_fullscreen) OVERRIDE;
379  virtual bool IsFullscreenForTabOrPending(const content::WebContents* source)
380      const OVERRIDE;
381  virtual void RequestMediaAccessPermission(
382      content::WebContents* web_contents,
383      const content::MediaStreamRequest& request,
384      const content::MediaResponseCallback& callback) OVERRIDE;
385  virtual bool CheckMediaAccessPermission(
386      content::WebContents* web_contents,
387      const GURL& security_origin,
388      content::MediaStreamType type) OVERRIDE;
389  virtual content::WebContents* OpenURLFromTab(
390      content::WebContents* source,
391      const content::OpenURLParams& params) OVERRIDE;
392  virtual void AddNewContents(content::WebContents* source,
393                              content::WebContents* new_contents,
394                              WindowOpenDisposition disposition,
395                              const gfx::Rect& initial_pos,
396                              bool user_gesture,
397                              bool* was_blocked) OVERRIDE;
398  virtual bool PreHandleKeyboardEvent(
399      content::WebContents* source,
400      const content::NativeWebKeyboardEvent& event,
401      bool* is_keyboard_shortcut) OVERRIDE;
402  virtual void HandleKeyboardEvent(content::WebContents* source,
403                                   const content::NativeWebKeyboardEvent& event)
404      OVERRIDE;
405  virtual void RequestToLockMouse(content::WebContents* web_contents,
406                                  bool user_gesture,
407                                  bool last_unlocked_by_target) OVERRIDE;
408  virtual bool PreHandleGestureEvent(content::WebContents* source,
409                                     const blink::WebGestureEvent& event)
410      OVERRIDE;
411
412  // content::WebContentsObserver implementation.
413  virtual void DidFirstVisuallyNonEmptyPaint() OVERRIDE;
414
415  // content::NotificationObserver implementation.
416  virtual void Observe(int type,
417                       const content::NotificationSource& source,
418                       const content::NotificationDetails& details) OVERRIDE;
419
420  // web_modal::WebContentsModalDialogManagerDelegate implementation.
421  virtual void SetWebContentsBlocked(content::WebContents* web_contents,
422                                     bool blocked) OVERRIDE;
423  virtual bool IsWebContentsVisible(content::WebContents* web_contents)
424      OVERRIDE;
425
426  // Saves the window geometry/position/screen bounds.
427  void SaveWindowPosition();
428
429  // Helper method to adjust the cached bounds so that we can make sure it can
430  // be visible on the screen. See http://crbug.com/145752.
431  void AdjustBoundsToBeVisibleOnScreen(const gfx::Rect& cached_bounds,
432                                       const gfx::Rect& cached_screen_bounds,
433                                       const gfx::Rect& current_screen_bounds,
434                                       const gfx::Size& minimum_size,
435                                       gfx::Rect* bounds) const;
436
437  // Loads the appropriate default or cached window bounds. Returns a new
438  // CreateParams that should be used to create the window.
439  CreateParams LoadDefaults(CreateParams params) const;
440
441  // Load the app's image, firing a load state change when loaded.
442  void UpdateExtensionAppIcon();
443
444  // Set the fullscreen state in the native app window.
445  void SetNativeWindowFullscreen();
446
447  // Returns true if there is any overlap between the window and the taskbar
448  // (Windows only).
449  bool IntersectsWithTaskbar() const;
450
451  // Update the always-on-top bit in the native app window.
452  void UpdateNativeAlwaysOnTop();
453
454  // Sends the onWindowShown event to the app if the window has been shown. Only
455  // has an effect in tests.
456  void SendOnWindowShownIfShown();
457
458  // web_modal::WebContentsModalDialogManagerDelegate implementation.
459  virtual web_modal::WebContentsModalDialogHost* GetWebContentsModalDialogHost()
460      OVERRIDE;
461
462  // Updates the badge to |image|. Called internally from the image loader
463  // callback.
464  void UpdateBadgeIcon(const gfx::Image& image);
465
466  // Callback from web_contents()->DownloadFavicon.
467  void DidDownloadFavicon(int id,
468                          int http_status_code,
469                          const GURL& image_url,
470                          const std::vector<SkBitmap>& bitmaps,
471                          const std::vector<gfx::Size>& original_bitmap_sizes);
472
473  // IconImage::Observer implementation.
474  virtual void OnExtensionIconImageChanged(IconImage* image) OVERRIDE;
475
476  // The browser context with which this window is associated. AppWindow does
477  // not own this object.
478  content::BrowserContext* browser_context_;
479
480  const std::string extension_id_;
481
482  // Identifier that is used when saving and restoring geometry for this
483  // window.
484  std::string window_key_;
485
486  const SessionID session_id_;
487  WindowType window_type_;
488  content::NotificationRegistrar registrar_;
489
490  // Icon shown in the task bar.
491  gfx::Image app_icon_;
492
493  // Icon URL to be used for setting the app icon. If not empty, app_icon_ will
494  // be fetched and set using this URL.
495  GURL app_icon_url_;
496
497  // An object to load the app's icon as an extension resource.
498  scoped_ptr<IconImage> app_icon_image_;
499
500  // Badge for icon shown in the task bar.
501  gfx::Image badge_icon_;
502
503  // URL to be used for setting the badge on the app icon.
504  GURL badge_icon_url_;
505
506  // An object to load the badge as an extension resource.
507  scoped_ptr<IconImage> badge_icon_image_;
508
509  scoped_ptr<NativeAppWindow> native_app_window_;
510  scoped_ptr<AppWindowContents> app_window_contents_;
511  scoped_ptr<AppDelegate> app_delegate_;
512  scoped_ptr<AppWebContentsHelper> helper_;
513
514  // Manages popup windows (bubbles, tab-modals) visible overlapping the
515  // app window.
516  scoped_ptr<web_modal::PopupManager> popup_manager_;
517
518  base::WeakPtrFactory<AppWindow> image_loader_ptr_factory_;
519
520  // Bit field of FullscreenType.
521  int fullscreen_types_;
522
523  // Show has been called, so the window should be shown once the first visually
524  // non-empty paint occurs.
525  bool show_on_first_paint_;
526
527  // The first visually non-empty paint has completed.
528  bool first_paint_complete_;
529
530  // Whether the window has been shown or not.
531  bool has_been_shown_;
532
533  // Whether events can be sent to the window.
534  bool can_send_events_;
535
536  // Whether the window is hidden or not. Hidden in this context means actively
537  // by the chrome.app.window API, not in an operating system context. For
538  // example windows which are minimized are not hidden, and windows which are
539  // part of a hidden app on OS X are not hidden. Windows which were created
540  // with the |hidden| flag set to true, or which have been programmatically
541  // hidden, are considered hidden.
542  bool is_hidden_;
543
544  // Whether the delayed Show() call was for an active or inactive window.
545  ShowType delayed_show_type_;
546
547  // Cache the desired value of the always-on-top property. When windows enter
548  // fullscreen or overlap the Windows taskbar, this property will be
549  // automatically and silently switched off for security reasons. It is
550  // reinstated when the window exits fullscreen and moves away from the
551  // taskbar.
552  bool cached_always_on_top_;
553
554  // Whether |alpha_enabled| was set in the CreateParams.
555  bool requested_alpha_enabled_;
556
557  DISALLOW_COPY_AND_ASSIGN(AppWindow);
558};
559
560}  // namespace extensions
561
562#endif  // EXTENSIONS_BROWSER_APP_WINDOW_APP_WINDOW_H_
563