summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortmdiep@chromium.org <tmdiep@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-11 04:12:46 +0000
committertmdiep@chromium.org <tmdiep@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-11 04:12:46 +0000
commit1dd77182636791cba0143c41c025a3f669524976 (patch)
tree3813636a5c3807f170b05984a954088a9a4db1a8
parentfd0acadd8ef82eae537c8de7333f5e9bb80907c2 (diff)
downloadchromium_src-1dd77182636791cba0143c41c025a3f669524976.zip
chromium_src-1dd77182636791cba0143c41c025a3f669524976.tar.gz
chromium_src-1dd77182636791cba0143c41c025a3f669524976.tar.bz2
Create windows for new app window bounds API
Added create options "innerBounds" and "outerBounds" for the new app window bounds API. BUG=315471, 349397 TEST=browser_tests (AppWindowAPITest.*) Review URL: https://codereview.chromium.org/186343002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@256125 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--apps/app_window.cc180
-rw-r--r--apps/app_window.h60
-rw-r--r--apps/size_constraints.cc24
-rw-r--r--apps/size_constraints.h8
-rw-r--r--apps/ui/native_app_window.h8
-rw-r--r--apps/ui/views/native_app_window_views.cc13
-rw-r--r--apps/ui/views/native_app_window_views.h8
-rw-r--r--chrome/browser/apps/app_window_browsertest.cc47
-rw-r--r--chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.cc35
-rw-r--r--chrome/browser/extensions/api/app_window/app_window_api.cc209
-rw-r--r--chrome/browser/extensions/api/app_window/app_window_api.h7
-rw-r--r--chrome/browser/extensions/api/tabs/tabs_api.cc2
-rw-r--r--chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h14
-rw-r--r--chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm130
-rw-r--r--chrome/browser/ui/gtk/apps/native_app_window_gtk.cc42
-rw-r--r--chrome/browser/ui/gtk/apps/native_app_window_gtk.h10
-rw-r--r--chrome/browser/ui/views/apps/chrome_native_app_window_views.cc38
-rw-r--r--chrome/common/extensions/api/app_window.idl72
-rw-r--r--chrome/renderer/resources/extensions/app_window_custom_bindings.js10
-rw-r--r--chrome/test/data/extensions/platform_apps/window_api/test.js554
20 files changed, 1168 insertions, 303 deletions
diff --git a/apps/app_window.cc b/apps/app_window.cc
index 8bb2b7e..e6f5a5e 100644
--- a/apps/app_window.cc
+++ b/apps/app_window.cc
@@ -4,6 +4,8 @@
#include "apps/app_window.h"
+#include <algorithm>
+
#include "apps/app_window_geometry_cache.h"
#include "apps/app_window_registry.h"
#include "apps/apps_client.h"
@@ -95,14 +97,62 @@ void SetBoundsProperties(const gfx::Rect& bounds,
window_properties->Set(bounds_name, bounds_properties.release());
}
+// Combines the constraints of the content and window, and returns constraints
+// for the window.
+gfx::Size GetCombinedWindowConstraints(const gfx::Size& window_constraints,
+ const gfx::Size& content_constraints,
+ const gfx::Insets& frame_insets) {
+ gfx::Size combined_constraints(window_constraints);
+ if (content_constraints.width() > 0) {
+ combined_constraints.set_width(
+ content_constraints.width() + frame_insets.width());
+ }
+ if (content_constraints.height() > 0) {
+ combined_constraints.set_height(
+ content_constraints.height() + frame_insets.height());
+ }
+ return combined_constraints;
+}
+
+// Combines the constraints of the content and window, and returns constraints
+// for the content.
+gfx::Size GetCombinedContentConstraints(const gfx::Size& window_constraints,
+ const gfx::Size& content_constraints,
+ const gfx::Insets& frame_insets) {
+ gfx::Size combined_constraints(content_constraints);
+ if (window_constraints.width() > 0) {
+ combined_constraints.set_width(
+ std::max(0, window_constraints.width() - frame_insets.width()));
+ }
+ if (window_constraints.height() > 0) {
+ combined_constraints.set_height(
+ std::max(0, window_constraints.height() - frame_insets.height()));
+ }
+ return combined_constraints;
+}
+
} // namespace
+// AppWindow::BoundsSpecification
+
+const int AppWindow::BoundsSpecification::kUnspecifiedPosition = INT_MIN;
+
+AppWindow::BoundsSpecification::BoundsSpecification()
+ : bounds(kUnspecifiedPosition, kUnspecifiedPosition, 0, 0) {}
+
+AppWindow::BoundsSpecification::~BoundsSpecification() {}
+
+void AppWindow::BoundsSpecification::ResetBounds() {
+ bounds.SetRect(kUnspecifiedPosition, kUnspecifiedPosition, 0, 0);
+}
+
+// AppWindow::CreateParams
+
AppWindow::CreateParams::CreateParams()
: window_type(AppWindow::WINDOW_TYPE_DEFAULT),
frame(AppWindow::FRAME_CHROME),
has_frame_color(false),
transparent_background(false),
- bounds(INT_MIN, INT_MIN, 0, 0),
creator_process_id(0),
state(ui::SHOW_STATE_DEFAULT),
hidden(false),
@@ -112,8 +162,68 @@ AppWindow::CreateParams::CreateParams()
AppWindow::CreateParams::~CreateParams() {}
+gfx::Rect AppWindow::CreateParams::GetInitialWindowBounds(
+ const gfx::Insets& frame_insets) const {
+ // Combine into a single window bounds.
+ gfx::Rect combined_bounds(window_spec.bounds);
+ if (content_spec.bounds.x() != BoundsSpecification::kUnspecifiedPosition)
+ combined_bounds.set_x(content_spec.bounds.x() - frame_insets.left());
+ if (content_spec.bounds.y() != BoundsSpecification::kUnspecifiedPosition)
+ combined_bounds.set_y(content_spec.bounds.y() - frame_insets.top());
+ if (content_spec.bounds.width() > 0) {
+ combined_bounds.set_width(
+ content_spec.bounds.width() + frame_insets.width());
+ }
+ if (content_spec.bounds.height() > 0) {
+ combined_bounds.set_height(
+ content_spec.bounds.height() + frame_insets.height());
+ }
+
+ // Constrain the bounds.
+ SizeConstraints constraints(
+ GetCombinedWindowConstraints(
+ window_spec.minimum_size, content_spec.minimum_size, frame_insets),
+ GetCombinedWindowConstraints(
+ window_spec.maximum_size, content_spec.maximum_size, frame_insets));
+ combined_bounds.set_size(constraints.ClampSize(combined_bounds.size()));
+
+ return combined_bounds;
+}
+
+gfx::Size AppWindow::CreateParams::GetContentMinimumSize(
+ const gfx::Insets& frame_insets) const {
+ return GetCombinedContentConstraints(window_spec.minimum_size,
+ content_spec.minimum_size,
+ frame_insets);
+}
+
+gfx::Size AppWindow::CreateParams::GetContentMaximumSize(
+ const gfx::Insets& frame_insets) const {
+ return GetCombinedContentConstraints(window_spec.maximum_size,
+ content_spec.maximum_size,
+ frame_insets);
+}
+
+gfx::Size AppWindow::CreateParams::GetWindowMinimumSize(
+ const gfx::Insets& frame_insets) const {
+ return GetCombinedWindowConstraints(window_spec.minimum_size,
+ content_spec.minimum_size,
+ frame_insets);
+}
+
+gfx::Size AppWindow::CreateParams::GetWindowMaximumSize(
+ const gfx::Insets& frame_insets) const {
+ return GetCombinedWindowConstraints(window_spec.maximum_size,
+ content_spec.maximum_size,
+ frame_insets);
+}
+
+// AppWindow::Delegate
+
AppWindow::Delegate::~Delegate() {}
+// AppWindow
+
AppWindow::AppWindow(BrowserContext* context,
Delegate* delegate,
const extensions::Extension* extension)
@@ -154,7 +264,7 @@ void AppWindow::Init(const GURL& url,
extensions::SetViewType(web_contents, extensions::VIEW_TYPE_APP_WINDOW);
// Initialize the window
- CreateParams new_params = LoadDefaultsAndConstrain(params);
+ CreateParams new_params = LoadDefaults(params);
window_type_ = new_params.window_type;
window_key_ = new_params.window_key;
@@ -208,7 +318,10 @@ void AppWindow::Init(const GURL& url,
// that to happen, we need to define a size for the content, otherwise the
// layout will happen in a 0x0 area.
// Note: WebContents::GetView() is guaranteed to be non-null.
- web_contents->GetView()->SizeContents(new_params.bounds.size());
+ gfx::Insets frame_insets = native_app_window_->GetFrameInsets();
+ gfx::Rect initial_bounds = new_params.GetInitialWindowBounds(frame_insets);
+ initial_bounds.Inset(frame_insets);
+ web_contents->GetView()->SizeContents(initial_bounds.size());
}
// Prevent the browser process from shutting down while this window is open.
@@ -519,13 +632,13 @@ void AppWindow::ForcedFullscreen() {
SetNativeWindowFullscreen();
}
-void AppWindow::SetMinimumSize(const gfx::Size& min_size) {
- native_app_window_->SetMinimumSize(min_size);
+void AppWindow::SetContentMinimumSize(const gfx::Size& min_size) {
+ native_app_window_->SetContentMinimumSize(min_size);
OnSizeConstraintsChanged();
}
-void AppWindow::SetMaximumSize(const gfx::Size& max_size) {
- native_app_window_->SetMaximumSize(max_size);
+void AppWindow::SetContentMaximumSize(const gfx::Size& max_size) {
+ native_app_window_->SetContentMaximumSize(max_size);
OnSizeConstraintsChanged();
}
@@ -588,17 +701,23 @@ void AppWindow::GetSerializedState(base::DictionaryValue* properties) const {
properties->SetInteger("frameColor", native_app_window_->FrameColor());
gfx::Rect content_bounds = GetClientBounds();
+ gfx::Size content_min_size = native_app_window_->GetContentMinimumSize();
+ gfx::Size content_max_size = native_app_window_->GetContentMaximumSize();
SetBoundsProperties(content_bounds,
- native_app_window_->GetMinimumSize(),
- native_app_window_->GetMaximumSize(),
+ content_min_size,
+ content_max_size,
"innerBounds",
properties);
- // TODO(tmdiep): Frame constraints will be implemented in a future patch.
+ gfx::Insets frame_insets = native_app_window_->GetFrameInsets();
gfx::Rect frame_bounds = native_app_window_->GetBounds();
+ gfx::Size frame_min_size =
+ SizeConstraints::AddFrameToConstraints(content_min_size, frame_insets);
+ gfx::Size frame_max_size =
+ SizeConstraints::AddFrameToConstraints(content_max_size, frame_insets);
SetBoundsProperties(frame_bounds,
- gfx::Size(),
- gfx::Size(),
+ frame_min_size,
+ frame_max_size,
"outerBounds",
properties);
}
@@ -663,12 +782,13 @@ void AppWindow::UpdateExtensionAppIcon() {
}
void AppWindow::OnSizeConstraintsChanged() {
- SizeConstraints size_constraints(native_app_window_->GetMinimumSize(),
- native_app_window_->GetMaximumSize());
+ SizeConstraints size_constraints(native_app_window_->GetContentMinimumSize(),
+ native_app_window_->GetContentMaximumSize());
gfx::Rect bounds = GetClientBounds();
gfx::Size constrained_size = size_constraints.ClampSize(bounds.size());
if (bounds.size() != constrained_size) {
bounds.set_size(constrained_size);
+ bounds.Inset(-native_app_window_->GetFrameInsets());
native_app_window_->SetBounds(bounds);
}
OnNativeWindowChanged();
@@ -852,7 +972,6 @@ void AppWindow::SaveWindowPosition() {
AppWindowGeometryCache::Get(browser_context());
gfx::Rect bounds = native_app_window_->GetRestoredBounds();
- bounds.Inset(native_app_window_->GetFrameInsets());
gfx::Rect screen_bounds =
gfx::Screen::GetNativeScreen()->GetDisplayMatching(bounds).work_area();
ui::WindowShowState window_state = native_app_window_->GetRestoredState();
@@ -890,12 +1009,17 @@ void AppWindow::AdjustBoundsToBeVisibleOnScreen(
}
}
-AppWindow::CreateParams AppWindow::LoadDefaultsAndConstrain(CreateParams params)
+AppWindow::CreateParams AppWindow::LoadDefaults(CreateParams params)
const {
- if (params.bounds.width() == 0)
- params.bounds.set_width(kDefaultWidth);
- if (params.bounds.height() == 0)
- params.bounds.set_height(kDefaultHeight);
+ // Ensure width and height are specified.
+ if (params.content_spec.bounds.width() == 0 &&
+ params.window_spec.bounds.width() == 0) {
+ params.content_spec.bounds.set_width(kDefaultWidth);
+ }
+ if (params.content_spec.bounds.height() == 0 &&
+ params.window_spec.bounds.height() == 0) {
+ params.content_spec.bounds.set_height(kDefaultHeight);
+ }
// If left and top are left undefined, the native app window will center
// the window on the main screen in a platform-defined manner.
@@ -913,25 +1037,27 @@ AppWindow::CreateParams AppWindow::LoadDefaultsAndConstrain(CreateParams params)
&cached_bounds,
&cached_screen_bounds,
&cached_state)) {
+
// App window has cached screen bounds, make sure it fits on screen in
// case the screen resolution changed.
gfx::Screen* screen = gfx::Screen::GetNativeScreen();
gfx::Display display = screen->GetDisplayMatching(cached_bounds);
gfx::Rect current_screen_bounds = display.work_area();
+ SizeConstraints constraints(params.GetWindowMinimumSize(gfx::Insets()),
+ params.GetWindowMaximumSize(gfx::Insets()));
AdjustBoundsToBeVisibleOnScreen(cached_bounds,
cached_screen_bounds,
current_screen_bounds,
- params.minimum_size,
- &params.bounds);
+ constraints.GetMinimumSize(),
+ &params.window_spec.bounds);
params.state = cached_state;
+
+ // Since we are restoring a cached state, reset the content bounds spec to
+ // ensure it is not used.
+ params.content_spec.ResetBounds();
}
}
- SizeConstraints size_constraints(params.minimum_size, params.maximum_size);
- params.bounds.set_size(size_constraints.ClampSize(params.bounds.size()));
- params.minimum_size = size_constraints.GetMinimumSize();
- params.maximum_size = size_constraints.GetMaximumSize();
-
return params;
}
diff --git a/apps/app_window.h b/apps/app_window.h
index 2614a40..9729484 100644
--- a/apps/app_window.h
+++ b/apps/app_window.h
@@ -114,6 +114,26 @@ class AppWindow : public content::NotificationObserver,
FULLSCREEN_TYPE_FORCED = 1 << 3,
};
+ struct BoundsSpecification {
+ // INT_MIN represents an unspecified position component.
+ static const int kUnspecifiedPosition;
+
+ BoundsSpecification();
+ ~BoundsSpecification();
+
+ // INT_MIN designates 'unspecified' for the position components, and 0
+ // designates 'unspecified' for the size components. When unspecified,
+ // they should be replaced with a default value.
+ gfx::Rect bounds;
+
+ gfx::Size minimum_size;
+ gfx::Size maximum_size;
+
+ // Reset the bounds fields to their 'unspecified' values. The minimum and
+ // maximum size constraints remain unchanged.
+ void ResetBounds();
+ };
+
struct CreateParams {
CreateParams();
~CreateParams();
@@ -125,14 +145,13 @@ class AppWindow : public content::NotificationObserver,
SkColor frame_color;
bool transparent_background; // Only supported on ash.
- // Specify the initial content bounds of the window (excluding any window
- // decorations). INT_MIN designates 'unspecified' for the position
- // components, and 0 for the size components. When unspecified, they should
- // be replaced with a default value.
- gfx::Rect bounds;
+ // The initial content/inner bounds specification (excluding any window
+ // decorations).
+ BoundsSpecification content_spec;
- gfx::Size minimum_size;
- gfx::Size maximum_size;
+ // The initial window/outer bounds specification (including window
+ // decorations).
+ BoundsSpecification window_spec;
std::string window_key;
@@ -154,6 +173,18 @@ class AppWindow : public content::NotificationObserver,
// If true, the window will stay on top of other windows that are not
// configured to be always on top. Defaults to false.
bool always_on_top;
+
+ // The API enables developers to specify content or window bounds. This
+ // function combines them into a single, constrained window size.
+ gfx::Rect GetInitialWindowBounds(const gfx::Insets& frame_insets) const;
+
+ // The API enables developers to specify content or window size constraints.
+ // These functions combine them so that we can work with one set of
+ // constraints.
+ gfx::Size GetContentMinimumSize(const gfx::Insets& frame_insets) const;
+ gfx::Size GetContentMaximumSize(const gfx::Insets& frame_insets) const;
+ gfx::Size GetWindowMinimumSize(const gfx::Insets& frame_insets) const;
+ gfx::Size GetWindowMaximumSize(const gfx::Insets& frame_insets) const;
};
class Delegate {
@@ -287,9 +318,9 @@ class AppWindow : public content::NotificationObserver,
// details.
void ForcedFullscreen();
- // Set the minimum and maximum size that this window is allowed to be.
- void SetMinimumSize(const gfx::Size& min_size);
- void SetMaximumSize(const gfx::Size& max_size);
+ // Set the minimum and maximum size of the content bounds.
+ void SetContentMinimumSize(const gfx::Size& min_size);
+ void SetContentMaximumSize(const gfx::Size& max_size);
enum ShowType { SHOW_ACTIVE, SHOW_INACTIVE };
@@ -394,17 +425,16 @@ class AppWindow : public content::NotificationObserver,
void SaveWindowPosition();
// Helper method to adjust the cached bounds so that we can make sure it can
- // be visible on the screen. See http://crbug.com/145752 .
+ // be visible on the screen. See http://crbug.com/145752.
void AdjustBoundsToBeVisibleOnScreen(const gfx::Rect& cached_bounds,
const gfx::Rect& cached_screen_bounds,
const gfx::Rect& current_screen_bounds,
const gfx::Size& minimum_size,
gfx::Rect* bounds) const;
- // Loads the appropriate default or cached window bounds and constrains them
- // based on screen size and minimum/maximum size. Returns a new CreateParams
- // that should be used to create the window.
- CreateParams LoadDefaultsAndConstrain(CreateParams params) const;
+ // Loads the appropriate default or cached window bounds. Returns a new
+ // CreateParams that should be used to create the window.
+ CreateParams LoadDefaults(CreateParams params) const;
// Load the app's image, firing a load state change when loaded.
void UpdateExtensionAppIcon();
diff --git a/apps/size_constraints.cc b/apps/size_constraints.cc
index bf7729a..4686965 100644
--- a/apps/size_constraints.cc
+++ b/apps/size_constraints.cc
@@ -6,6 +6,8 @@
#include <algorithm>
+#include "ui/gfx/insets.h"
+
namespace apps {
SizeConstraints::SizeConstraints()
@@ -17,19 +19,33 @@ SizeConstraints::SizeConstraints(const gfx::Size& min_size,
SizeConstraints::~SizeConstraints() {}
+// static
+gfx::Size SizeConstraints::AddFrameToConstraints(
+ const gfx::Size& size_constraints,
+ const gfx::Insets& frame_insets) {
+ return gfx::Size(
+ size_constraints.width() == kUnboundedSize
+ ? kUnboundedSize
+ : size_constraints.width() + frame_insets.width(),
+ size_constraints.height() == kUnboundedSize
+ ? kUnboundedSize
+ : size_constraints.height() + frame_insets.height());
+}
+
gfx::Size SizeConstraints::ClampSize(gfx::Size size) const {
const gfx::Size max_size = GetMaximumSize();
if (max_size.width() != kUnboundedSize)
- size.set_width(std::min(size.width(), GetMaximumSize().width()));
+ size.set_width(std::min(size.width(), max_size.width()));
if (max_size.height() != kUnboundedSize)
- size.set_height(std::min(size.height(), GetMaximumSize().height()));
+ size.set_height(std::min(size.height(), max_size.height()));
size.SetToMax(GetMinimumSize());
return size;
}
bool SizeConstraints::HasMinimumSize() const {
- return GetMinimumSize().width() != kUnboundedSize ||
- GetMinimumSize().height() != kUnboundedSize;
+ const gfx::Size min_size = GetMinimumSize();
+ return min_size.width() != kUnboundedSize ||
+ min_size.height() != kUnboundedSize;
}
bool SizeConstraints::HasMaximumSize() const {
diff --git a/apps/size_constraints.h b/apps/size_constraints.h
index 1af2692..353037b 100644
--- a/apps/size_constraints.h
+++ b/apps/size_constraints.h
@@ -7,6 +7,10 @@
#include "ui/gfx/geometry/size.h"
+namespace gfx {
+class Insets;
+}
+
namespace apps {
class SizeConstraints {
@@ -19,6 +23,10 @@ class SizeConstraints {
SizeConstraints(const gfx::Size& min_size, const gfx::Size& max_size);
~SizeConstraints();
+ // Adds frame insets to a size constraint.
+ static gfx::Size AddFrameToConstraints(const gfx::Size& size_constraints,
+ const gfx::Insets& frame_insets);
+
// Returns the bounds with its size clamped to the min/max size.
gfx::Size ClampSize(gfx::Size size) const;
diff --git a/apps/ui/native_app_window.h b/apps/ui/native_app_window.h
index 0093a3c..12b08d5 100644
--- a/apps/ui/native_app_window.h
+++ b/apps/ui/native_app_window.h
@@ -77,16 +77,16 @@ class NativeAppWindow : public ui::BaseWindow,
virtual void UpdateShelfMenu() = 0;
// Returns the minimum size constraints of the content.
- virtual gfx::Size GetMinimumSize() const = 0;
+ virtual gfx::Size GetContentMinimumSize() const = 0;
// Updates the minimum size constraints of the content.
- virtual void SetMinimumSize(const gfx::Size& size) = 0;
+ virtual void SetContentMinimumSize(const gfx::Size& size) = 0;
// Returns the maximum size constraints of the content.
- virtual gfx::Size GetMaximumSize() const = 0;
+ virtual gfx::Size GetContentMaximumSize() const = 0;
// Updates the maximum size constraints of the content.
- virtual void SetMaximumSize(const gfx::Size& size) = 0;
+ virtual void SetContentMaximumSize(const gfx::Size& size) = 0;
virtual ~NativeAppWindow() {}
};
diff --git a/apps/ui/views/native_app_window_views.cc b/apps/ui/views/native_app_window_views.cc
index 070851e..90a63af 100644
--- a/apps/ui/views/native_app_window_views.cc
+++ b/apps/ui/views/native_app_window_views.cc
@@ -37,8 +37,6 @@ void NativeAppWindowViews::Init(AppWindow* app_window,
frameless_ = create_params.frame == AppWindow::FRAME_NONE;
transparent_background_ = create_params.transparent_background;
resizable_ = create_params.resizable;
- size_constraints_.set_minimum_size(create_params.minimum_size);
- size_constraints_.set_maximum_size(create_params.maximum_size);
Observe(app_window_->web_contents());
window_ = new views::Widget;
@@ -61,7 +59,8 @@ void NativeAppWindowViews::InitializeWindow(
init_params.top_level = true;
init_params.keep_on_top = create_params.always_on_top;
window_->Init(init_params);
- window_->CenterWindow(create_params.bounds.size());
+ window_->CenterWindow(
+ create_params.GetInitialWindowBounds(gfx::Insets()).size());
}
// ui::BaseWindow implementation.
@@ -408,19 +407,19 @@ void NativeAppWindowViews::ShowWithApp() {}
void NativeAppWindowViews::UpdateShelfMenu() {}
-gfx::Size NativeAppWindowViews::GetMinimumSize() const {
+gfx::Size NativeAppWindowViews::GetContentMinimumSize() const {
return size_constraints_.GetMinimumSize();
}
-void NativeAppWindowViews::SetMinimumSize(const gfx::Size& size) {
+void NativeAppWindowViews::SetContentMinimumSize(const gfx::Size& size) {
size_constraints_.set_minimum_size(size);
}
-gfx::Size NativeAppWindowViews::GetMaximumSize() const {
+gfx::Size NativeAppWindowViews::GetContentMaximumSize() const {
return size_constraints_.GetMaximumSize();
}
-void NativeAppWindowViews::SetMaximumSize(const gfx::Size& size) {
+void NativeAppWindowViews::SetContentMaximumSize(const gfx::Size& size) {
size_constraints_.set_maximum_size(size);
}
diff --git a/apps/ui/views/native_app_window_views.h b/apps/ui/views/native_app_window_views.h
index d8147f3..f22cea9 100644
--- a/apps/ui/views/native_app_window_views.h
+++ b/apps/ui/views/native_app_window_views.h
@@ -150,10 +150,10 @@ class NativeAppWindowViews : public NativeAppWindow,
virtual void HideWithApp() OVERRIDE;
virtual void ShowWithApp() OVERRIDE;
virtual void UpdateShelfMenu() OVERRIDE;
- virtual gfx::Size GetMinimumSize() const OVERRIDE;
- virtual void SetMinimumSize(const gfx::Size& size) OVERRIDE;
- virtual gfx::Size GetMaximumSize() const OVERRIDE;
- virtual void SetMaximumSize(const gfx::Size& size) OVERRIDE;
+ virtual gfx::Size GetContentMinimumSize() const OVERRIDE;
+ virtual void SetContentMinimumSize(const gfx::Size& size) OVERRIDE;
+ virtual gfx::Size GetContentMaximumSize() const OVERRIDE;
+ virtual void SetContentMaximumSize(const gfx::Size& size) OVERRIDE;
// web_modal::WebContentsModalDialogHost implementation.
virtual gfx::NativeView GetHostView() const OVERRIDE;
diff --git a/chrome/browser/apps/app_window_browsertest.cc b/chrome/browser/apps/app_window_browsertest.cc
index dc77305..d11505a 100644
--- a/chrome/browser/apps/app_window_browsertest.cc
+++ b/chrome/browser/apps/app_window_browsertest.cc
@@ -126,16 +126,53 @@ class AppWindowAPITest : public extensions::PlatformAppBrowserTest {
// These tests are flaky after https://codereview.chromium.org/57433010/.
// See http://crbug.com/319613.
-IN_PROC_BROWSER_TEST_F(AppWindowAPITest, DISABLED_TestCreate) {
+IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestCreate) {
ASSERT_TRUE(RunAppWindowAPITest("testCreate")) << message_;
}
-IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestSingleton) {
- ASSERT_TRUE(RunAppWindowAPITest("testSingleton")) << message_;
+#if defined(TOOLKIT_GTK)
+#define MAYBE_TestDeprecatedBounds DISABLED_TestDeprecatedBounds
+#else
+#define MAYBE_TestDeprecatedBounds TestDeprecatedBounds
+#endif
+
+IN_PROC_BROWSER_TEST_F(AppWindowAPITest, MAYBE_TestDeprecatedBounds) {
+ ASSERT_TRUE(RunAppWindowAPITest("testDeprecatedBounds")) << message_;
+}
+
+#if defined(TOOLKIT_GTK)
+#define MAYBE_TestInitialBounds DISABLED_TestInitialBounds
+#else
+#define MAYBE_TestInitialBounds TestInitialBounds
+#endif
+
+IN_PROC_BROWSER_TEST_F(AppWindowAPITest, MAYBE_TestInitialBounds) {
+ ASSERT_TRUE(RunAppWindowAPITest("testInitialBounds")) << message_;
}
-IN_PROC_BROWSER_TEST_F(AppWindowAPITest, DISABLED_TestBounds) {
- ASSERT_TRUE(RunAppWindowAPITest("testBounds")) << message_;
+#if defined(TOOLKIT_GTK)
+#define MAYBE_TestInitialBoundsInStable DISABLED_TestInitialBoundsInStable
+#else
+#define MAYBE_TestInitialBoundsInStable TestInitialBoundsInStable
+#endif
+
+IN_PROC_BROWSER_TEST_F(AppWindowAPITest, MAYBE_TestInitialBoundsInStable) {
+ extensions::ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_STABLE);
+ ASSERT_TRUE(RunAppWindowAPITest("testInitialBoundsInStable")) << message_;
+}
+
+#if defined(TOOLKIT_GTK)
+#define MAYBE_TestInitialConstraints DISABLED_TestInitialConstraints
+#else
+#define MAYBE_TestInitialConstraints TestInitialConstraints
+#endif
+
+IN_PROC_BROWSER_TEST_F(AppWindowAPITest, MAYBE_TestInitialConstraints) {
+ ASSERT_TRUE(RunAppWindowAPITest("testInitialConstraints")) << message_;
+}
+
+IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestSingleton) {
+ ASSERT_TRUE(RunAppWindowAPITest("testSingleton")) << message_;
}
IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestCloseEvent) {
diff --git a/chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.cc b/chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.cc
index ebba22d..03b7d71 100644
--- a/chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.cc
+++ b/chrome/browser/extensions/api/app_current_window_internal/app_current_window_internal_api.cc
@@ -133,20 +133,25 @@ bool AppCurrentWindowInternalSetBoundsFunction::RunWithWindow(
AppWindow* window) {
// Start with the current bounds, and change any values that are specified in
// the incoming parameters.
- gfx::Rect bounds = window->GetClientBounds();
+ gfx::Rect window_bounds = window->GetBaseWindow()->GetBounds();
+ gfx::Rect content_bounds = window->GetClientBounds();
+
+ // We need to maintain backcompatibility with a bug on Windows and ChromeOS,
+ // which returns the position of the window but the size of the content.
scoped_ptr<SetBounds::Params> params(SetBounds::Params::Create(*args_));
CHECK(params.get());
if (params->bounds.left)
- bounds.set_x(*(params->bounds.left));
+ window_bounds.set_x(*(params->bounds.left));
if (params->bounds.top)
- bounds.set_y(*(params->bounds.top));
+ window_bounds.set_y(*(params->bounds.top));
if (params->bounds.width)
- bounds.set_width(*(params->bounds.width));
+ content_bounds.set_width(*(params->bounds.width));
if (params->bounds.height)
- bounds.set_height(*(params->bounds.height));
+ content_bounds.set_height(*(params->bounds.height));
- bounds.Inset(-window->GetBaseWindow()->GetFrameInsets());
- window->GetBaseWindow()->SetBounds(bounds);
+ content_bounds.Inset(-window->GetBaseWindow()->GetFrameInsets());
+ window_bounds.set_size(content_bounds.size());
+ window->GetBaseWindow()->SetBounds(window_bounds);
return true;
}
@@ -159,10 +164,10 @@ bool AppCurrentWindowInternalSetMinWidthFunction::RunWithWindow(
scoped_ptr<SetMinWidth::Params> params(SetMinWidth::Params::Create(*args_));
CHECK(params.get());
- gfx::Size min_size = window->GetBaseWindow()->GetMinimumSize();
+ gfx::Size min_size = window->GetBaseWindow()->GetContentMinimumSize();
min_size.set_width(params->min_width.get() ?
*(params->min_width) : kUnboundedSize);
- window->SetMinimumSize(min_size);
+ window->SetContentMinimumSize(min_size);
return true;
}
@@ -175,10 +180,10 @@ bool AppCurrentWindowInternalSetMinHeightFunction::RunWithWindow(
scoped_ptr<SetMinHeight::Params> params(SetMinHeight::Params::Create(*args_));
CHECK(params.get());
- gfx::Size min_size = window->GetBaseWindow()->GetMinimumSize();
+ gfx::Size min_size = window->GetBaseWindow()->GetContentMinimumSize();
min_size.set_height(params->min_height.get() ?
*(params->min_height) : kUnboundedSize);
- window->SetMinimumSize(min_size);
+ window->SetContentMinimumSize(min_size);
return true;
}
@@ -191,10 +196,10 @@ bool AppCurrentWindowInternalSetMaxWidthFunction::RunWithWindow(
scoped_ptr<SetMaxWidth::Params> params(SetMaxWidth::Params::Create(*args_));
CHECK(params.get());
- gfx::Size max_size = window->GetBaseWindow()->GetMaximumSize();
+ gfx::Size max_size = window->GetBaseWindow()->GetContentMaximumSize();
max_size.set_width(params->max_width.get() ?
*(params->max_width) : kUnboundedSize);
- window->SetMaximumSize(max_size);
+ window->SetContentMaximumSize(max_size);
return true;
}
@@ -207,10 +212,10 @@ bool AppCurrentWindowInternalSetMaxHeightFunction::RunWithWindow(
scoped_ptr<SetMaxHeight::Params> params(SetMaxHeight::Params::Create(*args_));
CHECK(params.get());
- gfx::Size max_size = window->GetBaseWindow()->GetMaximumSize();
+ gfx::Size max_size = window->GetBaseWindow()->GetContentMaximumSize();
max_size.set_height(params->max_height.get() ?
*(params->max_height) : kUnboundedSize);
- window->SetMaximumSize(max_size);
+ window->SetContentMaximumSize(max_size);
return true;
}
diff --git a/chrome/browser/extensions/api/app_window/app_window_api.cc b/chrome/browser/extensions/api/app_window/app_window_api.cc
index 856d82d..1fe6cd1 100644
--- a/chrome/browser/extensions/api/app_window/app_window_api.cc
+++ b/chrome/browser/extensions/api/app_window/app_window_api.cc
@@ -11,6 +11,7 @@
#include "apps/ui/native_app_window.h"
#include "base/command_line.h"
#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/app_mode/app_mode_utils.h"
@@ -56,6 +57,10 @@ const char kInvalidChannelForFrameOptions[] =
const char kFrameOptionsAndFrame[] =
"Only one of frame and frameOptions can be supplied.";
const char kColorWithFrameNone[] = "Windows with no frame cannot have a color.";
+const char kInvalidChannelForBounds[] =
+ "innerBounds and outerBounds are only available in dev channel.";
+const char kConflictingBoundsOptions[] =
+ "The $1 property cannot be specified for both inner and outer bounds.";
} // namespace app_window_constants
const char kNoneFrameOption[] = "none";
@@ -93,6 +98,49 @@ class DevToolsRestorer : public base::RefCounted<DevToolsRestorer> {
scoped_refptr<AppWindowCreateFunction> delayed_create_function_;
};
+// If the same property is specified for the inner and outer bounds, raise an
+// error.
+bool CheckBoundsConflict(const scoped_ptr<int>& inner_property,
+ const scoped_ptr<int>& outer_property,
+ const std::string& property_name,
+ std::string* error) {
+ if (inner_property.get() && outer_property.get()) {
+ std::vector<std::string> subst;
+ subst.push_back(property_name);
+ *error = ReplaceStringPlaceholders(
+ app_window_constants::kConflictingBoundsOptions, subst, NULL);
+ return false;
+ }
+
+ return true;
+}
+
+// Copy over the bounds specification properties from the API to the
+// AppWindow::CreateParams.
+void CopyBoundsSpec(
+ const extensions::api::app_window::BoundsSpecification* input_spec,
+ apps::AppWindow::BoundsSpecification* create_spec) {
+ if (!input_spec)
+ return;
+
+ if (input_spec->left.get())
+ create_spec->bounds.set_x(*input_spec->left);
+ if (input_spec->top.get())
+ create_spec->bounds.set_y(*input_spec->top);
+ if (input_spec->width.get())
+ create_spec->bounds.set_width(*input_spec->width);
+ if (input_spec->height.get())
+ create_spec->bounds.set_height(*input_spec->height);
+ if (input_spec->min_width.get())
+ create_spec->minimum_size.set_width(*input_spec->min_width);
+ if (input_spec->min_height.get())
+ create_spec->minimum_size.set_height(*input_spec->min_height);
+ if (input_spec->max_width.get())
+ create_spec->maximum_size.set_width(*input_spec->max_width);
+ if (input_spec->max_height.get())
+ create_spec->maximum_size.set_height(*input_spec->max_height);
+}
+
} // namespace
AppWindowCreateFunction::AppWindowCreateFunction()
@@ -173,37 +221,8 @@ bool AppWindowCreateFunction::RunImpl() {
}
}
- // TODO(jeremya): remove these, since they do the same thing as
- // left/top/width/height.
- if (options->default_width.get())
- create_params.bounds.set_width(*options->default_width.get());
- if (options->default_height.get())
- create_params.bounds.set_height(*options->default_height.get());
- if (options->default_left.get())
- create_params.bounds.set_x(*options->default_left.get());
- if (options->default_top.get())
- create_params.bounds.set_y(*options->default_top.get());
-
- if (options->width.get())
- create_params.bounds.set_width(*options->width.get());
- if (options->height.get())
- create_params.bounds.set_height(*options->height.get());
- if (options->left.get())
- create_params.bounds.set_x(*options->left.get());
- if (options->top.get())
- create_params.bounds.set_y(*options->top.get());
-
- if (options->bounds.get()) {
- app_window::ContentBounds* bounds = options->bounds.get();
- if (bounds->width.get())
- create_params.bounds.set_width(*bounds->width.get());
- if (bounds->height.get())
- create_params.bounds.set_height(*bounds->height.get());
- if (bounds->left.get())
- create_params.bounds.set_x(*bounds->left.get());
- if (bounds->top.get())
- create_params.bounds.set_y(*bounds->top.get());
- }
+ if (!GetBoundsSpec(*options, &create_params, &error_))
+ return false;
if (GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV ||
GetExtension()->location() == extensions::Manifest::COMPONENT) {
@@ -222,17 +241,6 @@ bool AppWindowCreateFunction::RunImpl() {
create_params.transparent_background = *options->transparent_background;
}
- gfx::Size& minimum_size = create_params.minimum_size;
- if (options->min_width.get())
- minimum_size.set_width(*options->min_width);
- if (options->min_height.get())
- minimum_size.set_height(*options->min_height);
- gfx::Size& maximum_size = create_params.maximum_size;
- if (options->max_width.get())
- maximum_size.set_width(*options->max_width);
- if (options->max_height.get())
- maximum_size.set_height(*options->max_height);
-
if (options->hidden.get())
create_params.hidden = *options->hidden.get();
@@ -301,6 +309,123 @@ bool AppWindowCreateFunction::RunImpl() {
return true;
}
+bool AppWindowCreateFunction::GetBoundsSpec(
+ const extensions::api::app_window::CreateWindowOptions& options,
+ apps::AppWindow::CreateParams* params,
+ std::string* error) {
+ DCHECK(params);
+ DCHECK(error);
+
+ if (options.inner_bounds.get() || options.outer_bounds.get()) {
+ // Parse the inner and outer bounds specifications. If developers use the
+ // new API, the deprecated fields will be ignored - do not attempt to merge
+ // them.
+
+ if (GetCurrentChannel() > chrome::VersionInfo::CHANNEL_DEV) {
+ *error = app_window_constants::kInvalidChannelForBounds;
+ return false;
+ }
+
+ const extensions::api::app_window::BoundsSpecification* inner_bounds =
+ options.inner_bounds.get();
+ const extensions::api::app_window::BoundsSpecification* outer_bounds =
+ options.outer_bounds.get();
+ if (inner_bounds && outer_bounds) {
+ if (!CheckBoundsConflict(
+ inner_bounds->left, outer_bounds->left, "left", error)) {
+ return false;
+ }
+ if (!CheckBoundsConflict(
+ inner_bounds->top, outer_bounds->top, "top", error)) {
+ return false;
+ }
+ if (!CheckBoundsConflict(
+ inner_bounds->width, outer_bounds->width, "width", error)) {
+ return false;
+ }
+ if (!CheckBoundsConflict(
+ inner_bounds->height, outer_bounds->height, "height", error)) {
+ return false;
+ }
+ if (!CheckBoundsConflict(inner_bounds->min_width,
+ outer_bounds->min_width,
+ "minWidth",
+ error)) {
+ return false;
+ }
+ if (!CheckBoundsConflict(inner_bounds->min_height,
+ outer_bounds->min_height,
+ "minHeight",
+ error)) {
+ return false;
+ }
+ if (!CheckBoundsConflict(inner_bounds->max_width,
+ outer_bounds->max_width,
+ "maxWidth",
+ error)) {
+ return false;
+ }
+ if (!CheckBoundsConflict(inner_bounds->max_height,
+ outer_bounds->max_height,
+ "maxHeight",
+ error)) {
+ return false;
+ }
+ }
+
+ CopyBoundsSpec(inner_bounds, &(params->content_spec));
+ CopyBoundsSpec(outer_bounds, &(params->window_spec));
+ } else {
+ // Parse deprecated fields.
+ // Due to a bug in NativeAppWindow::GetFrameInsets() on Windows and ChromeOS
+ // the bounds set the position of the window and the size of the content.
+ // This will be preserved as apps may be relying on this behavior.
+
+ if (options.default_width.get())
+ params->content_spec.bounds.set_width(*options.default_width.get());
+ if (options.default_height.get())
+ params->content_spec.bounds.set_height(*options.default_height.get());
+ if (options.default_left.get())
+ params->window_spec.bounds.set_x(*options.default_left.get());
+ if (options.default_top.get())
+ params->window_spec.bounds.set_y(*options.default_top.get());
+
+ if (options.width.get())
+ params->content_spec.bounds.set_width(*options.width.get());
+ if (options.height.get())
+ params->content_spec.bounds.set_height(*options.height.get());
+ if (options.left.get())
+ params->window_spec.bounds.set_x(*options.left.get());
+ if (options.top.get())
+ params->window_spec.bounds.set_y(*options.top.get());
+
+ if (options.bounds.get()) {
+ app_window::ContentBounds* bounds = options.bounds.get();
+ if (bounds->width.get())
+ params->content_spec.bounds.set_width(*bounds->width.get());
+ if (bounds->height.get())
+ params->content_spec.bounds.set_height(*bounds->height.get());
+ if (bounds->left.get())
+ params->window_spec.bounds.set_x(*bounds->left.get());
+ if (bounds->top.get())
+ params->window_spec.bounds.set_y(*bounds->top.get());
+ }
+
+ gfx::Size& minimum_size = params->content_spec.minimum_size;
+ if (options.min_width.get())
+ minimum_size.set_width(*options.min_width);
+ if (options.min_height.get())
+ minimum_size.set_height(*options.min_height);
+ gfx::Size& maximum_size = params->content_spec.maximum_size;
+ if (options.max_width.get())
+ maximum_size.set_width(*options.max_width);
+ if (options.max_height.get())
+ maximum_size.set_height(*options.max_height);
+ }
+
+ return true;
+}
+
AppWindow::Frame AppWindowCreateFunction::GetFrameFromString(
const std::string& frame_string) {
if (frame_string == kHtmlFrameOption &&
diff --git a/chrome/browser/extensions/api/app_window/app_window_api.h b/chrome/browser/extensions/api/app_window/app_window_api.h
index 28c3585..1dbb105 100644
--- a/chrome/browser/extensions/api/app_window/app_window_api.h
+++ b/chrome/browser/extensions/api/app_window/app_window_api.h
@@ -28,7 +28,10 @@ class AppWindowCreateFunction : public ChromeAsyncExtensionFunction {
virtual bool RunImpl() OVERRIDE;
private:
- bool inject_html_titlebar_;
+ bool GetBoundsSpec(
+ const extensions::api::app_window::CreateWindowOptions& options,
+ apps::AppWindow::CreateParams* params,
+ std::string* error);
apps::AppWindow::Frame GetFrameFromString(const std::string& frame_string);
bool GetFrameOptions(
@@ -36,6 +39,8 @@ class AppWindowCreateFunction : public ChromeAsyncExtensionFunction {
apps::AppWindow::CreateParams* create_params);
void UpdateFrameOptionsForChannel(
apps::AppWindow::CreateParams* create_params);
+
+ bool inject_html_titlebar_;
};
} // namespace extensions
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index a809ab2..0a1c36d 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -525,7 +525,7 @@ bool WindowsCreateFunction::RunImpl() {
if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH) {
AppWindow::CreateParams create_params;
create_params.window_type = AppWindow::WINDOW_TYPE_V1_PANEL;
- create_params.bounds = window_bounds;
+ create_params.window_spec.bounds = window_bounds;
create_params.focused = saw_focus_key && focused;
AppWindow* app_window = new AppWindow(
window_profile, new ChromeAppWindowDelegate(), GetExtension());
diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h
index 2527836..e7ca796 100644
--- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h
+++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h
@@ -136,10 +136,10 @@ class NativeAppWindowCocoa : public apps::NativeAppWindow,
virtual void ShowWithApp() OVERRIDE;
virtual void HideWithApp() OVERRIDE;
virtual void UpdateShelfMenu() OVERRIDE;
- virtual gfx::Size GetMinimumSize() const OVERRIDE;
- virtual void SetMinimumSize(const gfx::Size& size) OVERRIDE;
- virtual gfx::Size GetMaximumSize() const OVERRIDE;
- virtual void SetMaximumSize(const gfx::Size& size) OVERRIDE;
+ virtual gfx::Size GetContentMinimumSize() const OVERRIDE;
+ virtual void SetContentMinimumSize(const gfx::Size& size) OVERRIDE;
+ virtual gfx::Size GetContentMaximumSize() const OVERRIDE;
+ virtual void SetContentMaximumSize(const gfx::Size& size) OVERRIDE;
// WebContentsObserver implementation.
virtual void RenderViewCreated(content::RenderViewHost* rvh) OVERRIDE;
@@ -185,6 +185,11 @@ class NativeAppWindowCocoa : public apps::NativeAppWindow,
// Cache |restored_bounds_| only if the window is currently restored.
void UpdateRestoredBounds();
+ // Update the minimum and maximum size constraints, fullscreen and resize
+ // controls.
+ void SetContentSizeConstraints(const gfx::Size& minimum_size,
+ const gfx::Size& maximum_size);
+
// Hides the window unconditionally. Used by Hide and HideWithApp.
void HideWithoutMarkingHidden();
@@ -203,6 +208,7 @@ class NativeAppWindowCocoa : public apps::NativeAppWindow,
bool is_fullscreen_;
NSRect restored_bounds_;
+ bool is_resizable_;
bool shows_resize_controls_;
bool shows_fullscreen_controls_;
diff --git a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm
index 53812b9..917c2dd 100644
--- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm
+++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm
@@ -87,6 +87,23 @@ NSInteger AlwaysOnTopWindowLevel() {
return NSFloatingWindowLevel;
}
+NSRect GfxToCocoaBounds(gfx::Rect bounds) {
+ typedef apps::AppWindow::BoundsSpecification BoundsSpecification;
+
+ NSRect main_screen_rect = [[[NSScreen screens] objectAtIndex:0] frame];
+
+ // If coordinates are unspecified, center window on primary screen.
+ if (bounds.x() == BoundsSpecification::kUnspecifiedPosition)
+ bounds.set_x(floor((NSWidth(main_screen_rect) - bounds.width()) / 2));
+ if (bounds.y() == BoundsSpecification::kUnspecifiedPosition)
+ bounds.set_y(floor((NSHeight(main_screen_rect) - bounds.height()) / 2));
+
+ // Convert to Mac coordinates.
+ NSRect cocoa_bounds = NSRectFromCGRect(bounds.ToCGRect());
+ cocoa_bounds.origin.y = NSHeight(main_screen_rect) - NSMaxY(cocoa_bounds);
+ return cocoa_bounds;
+}
+
} // namespace
@implementation NativeAppWindowController
@@ -284,29 +301,13 @@ NativeAppWindowCocoa::NativeAppWindowCocoa(
is_hidden_with_app_(false),
is_maximized_(false),
is_fullscreen_(false),
+ is_resizable_(params.resizable),
+ shows_resize_controls_(true),
+ shows_fullscreen_controls_(true),
attention_request_id_(0),
use_system_drag_(true) {
Observe(web_contents());
- // Flip coordinates based on the primary screen.
- NSRect main_screen_rect = [[[NSScreen screens] objectAtIndex:0] frame];
- NSRect cocoa_bounds = NSMakeRect(params.bounds.x(),
- NSHeight(main_screen_rect) - params.bounds.y() - params.bounds.height(),
- params.bounds.width(), params.bounds.height());
-
- // If coordinates are < 0, center window on primary screen.
- if (params.bounds.x() == INT_MIN) {
- cocoa_bounds.origin.x =
- floor((NSWidth(main_screen_rect) - NSWidth(cocoa_bounds)) / 2);
- }
- if (params.bounds.y() == INT_MIN) {
- cocoa_bounds.origin.y =
- floor((NSHeight(main_screen_rect) - NSHeight(cocoa_bounds)) / 2);
- }
-
- // Initialize |restored_bounds_| after |cocoa_bounds| have been sanitized.
- restored_bounds_ = cocoa_bounds;
-
base::scoped_nsobject<NSWindow> window;
Class window_class;
if (has_frame_) {
@@ -319,12 +320,10 @@ NativeAppWindowCocoa::NativeAppWindowCocoa(
window_class = [ShellFramelessNSWindow class];
}
- size_constraints_.set_minimum_size(params.minimum_size);
- size_constraints_.set_maximum_size(params.maximum_size);
- shows_resize_controls_ =
- params.resizable && !size_constraints_.HasFixedSize();
- shows_fullscreen_controls_ =
- params.resizable && !size_constraints_.HasMaximumSize();
+ // Estimate the initial bounds of the window. Once the frame insets are known,
+ // the window bounds and constraints can be set precisely.
+ NSRect cocoa_bounds = GfxToCocoaBounds(
+ params.GetInitialWindowBounds(gfx::Insets()));
window.reset([[window_class alloc]
initWithContentRect:cocoa_bounds
styleMask:GetWindowStyleMask()
@@ -340,12 +339,6 @@ NativeAppWindowCocoa::NativeAppWindowCocoa(
[window setLevel:AlwaysOnTopWindowLevel()];
InitCollectionBehavior(window);
- // Set the window to participate in Lion Fullscreen mode. Setting this flag
- // has no effect on Snow Leopard or earlier. UI controls for fullscreen are
- // only shown for apps that have unbounded size.
- if (shows_fullscreen_controls_)
- SetFullScreenCollectionBehavior(window, true);
-
window_controller_.reset(
[[NativeAppWindowController alloc] initWithWindow:window.release()]);
@@ -364,9 +357,14 @@ NativeAppWindowCocoa::NativeAppWindowCocoa(
[[window_controller_ window] setDelegate:window_controller_];
[window_controller_ setAppWindow:this];
- // Update the size constraints of the NSWindow.
- SetMinimumSize(params.minimum_size);
- SetMaximumSize(params.maximum_size);
+ // We can now compute the precise window bounds and constraints.
+ gfx::Insets insets = GetFrameInsets();
+ SetBounds(params.GetInitialWindowBounds(insets));
+ SetContentSizeConstraints(params.GetContentMinimumSize(insets),
+ params.GetContentMaximumSize(insets));
+
+ // Initialize |restored_bounds_|.
+ restored_bounds_ = [this->window() frame];
extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryCocoa(
Profile::FromBrowserContext(app_window_->browser_context()),
@@ -613,13 +611,7 @@ void NativeAppWindowCocoa::SetBounds(const gfx::Rect& bounds) {
if (checked_bounds.height() > max_size.height)
checked_bounds.set_height(max_size.height);
- NSRect cocoa_bounds = NSMakeRect(checked_bounds.x(), 0,
- checked_bounds.width(),
- checked_bounds.height());
- // Flip coordinates based on the primary screen.
- NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
- cocoa_bounds.origin.y = NSHeight([screen frame]) - checked_bounds.bottom();
-
+ NSRect cocoa_bounds = GfxToCocoaBounds(checked_bounds);
[window() setFrame:cocoa_bounds display:YES];
// setFrame: without animate: does not trigger a windowDidEndLiveResize: so
// call it here.
@@ -1037,34 +1029,56 @@ void NativeAppWindowCocoa::UpdateRestoredBounds() {
restored_bounds_ = [window() frame];
}
+void NativeAppWindowCocoa::SetContentSizeConstraints(
+ const gfx::Size& minimum_size, const gfx::Size& maximum_size) {
+ // Update the size constraints.
+ size_constraints_.set_minimum_size(minimum_size);
+ size_constraints_.set_maximum_size(maximum_size);
+
+ gfx::Size min_size = size_constraints_.GetMinimumSize();
+ [window() setContentMinSize:NSMakeSize(min_size.width(), min_size.height())];
+
+ gfx::Size max_size = size_constraints_.GetMaximumSize();
+ const int kUnboundedSize = apps::SizeConstraints::kUnboundedSize;
+ CGFloat max_width = max_size.width() == kUnboundedSize ?
+ CGFLOAT_MAX : max_size.width();
+ CGFloat max_height = max_size.height() == kUnboundedSize ?
+ CGFLOAT_MAX : max_size.height();
+ [window() setContentMaxSize:NSMakeSize(max_width, max_height)];
+
+ // Update the window controls.
+ shows_resize_controls_ =
+ is_resizable_ && !size_constraints_.HasFixedSize();
+ shows_fullscreen_controls_ =
+ is_resizable_ && !size_constraints_.HasMaximumSize();
+
+ if (!is_fullscreen_) {
+ [window() setStyleMask:GetWindowStyleMask()];
+
+ // Set the window to participate in Lion Fullscreen mode. Setting this flag
+ // has no effect on Snow Leopard or earlier. UI controls for fullscreen are
+ // only shown for apps that have unbounded size.
+ SetFullScreenCollectionBehavior(window(), shows_fullscreen_controls_);
+ }
+}
+
void NativeAppWindowCocoa::UpdateShelfMenu() {
// TODO(tmdiep): To be implemented for Mac.
NOTIMPLEMENTED();
}
-gfx::Size NativeAppWindowCocoa::GetMinimumSize() const {
+gfx::Size NativeAppWindowCocoa::GetContentMinimumSize() const {
return size_constraints_.GetMinimumSize();
}
-void NativeAppWindowCocoa::SetMinimumSize(const gfx::Size& size) {
- size_constraints_.set_minimum_size(size);
-
- gfx::Size min_size = size_constraints_.GetMinimumSize();
- [window() setContentMinSize:NSMakeSize(min_size.width(), min_size.height())];
+void NativeAppWindowCocoa::SetContentMinimumSize(const gfx::Size& size) {
+ SetContentSizeConstraints(size, size_constraints_.GetMaximumSize());
}
-gfx::Size NativeAppWindowCocoa::GetMaximumSize() const {
+gfx::Size NativeAppWindowCocoa::GetContentMaximumSize() const {
return size_constraints_.GetMaximumSize();
}
-void NativeAppWindowCocoa::SetMaximumSize(const gfx::Size& size) {
- size_constraints_.set_maximum_size(size);
-
- gfx::Size max_size = size_constraints_.GetMaximumSize();
- const int kUnboundedSize = apps::SizeConstraints::kUnboundedSize;
- CGFloat max_width = max_size.width() == kUnboundedSize ?
- CGFLOAT_MAX : max_size.width();
- CGFloat max_height = max_size.height() == kUnboundedSize ?
- CGFLOAT_MAX : max_size.height();
- [window() setContentMaxSize:NSMakeSize(max_width, max_height)];
+void NativeAppWindowCocoa::SetContentMaximumSize(const gfx::Size& size) {
+ SetContentSizeConstraints(size_constraints_.GetMinimumSize(), size);
}
diff --git a/chrome/browser/ui/gtk/apps/native_app_window_gtk.cc b/chrome/browser/ui/gtk/apps/native_app_window_gtk.cc
index 1216992..c421c0a 100644
--- a/chrome/browser/ui/gtk/apps/native_app_window_gtk.cc
+++ b/chrome/browser/ui/gtk/apps/native_app_window_gtk.cc
@@ -61,30 +61,36 @@ NativeAppWindowGtk::NativeAppWindowGtk(AppWindow* app_window,
web_contents()->GetView()->GetNativeView();
gtk_container_add(GTK_CONTAINER(window_), native_view);
- if (params.bounds.x() != INT_MIN && params.bounds.y() != INT_MIN)
- gtk_window_move(window_, params.bounds.x(), params.bounds.y());
+ gfx::Insets frame_insets = GetFrameInsets();
+ gfx::Rect initial_bounds = params.GetInitialWindowBounds(frame_insets);
+
+ typedef apps::AppWindow::BoundsSpecification BoundsSpecification;
+ if (initial_bounds.x() != BoundsSpecification::kUnspecifiedPosition &&
+ initial_bounds.y() != BoundsSpecification::kUnspecifiedPosition) {
+ gtk_window_move(window_, initial_bounds.x(), initial_bounds.y());
+ }
// This is done to avoid a WM "feature" where setting the window size to
// the monitor size causes the WM to set the EWMH for full screen mode.
- int win_height = params.bounds.height();
+ int win_height = initial_bounds.height();
if (frameless_ &&
- gtk_window_util::BoundsMatchMonitorSize(window_, params.bounds)) {
+ gtk_window_util::BoundsMatchMonitorSize(window_, initial_bounds)) {
win_height -= 1;
}
- gtk_window_set_default_size(window_, params.bounds.width(), win_height);
+ gtk_window_set_default_size(window_, initial_bounds.width(), win_height);
resizable_ = params.resizable;
if (!resizable_) {
// If the window doesn't have a size request when we set resizable to
// false, GTK will shrink the window to 1x1px.
gtk_widget_set_size_request(GTK_WIDGET(window_),
- params.bounds.width(), win_height);
+ initial_bounds.width(), win_height);
gtk_window_set_resizable(window_, FALSE);
}
// make sure bounds_ and restored_bounds_ have correct values until we
// get our first configure-event
- bounds_ = restored_bounds_ = params.bounds;
+ bounds_ = restored_bounds_ = initial_bounds;
gint x, y;
gtk_window_get_position(window_, &x, &y);
bounds_.set_origin(gfx::Point(x, y));
@@ -96,9 +102,11 @@ NativeAppWindowGtk::NativeAppWindowGtk(AppWindow* app_window,
if (always_on_top_)
gtk_window_set_keep_above(window_, TRUE);
- size_constraints_.set_minimum_size(params.minimum_size);
- size_constraints_.set_maximum_size(params.maximum_size);
- UpdateWindowMinMaxSize();
+ size_constraints_.set_minimum_size(
+ params.GetContentMinimumSize(frame_insets));
+ size_constraints_.set_maximum_size(
+ params.GetContentMaximumSize(frame_insets));
+ UpdateContentMinMaxSize();
// In some (older) versions of compiz, raising top-level windows when they
// are partially off-screen causes them to get snapped back on screen, not
@@ -479,7 +487,7 @@ void NativeAppWindowGtk::OnConfigureDebounced() {
}
}
-void NativeAppWindowGtk::UpdateWindowMinMaxSize() {
+void NativeAppWindowGtk::UpdateContentMinMaxSize() {
GdkGeometry hints;
int hints_mask = 0;
if (size_constraints_.HasMinimumSize()) {
@@ -739,20 +747,20 @@ void NativeAppWindowGtk::UpdateShelfMenu() {
NOTIMPLEMENTED();
}
-gfx::Size NativeAppWindowGtk::GetMinimumSize() const {
+gfx::Size NativeAppWindowGtk::GetContentMinimumSize() const {
return size_constraints_.GetMinimumSize();
}
-void NativeAppWindowGtk::SetMinimumSize(const gfx::Size& size) {
+void NativeAppWindowGtk::SetContentMinimumSize(const gfx::Size& size) {
size_constraints_.set_minimum_size(size);
- UpdateWindowMinMaxSize();
+ UpdateContentMinMaxSize();
}
-gfx::Size NativeAppWindowGtk::GetMaximumSize() const {
+gfx::Size NativeAppWindowGtk::GetContentMaximumSize() const {
return size_constraints_.GetMaximumSize();
}
-void NativeAppWindowGtk::SetMaximumSize(const gfx::Size& size) {
+void NativeAppWindowGtk::SetContentMaximumSize(const gfx::Size& size) {
size_constraints_.set_maximum_size(size);
- UpdateWindowMinMaxSize();
+ UpdateContentMinMaxSize();
}
diff --git a/chrome/browser/ui/gtk/apps/native_app_window_gtk.h b/chrome/browser/ui/gtk/apps/native_app_window_gtk.h
index 0ba3edb..dca8474 100644
--- a/chrome/browser/ui/gtk/apps/native_app_window_gtk.h
+++ b/chrome/browser/ui/gtk/apps/native_app_window_gtk.h
@@ -87,10 +87,10 @@ class NativeAppWindowGtk : public apps::NativeAppWindow,
virtual void ShowWithApp() OVERRIDE;
virtual void UpdateShelfMenu() OVERRIDE;
// Calls gtk_window_set_geometry_hints with the current size constraints.
- virtual gfx::Size GetMinimumSize() const OVERRIDE;
- virtual void SetMinimumSize(const gfx::Size& size) OVERRIDE;
- virtual gfx::Size GetMaximumSize() const OVERRIDE;
- virtual void SetMaximumSize(const gfx::Size& size) OVERRIDE;
+ virtual gfx::Size GetContentMinimumSize() const OVERRIDE;
+ virtual void SetContentMinimumSize(const gfx::Size& size) OVERRIDE;
+ virtual gfx::Size GetContentMaximumSize() const OVERRIDE;
+ virtual void SetContentMaximumSize(const gfx::Size& size) OVERRIDE;
// web_modal::WebContentsModalDialogHost implementation.
virtual gfx::NativeView GetHostView() const OVERRIDE;
@@ -131,7 +131,7 @@ class NativeAppWindowGtk : public apps::NativeAppWindow,
void OnConfigureDebounced();
- void UpdateWindowMinMaxSize();
+ void UpdateContentMinMaxSize();
apps::AppWindow* app_window_; // weak - AppWindow owns NativeAppWindow.
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
index e4c5fe3..e75277b 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views.cc
@@ -224,11 +224,6 @@ void ChromeNativeAppWindowViews::InitializeDefaultWindow(
if (create_params.transparent_background)
init_params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
init_params.keep_on_top = create_params.always_on_top;
- gfx::Rect window_bounds = create_params.bounds;
- bool position_specified =
- window_bounds.x() != INT_MIN && window_bounds.y() != INT_MIN;
- if (position_specified && !window_bounds.IsEmpty())
- init_params.bounds = window_bounds;
#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
// Set up a custom WM_CLASS for app windows. This allows task switchers in
@@ -242,13 +237,22 @@ void ChromeNativeAppWindowViews::InitializeDefaultWindow(
OnBeforeWidgetInit(&init_params, window());
window()->Init(init_params);
- gfx::Rect adjusted_bounds = window_bounds;
- adjusted_bounds.Inset(-GetFrameInsets());
- // Center window if no position was specified.
- if (!position_specified)
- window()->CenterWindow(adjusted_bounds.size());
- else if (!adjusted_bounds.IsEmpty() && adjusted_bounds != window_bounds)
- window()->SetBounds(adjusted_bounds);
+ // The frame insets are required to resolve the bounds specifications
+ // correctly. So we set the window bounds and constraints now.
+ gfx::Insets frame_insets = GetFrameInsets();
+ gfx::Rect window_bounds = create_params.GetInitialWindowBounds(frame_insets);
+ SetContentMinimumSize(create_params.GetContentMinimumSize(frame_insets));
+ SetContentMaximumSize(create_params.GetContentMaximumSize(frame_insets));
+ if (!window_bounds.IsEmpty()) {
+ typedef apps::AppWindow::BoundsSpecification BoundsSpecification;
+ bool position_specified =
+ window_bounds.x() != BoundsSpecification::kUnspecifiedPosition &&
+ window_bounds.y() != BoundsSpecification::kUnspecifiedPosition;
+ if (!position_specified)
+ window()->CenterWindow(window_bounds.size());
+ else
+ window()->SetBounds(window_bounds);
+ }
// Register accelarators supported by app windows.
// TODO(jeremya/stevenjb): should these be registered for panels too?
@@ -283,8 +287,10 @@ void ChromeNativeAppWindowViews::InitializePanelWindow(
views::Widget::InitParams params(views::Widget::InitParams::TYPE_PANEL);
params.delegate = this;
- preferred_size_ = gfx::Size(create_params.bounds.width(),
- create_params.bounds.height());
+ gfx::Rect initial_window_bounds =
+ create_params.GetInitialWindowBounds(gfx::Insets());
+ preferred_size_ = gfx::Size(initial_window_bounds.width(),
+ initial_window_bounds.height());
if (preferred_size_.width() == 0)
preferred_size_.set_width(kDefaultPanelWidth);
else if (preferred_size_.width() < kMinPanelWidth)
@@ -314,8 +320,8 @@ void ChromeNativeAppWindowViews::InitializePanelWindow(
#if defined(USE_ASH)
if (create_params.state == ui::SHOW_STATE_DETACHED) {
- gfx::Rect window_bounds(create_params.bounds.x(),
- create_params.bounds.y(),
+ gfx::Rect window_bounds(initial_window_bounds.x(),
+ initial_window_bounds.y(),
preferred_size_.width(),
preferred_size_.height());
aura::Window* native_window = GetNativeWindow();
diff --git a/chrome/common/extensions/api/app_window.idl b/chrome/common/extensions/api/app_window.idl
index 6ccbac7..66f728f 100644
--- a/chrome/common/extensions/api/app_window.idl
+++ b/chrome/common/extensions/api/app_window.idl
@@ -15,7 +15,7 @@ namespace app.window {
long? height;
};
- [nodoc] dictionary BoundsSpecification {
+ dictionary BoundsSpecification {
// The X coordinate of the content or window.
long? left;
@@ -93,6 +93,34 @@ namespace app.window {
// creating a new window.
DOMString? id;
+ // Used to specify the initial position, initial size and constraints of the
+ // window's content (excluding window decorations).
+ // If an <code>id</code> is also specified and a window with a matching
+ // <code>id</code> has been shown before, the remembered bounds will be used
+ // instead.
+ //
+ // Note that the padding between the inner and outer bounds is determined by
+ // the OS. Therefore setting the same bounds property for both the
+ // <code>innerBounds</code> and <code>outerBounds</code> will result in an
+ // error.
+ //
+ // Currently only available on the Dev channel from Chrome 35.
+ BoundsSpecification? innerBounds;
+
+ // Used to specify the initial position, initial size and constraints of the
+ // window (including window decorations such as the title bar and frame).
+ // If an <code>id</code> is also specified and a window with a matching
+ // <code>id</code> has been shown before, the remembered bounds will be used
+ // instead.
+ //
+ // Note that the padding between the inner and outer bounds is determined by
+ // the OS. Therefore setting the same bounds property for both the
+ // <code>innerBounds</code> and <code>outerBounds</code> will result in an
+ // error.
+ //
+ // Currently only available on the Dev channel from Chrome 35.
+ BoundsSpecification? outerBounds;
+
// Default width of the window.
[nodoc, deprecated="Use $ref:BoundsSpecification."] long? defaultWidth;
@@ -252,36 +280,36 @@ namespace app.window {
// Set the window's inner bounds.
static void setBounds(ContentBounds bounds);
- // Get the current minimum width of the window. Returns |undefined| if there
- // is no minimum. Currently only available on the Dev channel.
+ // Get the current minimum width of the window. Returns <code>null</code> if
+ // there is no minimum. Currently only available on the Dev channel.
[nocompile] static long getMinWidth();
- // Get the current minimum height of the window. Returns |undefined| if
- // there is no minimum. Currently only available on the Dev channel.
+ // Get the current minimum height of the window. Returns <code>null</code>
+ // if there is no minimum. Currently only available on the Dev channel.
[nocompile] static long getMinHeight();
- // Get the current maximum width of the window. Returns |undefined| if there
- // is no maximum. Currently only available on the Dev channel.
+ // Get the current maximum width of the window. Returns <code>null</code> if
+ // there is no maximum. Currently only available on the Dev channel.
[nocompile] static long getMaxWidth();
- // Get the current maximum height of the window. Returns |undefined| if
- // there is no maximum. Currently only available on the Dev channel.
+ // Get the current maximum height of the window. Returns <code>null</code>
+ // if there is no maximum. Currently only available on the Dev channel.
[nocompile] static long getMaxHeight();
- // Set the current minimum width of the window. Set to |null| to remove the
- // constraint. Currently only available on the Dev channel.
+ // Set the current minimum width of the window. Set to <code>null</code> to
+ // remove the constraint. Currently only available on the Dev channel.
static void setMinWidth(optional long minWidth);
- // Set the current minimum height of the window. Set to |null| to remove the
- // constraint. Currently only available on the Dev channel.
+ // Set the current minimum height of the window. Set to <code>null</code> to
+ // remove the constraint. Currently only available on the Dev channel.
static void setMinHeight(optional long minHeight);
- // Set the current maximum width of the window. Set to |null| to remove the
- // constraint. Currently only available on the Dev channel.
+ // Set the current maximum width of the window. Set to <code>null</code> to
+ // remove the constraint. Currently only available on the Dev channel.
static void setMaxWidth(optional long maxWidth);
- // Set the current maximum height of the window. Set to |null| to remove the
- // constraint. Currently only available on the Dev channel.
+ // Set the current maximum height of the window. Set to <code>null</code> to
+ // remove the constraint. Currently only available on the Dev channel.
static void setMaxHeight(optional long maxHeight);
// Set the app icon for the window (experimental).
@@ -331,11 +359,11 @@ namespace app.window {
// all, in which case a default size and platform dependent position will
// be used.
//
- // Another option is to use the bounds property, which will put the window
- // at the specified coordinates with the specified size. If the window has
- // a frame, it's total size will be the size given plus the size of the
- // frame; that is, the size in bounds is the content size, not the window
- // size.
+ // Another option is to use the <code>bounds</code> property, which will put
+ // the window at the specified coordinates with the specified size. If the
+ // window has a frame, it's total size will be the size given plus the size
+ // of the frame; that is, the size in bounds is the content size, not the
+ // window size.
//
// To automatically remember the positions of windows you can give them ids.
// If a window has an id, This id is used to remember the size and position
diff --git a/chrome/renderer/resources/extensions/app_window_custom_bindings.js b/chrome/renderer/resources/extensions/app_window_custom_bindings.js
index 53eed44..8950667 100644
--- a/chrome/renderer/resources/extensions/app_window_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/app_window_custom_bindings.js
@@ -169,9 +169,13 @@ appWindow.registerCustomHook(function(bindingsAPI) {
this.contentWindow.close();
};
AppWindow.prototype.getBounds = function() {
- var bounds = appWindowData.innerBounds;
- return { left: bounds.left, top: bounds.top,
- width: bounds.width, height: bounds.height };
+ // This is to maintain backcompatibility with a bug on Windows and
+ // ChromeOS, which returns the position of the window but the size of
+ // the content.
+ var innerBounds = appWindowData.innerBounds;
+ var outerBounds = appWindowData.outerBounds;
+ return { left: outerBounds.left, top: outerBounds.top,
+ width: innerBounds.width, height: innerBounds.height };
};
AppWindow.prototype.getMinWidth = function() {
return appWindowData.innerBounds.minWidth;
diff --git a/chrome/test/data/extensions/platform_apps/window_api/test.js b/chrome/test/data/extensions/platform_apps/window_api/test.js
index 9760900..62edcae 100644
--- a/chrome/test/data/extensions/platform_apps/window_api/test.js
+++ b/chrome/test/data/extensions/platform_apps/window_api/test.js
@@ -49,6 +49,65 @@ function waitForLoad(win, callback) {
}));
}
+function assertConstraintsUnspecified(win) {
+ chrome.test.assertEq(null, win.innerBounds.minWidth);
+ chrome.test.assertEq(null, win.innerBounds.minHeight);
+ chrome.test.assertEq(null, win.innerBounds.maxWidth);
+ chrome.test.assertEq(null, win.innerBounds.maxHeight);
+ chrome.test.assertEq(null, win.outerBounds.minWidth);
+ chrome.test.assertEq(null, win.outerBounds.minHeight);
+ chrome.test.assertEq(null, win.outerBounds.maxWidth);
+ chrome.test.assertEq(null, win.outerBounds.maxHeight);
+}
+
+function assertBoundsConsistent(win) {
+ // Ensure that the inner and outer bounds are consistent. Since platforms
+ // have different frame padding, we cannot check the sizes precisely.
+ // It is a reasonable assumption that all platforms will have a title bar at
+ // the top of the window.
+ chrome.test.assertTrue(win.innerBounds.left >= win.outerBounds.left);
+ chrome.test.assertTrue(win.innerBounds.top > win.outerBounds.top);
+ chrome.test.assertTrue(win.innerBounds.width <= win.outerBounds.width);
+ chrome.test.assertTrue(win.innerBounds.height < win.outerBounds.height);
+
+ if (win.innerBounds.minWidth === null)
+ chrome.test.assertEq(null, win.outerBounds.minWidth);
+ else
+ chrome.test.assertTrue(
+ win.innerBounds.minWidth <= win.outerBounds.minWidth);
+
+ if (win.innerBounds.minHeight === null)
+ chrome.test.assertEq(null, win.outerBounds.minHeight);
+ else
+ chrome.test.assertTrue(
+ win.innerBounds.minHeight < win.outerBounds.minHeight);
+
+ if (win.innerBounds.maxWidth === null)
+ chrome.test.assertEq(null, win.outerBounds.maxWidth);
+ else
+ chrome.test.assertTrue(
+ win.innerBounds.maxWidth <= win.outerBounds.maxWidth);
+
+ if (win.innerBounds.maxHeight === null)
+ chrome.test.assertEq(null, win.outerBounds.maxHeight);
+ else
+ chrome.test.assertTrue(
+ win.innerBounds.maxHeight < win.outerBounds.maxHeight);
+}
+
+function testConflictingBoundsProperty(propertyName) {
+ var innerBounds = {};
+ var outerBounds = {};
+ innerBounds[propertyName] = 20;
+ outerBounds[propertyName] = 20;
+ chrome.app.window.create('test.html', {
+ innerBounds: innerBounds,
+ outerBounds: outerBounds
+ }, callbackFail('The ' + propertyName + ' property cannot be specified for ' +
+ 'both inner and outer bounds.')
+ );
+}
+
function testCreate() {
chrome.test.runTests([
function basic() {
@@ -103,51 +162,471 @@ function testCreate() {
win2.contentWindow.close();
}));
}));
- },
+ }
+ ]);
+}
+function testDeprecatedBounds() {
+ chrome.test.runTests([
function contentSize() {
- chrome.app.window.create('test.html',
- { bounds: { width: 250, height: 200 } }, callbackPass(function(win) {
- assertFuzzyEq(250, win.contentWindow.innerWidth, defaultFuzzFactor);
- assertFuzzyEq(200, win.contentWindow.innerHeight, defaultFuzzFactor);
+ var options = { bounds: { width: 250, height: 200 } };
+ chrome.app.window.create('test.html', options, callbackPass(
+ function(win) {
+ var bounds = win.getBounds();
+ chrome.test.assertEq(options.bounds.width, bounds.width);
+ chrome.test.assertEq(options.bounds.height, bounds.height);
+ chrome.test.assertEq(options.bounds.width, win.innerBounds.width);
+ chrome.test.assertEq(options.bounds.height, win.innerBounds.height);
+ win.close();
+ }));
+ },
+
+ function windowPosition() {
+ var options = { bounds: { left: 250, top: 200 } };
+ chrome.app.window.create('test.html', options, callbackPass(
+ function(win) {
+ var bounds = win.getBounds();
+ chrome.test.assertEq(options.bounds.left, bounds.left);
+ chrome.test.assertEq(options.bounds.top, bounds.top);
+ chrome.test.assertEq(options.bounds.left, win.outerBounds.left);
+ chrome.test.assertEq(options.bounds.top, win.outerBounds.top);
win.close();
}));
},
function minSize() {
- chrome.app.window.create('test.html', {
+ var options = {
bounds: { width: 250, height: 250 },
minWidth: 400, minHeight: 450
- }, callbackPass(function(win) {
- var w = win.contentWindow;
- assertFuzzyEq(400, w.innerWidth, defaultFuzzFactor);
- assertFuzzyEq(450, w.innerHeight, defaultFuzzFactor);
- w.close();
+ };
+ chrome.app.window.create('test.html', options, callbackPass(
+ function(win) {
+ var bounds = win.getBounds();
+ chrome.test.assertEq(options.minWidth, bounds.width);
+ chrome.test.assertEq(options.minHeight, bounds.height);
+ win.close();
}));
},
function maxSize() {
- chrome.app.window.create('test.html', {
+ var options = {
bounds: { width: 250, height: 250 },
maxWidth: 200, maxHeight: 150
- }, callbackPass(function(win) {
- var w = win.contentWindow;
- assertFuzzyEq(200, w.innerWidth, defaultFuzzFactor);
- assertFuzzyEq(150, w.innerHeight, defaultFuzzFactor);
- w.close();
+ };
+ chrome.app.window.create('test.html', options, callbackPass(
+ function(win) {
+ var bounds = win.getBounds();
+ chrome.test.assertEq(options.maxWidth, bounds.width);
+ chrome.test.assertEq(options.maxHeight, bounds.height);
+ win.close();
}));
},
function minAndMaxSize() {
- chrome.app.window.create('test.html', {
+ var options = {
bounds: { width: 250, height: 250 },
minWidth: 400, minHeight: 450,
maxWidth: 200, maxHeight: 150
+ };
+ chrome.app.window.create('test.html', options, callbackPass(
+ function(win) {
+ var bounds = win.getBounds();
+ chrome.test.assertEq(options.minWidth, bounds.width);
+ chrome.test.assertEq(options.minHeight, bounds.height);
+ win.close();
+ }));
+ },
+
+ function simpleSetBounds() {
+ chrome.app.window.create('test.html',
+ { bounds: { width: 250, height: 200 } }, callbackPass(function(win) {
+ var newBounds = {width: 400, height: 450};
+ win.setBounds(newBounds);
+ chrome.test.waitForRoundTrip('msg', callbackPass(function() {
+ var bounds = win.getBounds();
+ chrome.test.assertEq(newBounds.width, bounds.width);
+ chrome.test.assertEq(newBounds.height, bounds.height);
+ win.close();
+ }));
+ }));
+ },
+
+ function heightOnlySetBounds() {
+ chrome.app.window.create('test.html', {
+ bounds: { width: 512, height: 256 }
}, callbackPass(function(win) {
- var w = win.contentWindow;
- assertFuzzyEq(400, w.innerWidth, defaultFuzzFactor);
- assertFuzzyEq(450, w.innerHeight, defaultFuzzFactor);
- w.close();
+ win.setBounds({ height: 512 });
+ chrome.test.waitForRoundTrip('msg', callbackPass(function() {
+ var bounds = win.getBounds();
+ chrome.test.assertEq(512, bounds.width);
+ chrome.test.assertEq(512, bounds.height);
+ win.close();
+ }));
+ }));
+ },
+ ]);
+}
+
+function testInitialBounds() {
+ chrome.test.runTests([
+ function testNoOptions() {
+ chrome.app.window.create('test.html', {
+ }, callbackPass(function(win) {
+ chrome.test.assertTrue(win != null);
+ chrome.test.assertTrue(win.innerBounds.width > 0);
+ chrome.test.assertTrue(win.innerBounds.height > 0);
+ chrome.test.assertTrue(win.outerBounds.width > 0);
+ chrome.test.assertTrue(win.outerBounds.height > 0);
+ assertConstraintsUnspecified(win);
+ assertBoundsConsistent(win);
+ win.close();
+ }));
+ },
+
+ function testInnerBoundsOnly() {
+ var innerBounds = {
+ left: 150,
+ top: 100,
+ width: 400,
+ height: 300
+ };
+ chrome.app.window.create('test.html', {
+ innerBounds: innerBounds
+ }, callbackPass(function(win) {
+ chrome.test.assertTrue(win != null);
+ chrome.test.assertEq(innerBounds.left, win.innerBounds.left);
+ chrome.test.assertEq(innerBounds.top, win.innerBounds.top);
+ chrome.test.assertEq(innerBounds.width, win.innerBounds.width);
+ chrome.test.assertEq(innerBounds.height, win.innerBounds.height);
+ assertBoundsConsistent(win);
+ assertConstraintsUnspecified(win);
+ win.close();
+ }));
+ },
+
+ function testOuterBoundsOnly() {
+ var outerBounds = {
+ left: 150,
+ top: 100,
+ width: 400,
+ height: 300
+ };
+ chrome.app.window.create('test.html', {
+ outerBounds: outerBounds
+ }, callbackPass(function(win) {
+ chrome.test.assertTrue(win != null);
+ chrome.test.assertEq(outerBounds.left, win.outerBounds.left);
+ chrome.test.assertEq(outerBounds.top, win.outerBounds.top);
+ chrome.test.assertEq(outerBounds.width, win.outerBounds.width);
+ chrome.test.assertEq(outerBounds.height, win.outerBounds.height);
+ assertBoundsConsistent(win);
+ assertConstraintsUnspecified(win);
+ win.close();
+ }));
+ },
+
+ function testFrameless() {
+ var outerBounds = {
+ left: 150,
+ top: 100,
+ width: 400,
+ height: 300
+ };
+ chrome.app.window.create('test.html', {
+ outerBounds: outerBounds,
+ frame: 'none'
+ }, callbackPass(function(win) {
+ chrome.test.assertTrue(win != null);
+ chrome.test.assertEq(outerBounds.left, win.outerBounds.left);
+ chrome.test.assertEq(outerBounds.top, win.outerBounds.top);
+ chrome.test.assertEq(outerBounds.width, win.outerBounds.width);
+ chrome.test.assertEq(outerBounds.height, win.outerBounds.height);
+ chrome.test.assertEq(outerBounds.left, win.innerBounds.left);
+ chrome.test.assertEq(outerBounds.top, win.innerBounds.top);
+ chrome.test.assertEq(outerBounds.width, win.innerBounds.width);
+ chrome.test.assertEq(outerBounds.height, win.innerBounds.height);
+ assertConstraintsUnspecified(win);
+ win.close();
+ }));
+ },
+
+ function testInnerSizeAndOuterPos() {
+ var innerBounds = {
+ width: 400,
+ height: 300
+ };
+ var outerBounds = {
+ left: 150,
+ top: 100
+ };
+ chrome.app.window.create('test.html', {
+ innerBounds: innerBounds,
+ outerBounds: outerBounds
+ }, callbackPass(function(win) {
+ chrome.test.assertTrue(win != null);
+ chrome.test.assertEq(outerBounds.left, win.outerBounds.left);
+ chrome.test.assertEq(outerBounds.top, win.outerBounds.top);
+ chrome.test.assertEq(innerBounds.width, win.innerBounds.width);
+ chrome.test.assertEq(innerBounds.height, win.innerBounds.height);
+ assertBoundsConsistent(win);
+ assertConstraintsUnspecified(win);
+ win.close();
+ }));
+ },
+
+ function testInnerAndOuterBoundsEdgeCase() {
+ var innerBounds = {
+ left: 150,
+ height: 300
+ };
+ var outerBounds = {
+ width: 400,
+ top: 100
+ };
+ chrome.app.window.create('test.html', {
+ innerBounds: innerBounds,
+ outerBounds: outerBounds
+ }, callbackPass(function(win) {
+ chrome.test.assertTrue(win != null);
+ chrome.test.assertEq(innerBounds.left, win.innerBounds.left);
+ chrome.test.assertEq(innerBounds.height, win.innerBounds.height);
+ chrome.test.assertEq(outerBounds.top, win.outerBounds.top);
+ chrome.test.assertEq(outerBounds.width, win.outerBounds.width);
+ assertBoundsConsistent(win);
+ assertConstraintsUnspecified(win);
+ win.close();
+ }));
+ },
+
+ function testPositionOnly() {
+ var outerBounds = {
+ left: 150,
+ top: 100
+ };
+ chrome.app.window.create('test.html', {
+ outerBounds: outerBounds
+ }, callbackPass(function(win) {
+ chrome.test.assertTrue(win != null);
+ chrome.test.assertEq(outerBounds.left, win.outerBounds.left);
+ chrome.test.assertEq(outerBounds.top, win.outerBounds.top);
+ chrome.test.assertTrue(win.innerBounds.width > 0);
+ chrome.test.assertTrue(win.innerBounds.height > 0);
+ chrome.test.assertTrue(win.outerBounds.width > 0);
+ chrome.test.assertTrue(win.outerBounds.height > 0);
+ assertBoundsConsistent(win);
+ assertConstraintsUnspecified(win);
+ win.close();
+ }));
+ },
+
+ function testSizeOnly() {
+ var outerBounds = {
+ width: 500,
+ height: 400
+ };
+ chrome.app.window.create('test.html', {
+ outerBounds: outerBounds
+ }, callbackPass(function(win) {
+ chrome.test.assertTrue(win != null);
+ chrome.test.assertEq(outerBounds.width, win.outerBounds.width);
+ chrome.test.assertEq(outerBounds.height, win.outerBounds.height);
+ assertBoundsConsistent(win);
+ assertConstraintsUnspecified(win);
+ win.close();
+ }));
+ },
+
+ function testConflictingProperties() {
+ testConflictingBoundsProperty("width");
+ testConflictingBoundsProperty("height");
+ testConflictingBoundsProperty("left");
+ testConflictingBoundsProperty("top");
+ testConflictingBoundsProperty("minWidth");
+ testConflictingBoundsProperty("minHeight");
+ testConflictingBoundsProperty("maxWidth");
+ testConflictingBoundsProperty("maxHeight");
+ }
+ ]);
+}
+
+function testInitialBoundsInStable() {
+ chrome.test.runTests([
+ function testInnerBounds() {
+ var innerBounds = {
+ width: 600,
+ height: 400
+ };
+ chrome.app.window.create('test.html', {
+ innerBounds: innerBounds
+ }, callbackFail('innerBounds and outerBounds are only available in'+
+ ' dev channel.')
+ );
+ },
+
+ function testOuterBounds() {
+ var outerBounds = {
+ width: 600,
+ height: 400
+ };
+ chrome.app.window.create('test.html', {
+ outerBounds: outerBounds
+ }, callbackFail('innerBounds and outerBounds are only available in'+
+ ' dev channel.')
+ );
+ }
+ ]);
+}
+
+function testInitialConstraints() {
+ chrome.test.runTests([
+ function testMaxInnerConstraints() {
+ var innerBounds = {
+ width: 800,
+ height: 600,
+ maxWidth: 500,
+ maxHeight: 400
+ };
+ chrome.app.window.create('test.html', {
+ innerBounds: innerBounds
+ }, callbackPass(function(win) {
+ chrome.test.assertTrue(win != null);
+ chrome.test.assertEq(innerBounds.maxWidth, win.innerBounds.width);
+ chrome.test.assertEq(innerBounds.maxHeight, win.innerBounds.height);
+ chrome.test.assertEq(innerBounds.maxWidth, win.innerBounds.maxWidth);
+ chrome.test.assertEq(innerBounds.maxHeight, win.innerBounds.maxHeight);
+ assertBoundsConsistent(win);
+ win.close();
+ }));
+ },
+
+ function testMinInnerConstraints() {
+ var innerBounds = {
+ width: 100,
+ height: 100,
+ minWidth: 300,
+ minHeight: 200
+ };
+ chrome.app.window.create('test.html', {
+ innerBounds: innerBounds
+ }, callbackPass(function(win) {
+ chrome.test.assertTrue(win != null);
+ chrome.test.assertEq(innerBounds.minWidth, win.innerBounds.width);
+ chrome.test.assertEq(innerBounds.minHeight, win.innerBounds.height);
+ chrome.test.assertEq(innerBounds.minWidth, win.innerBounds.minWidth);
+ chrome.test.assertEq(innerBounds.minHeight, win.innerBounds.minHeight);
+ assertBoundsConsistent(win);
+ win.close();
+ }));
+ },
+
+ function testMaxOuterConstraints() {
+ var outerBounds = {
+ width: 800,
+ height: 600,
+ maxWidth: 500,
+ maxHeight: 400
+ };
+ chrome.app.window.create('test.html', {
+ outerBounds: outerBounds
+ }, callbackPass(function(win) {
+ chrome.test.assertTrue(win != null);
+ chrome.test.assertEq(outerBounds.maxWidth, win.outerBounds.width);
+ chrome.test.assertEq(outerBounds.maxHeight, win.outerBounds.height);
+ chrome.test.assertEq(outerBounds.maxWidth, win.outerBounds.maxWidth);
+ chrome.test.assertEq(outerBounds.maxHeight, win.outerBounds.maxHeight);
+ assertBoundsConsistent(win);
+ win.close();
+ }));
+ },
+
+ function testMinOuterConstraints() {
+ var outerBounds = {
+ width: 100,
+ height: 100,
+ minWidth: 300,
+ minHeight: 200
+ };
+ chrome.app.window.create('test.html', {
+ outerBounds: outerBounds
+ }, callbackPass(function(win) {
+ chrome.test.assertTrue(win != null);
+ chrome.test.assertEq(outerBounds.minWidth, win.outerBounds.width);
+ chrome.test.assertEq(outerBounds.minHeight, win.outerBounds.height);
+ chrome.test.assertEq(outerBounds.minWidth, win.outerBounds.minWidth);
+ chrome.test.assertEq(outerBounds.minHeight, win.outerBounds.minHeight);
+ assertBoundsConsistent(win);
+ win.close();
+ }));
+ },
+
+ function testMixedConstraints() {
+ var innerBounds = {
+ width: 100,
+ minHeight: 300
+ };
+ var outerBounds = {
+ height: 100,
+ minWidth: 400,
+ };
+ chrome.app.window.create('test.html', {
+ innerBounds: innerBounds,
+ outerBounds: outerBounds
+ }, callbackPass(function(win) {
+ chrome.test.assertTrue(win != null);
+ chrome.test.assertEq(outerBounds.minWidth, win.outerBounds.width);
+ chrome.test.assertEq(innerBounds.minHeight, win.innerBounds.height);
+ chrome.test.assertEq(outerBounds.minWidth, win.outerBounds.minWidth);
+ chrome.test.assertEq(innerBounds.minHeight, win.innerBounds.minHeight);
+ assertBoundsConsistent(win);
+ win.close();
+ }));
+ },
+
+ function testBadConstraints() {
+ var outerBounds = {
+ width: 500,
+ height: 400,
+ minWidth: 800,
+ minHeight: 700,
+ maxWidth: 300,
+ maxHeight: 200
+ };
+ chrome.app.window.create('test.html', {
+ outerBounds: outerBounds
+ }, callbackPass(function(win) {
+ chrome.test.assertTrue(win != null);
+ chrome.test.assertEq(outerBounds.minWidth, win.outerBounds.width);
+ chrome.test.assertEq(outerBounds.minHeight, win.outerBounds.height);
+ chrome.test.assertEq(outerBounds.minWidth, win.outerBounds.minWidth);
+ chrome.test.assertEq(outerBounds.minHeight, win.outerBounds.minHeight);
+ chrome.test.assertEq(outerBounds.minWidth, win.outerBounds.maxWidth);
+ chrome.test.assertEq(outerBounds.minHeight, win.outerBounds.maxHeight);
+ assertBoundsConsistent(win);
+ win.close();
+ }));
+ },
+
+ function testFrameless() {
+ var outerBounds = {
+ minWidth: 50,
+ minHeight: 50,
+ maxWidth: 800,
+ maxHeight: 800
+ };
+ chrome.app.window.create('test.html', {
+ outerBounds: outerBounds,
+ frame: 'none'
+ }, callbackPass(function(win) {
+ chrome.test.assertTrue(win != null);
+ chrome.test.assertEq(outerBounds.minWidth, win.outerBounds.minWidth);
+ chrome.test.assertEq(outerBounds.minHeight, win.outerBounds.minHeight);
+ chrome.test.assertEq(outerBounds.maxWidth, win.outerBounds.maxWidth);
+ chrome.test.assertEq(outerBounds.maxHeight, win.outerBounds.maxHeight);
+ chrome.test.assertEq(outerBounds.minWidth, win.innerBounds.minWidth);
+ chrome.test.assertEq(outerBounds.minHeight, win.innerBounds.minHeight);
+ chrome.test.assertEq(outerBounds.maxWidth, win.innerBounds.maxWidth);
+ chrome.test.assertEq(outerBounds.maxHeight, win.innerBounds.maxHeight);
+ win.close();
}));
}
]);
@@ -177,37 +656,6 @@ function testSingleton() {
]);
}
-function testBounds() {
- chrome.test.runTests([
- function simpleSetBounds() {
- chrome.app.window.create('test.html',
- { bounds: { width: 250, height: 200 } }, callbackPass(function(win) {
- var b = win.getBounds();
- win.setBounds({width: 400, height: 450})
- // Listen to onresize here rather than win.onBoundsChanged, because
- // onBoundsChanged is fired before the web contents are resized.
- win.contentWindow.onresize = callbackPass(function() {
- assertFuzzyEq(400, win.contentWindow.innerWidth, defaultFuzzFactor);
- assertFuzzyEq(450, win.contentWindow.innerHeight, defaultFuzzFactor);
- win.close();
- });
- }));
- },
-
- function heightOnlySetBounds() {
- chrome.app.window.create('test.html', {
- bounds: { width: 512, height: 256 }
- }, callbackPass(function(win) {
- win.setBounds({ height: 512 });
- win.contentWindow.onresize = callbackPass(function() {
- assertFuzzyEq(512, win.contentWindow.innerHeight, defaultFuzzFactor);
- win.close();
- });
- }));
- },
- ]);
-}
-
function testCloseEvent() {
chrome.test.runTests([
function basic() {