diff options
20 files changed, 334 insertions, 53 deletions
diff --git a/apps/app_window.cc b/apps/app_window.cc index 99198b0..1f0c945 100644 --- a/apps/app_window.cc +++ b/apps/app_window.cc @@ -123,6 +123,7 @@ void AppWindow::SizeConstraints::set_maximum_size(const gfx::Size& max_size) { 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), @@ -610,6 +611,8 @@ void AppWindow::GetSerializedState(base::DictionaryValue* properties) const { boundsValue->SetInteger("width", bounds.width()); boundsValue->SetInteger("height", bounds.height()); properties->Set("bounds", boundsValue.release()); + properties->SetBoolean("hasFrameColor", native_app_window_->HasFrameColor()); + properties->SetInteger("frameColor", native_app_window_->FrameColor()); const SizeConstraints& constraints = size_constraints(); gfx::Size min_size = constraints.GetMinimumSize(); diff --git a/apps/app_window.h b/apps/app_window.h index 46933c9..9551539 100644 --- a/apps/app_window.h +++ b/apps/app_window.h @@ -157,6 +157,9 @@ class AppWindow : public content::NotificationObserver, WindowType window_type; Frame frame; + + bool has_frame_color; + SkColor frame_color; bool transparent_background; // Only supported on ash. // Specify the initial content bounds of the window (excluding any window diff --git a/apps/ui/native_app_window.h b/apps/ui/native_app_window.h index 1668916..fe32f53 100644 --- a/apps/ui/native_app_window.h +++ b/apps/ui/native_app_window.h @@ -7,6 +7,7 @@ #include "apps/app_window.h" #include "components/web_modal/web_contents_modal_dialog_host.h" +#include "third_party/skia/include/core/SkColor.h" #include "ui/base/base_window.h" #include "ui/gfx/insets.h" @@ -57,6 +58,10 @@ class NativeAppWindow : public ui::BaseWindow, // chrome.app.window.create with the option 'frame' set to 'none'. virtual bool IsFrameless() const = 0; + // Returns information about the window's frame. + virtual bool HasFrameColor() const = 0; + virtual SkColor FrameColor() const = 0; + // Returns the difference between the window bounds (including titlebar and // borders) and the content bounds, if any. virtual gfx::Insets GetFrameInsets() const = 0; diff --git a/apps/ui/views/app_window_frame_view.cc b/apps/ui/views/app_window_frame_view.cc index a8c11c3..d981ee7 100644 --- a/apps/ui/views/app_window_frame_view.cc +++ b/apps/ui/views/app_window_frame_view.cc @@ -51,11 +51,13 @@ AppWindowFrameView::AppWindowFrameView(NativeAppWindow* window) AppWindowFrameView::~AppWindowFrameView() {} void AppWindowFrameView::Init(views::Widget* frame, + const SkColor& frame_color, int resize_inside_bounds_size, int resize_outside_bounds_size, int resize_outside_scale_for_touch, int resize_area_corner_size) { frame_ = frame; + frame_color_ = frame_color; resize_inside_bounds_size_ = resize_inside_bounds_size; resize_outside_bounds_size_ = resize_outside_bounds_size; resize_area_corner_size_ = resize_area_corner_size; @@ -156,11 +158,12 @@ int AppWindowFrameView::NonClientHitTest(const gfx::Point& point) { return HTCLIENT; gfx::Rect expanded_bounds = bounds(); - if (resize_outside_bounds_size_) + if (resize_outside_bounds_size_) { expanded_bounds.Inset(gfx::Insets(-resize_outside_bounds_size_, -resize_outside_bounds_size_, -resize_outside_bounds_size_, -resize_outside_bounds_size_)); + } // Points outside the (possibly expanded) bounds can be discarded. if (!expanded_bounds.Contains(point)) return HTNOWHERE; @@ -297,7 +300,7 @@ void AppWindowFrameView::OnPaint(gfx::Canvas* canvas) { SkPaint paint; paint.setAntiAlias(false); paint.setStyle(SkPaint::kFill_Style); - paint.setColor(SK_ColorWHITE); + paint.setColor(frame_color_); gfx::Path path; path.moveTo(0, 0); path.lineTo(width(), 0); diff --git a/apps/ui/views/app_window_frame_view.h b/apps/ui/views/app_window_frame_view.h index 77c3859..2be6161 100644 --- a/apps/ui/views/app_window_frame_view.h +++ b/apps/ui/views/app_window_frame_view.h @@ -44,6 +44,7 @@ class AppWindowFrameView : public views::NonClientFrameView, // which a click is interpreted as a resize for the inner and outer border of // the window and the lower-right corner resize handle. void Init(views::Widget* frame, + const SkColor& frame_color, int resize_inside_bounds_size, int resize_outside_bounds_size, int resize_outside_scale_for_touch, @@ -75,6 +76,7 @@ class AppWindowFrameView : public views::NonClientFrameView, NativeAppWindow* window_; views::Widget* frame_; + SkColor frame_color_; views::ImageButton* close_button_; views::ImageButton* maximize_button_; views::ImageButton* restore_button_; diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index b76c6d1..cf5fcf4 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -1533,7 +1533,7 @@ const Experiment kExperiments[] = { "apps-use-native-frame", IDS_FLAGS_ENABLE_NATIVE_FRAMES_FOR_APPS_NAME, IDS_FLAGS_ENABLE_NATIVE_FRAMES_FOR_APPS_DESCRIPTION, - kOsMac | kOsWin, + kOsMac, SINGLE_VALUE_TYPE(switches::kAppsUseNativeFrame) }, { diff --git a/chrome/browser/apps/app_window_browsertest.cc b/chrome/browser/apps/app_window_browsertest.cc index 234ee3f..468d603 100644 --- a/chrome/browser/apps/app_window_browsertest.cc +++ b/chrome/browser/apps/app_window_browsertest.cc @@ -215,3 +215,15 @@ IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestBadging) { ASSERT_TRUE( RunAppWindowAPITestAndWaitForRoundTrip("testBadging")) << message_; } + +// TODO(benwells): Implement on Mac. +#if defined(USE_AURA) +IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestFrameColors) { + ASSERT_TRUE(RunAppWindowAPITest("testFrameColors")) << message_; +} + +IN_PROC_BROWSER_TEST_F(AppWindowAPITest, TestFrameColorsInStable) { + extensions::ScopedCurrentChannel channel(chrome::VersionInfo::CHANNEL_STABLE); + ASSERT_TRUE(RunAppWindowAPITest("testFrameColorsInStable")) << message_; +} +#endif 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 8015f69..0ff1d3f 100644 --- a/chrome/browser/extensions/api/app_window/app_window_api.cc +++ b/chrome/browser/extensions/api/app_window/app_window_api.cc @@ -9,10 +9,12 @@ #include "apps/app_window_registry.h" #include "apps/ui/native_app_window.h" #include "base/command_line.h" +#include "base/strings/string_number_conversions.h" #include "base/time/time.h" #include "base/values.h" #include "chrome/browser/app_mode/app_mode_utils.h" #include "chrome/browser/devtools/devtools_window.h" +#include "chrome/browser/extensions/api/extension_action/extension_action_api.h" #include "chrome/browser/extensions/window_controller.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/apps/chrome_app_window_delegate.h" @@ -26,6 +28,7 @@ #include "content/public/common/url_constants.h" #include "extensions/browser/extensions_browser_client.h" #include "extensions/common/switches.h" +#include "third_party/skia/include/core/SkColor.h" #include "ui/base/ui_base_types.h" #include "ui/gfx/rect.h" #include "url/gurl.h" @@ -46,9 +49,17 @@ namespace extensions { namespace app_window_constants { const char kInvalidWindowId[] = "The window id can not be more than 256 characters long."; -} +const char kInvalidColorSpecification[] = + "The color specification could not be parsed."; +const char kInvalidChannelForFrameOptions[] = + "frameOptions is only available in dev channel."; +const char kFrameOptionsAndFrame[] = + "Only one of frame and frameOptions can be supplied."; +const char kColorWithFrameNone[] = "Windows with no frame cannot have a color."; +} // namespace app_window_constants const char kNoneFrameOption[] = "none"; + // TODO(benwells): Remove HTML titlebar injection. const char kHtmlFrameOption[] = "experimental-html"; namespace { @@ -84,6 +95,9 @@ class DevToolsRestorer : public base::RefCounted<DevToolsRestorer> { } // namespace +AppWindowCreateFunction::AppWindowCreateFunction() + : inject_html_titlebar_(false) {} + void AppWindowCreateFunction::SendDelayedResponse() { SendResponse(true); } @@ -105,8 +119,6 @@ bool AppWindowCreateFunction::RunImpl() { url = absolute; } - bool inject_html_titlebar = false; - // TODO(jeremya): figure out a way to pass the opening WebContents through to // AppWindow::Create so we can set the opener at create time rather than // with a hack in AppWindowCustomBindings::GetView(). @@ -152,6 +164,7 @@ bool AppWindowCreateFunction::RunImpl() { result->Set("viewId", new base::FundamentalValue(view_id)); window->GetSerializedState(result); result->SetBoolean("existingWindow", true); + // TODO(benwells): Remove HTML titlebar injection. result->SetBoolean("injectTitlebar", false); SetResult(result); SendResponse(true); @@ -199,19 +212,8 @@ bool AppWindowCreateFunction::RunImpl() { } } - if (options->frame.get()) { - if (*options->frame == kHtmlFrameOption && - (GetExtension()->HasAPIPermission(APIPermission::kExperimental) || - CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableExperimentalExtensionApis))) { - create_params.frame = AppWindow::FRAME_NONE; - inject_html_titlebar = true; - } else if (*options->frame == kNoneFrameOption) { - create_params.frame = AppWindow::FRAME_NONE; - } else { - create_params.frame = AppWindow::FRAME_CHROME; - } - } + if (!GetFrameOptions(*options, &create_params)) + return false; if (options->transparent_background.get() && (GetExtension()->HasAPIPermission(APIPermission::kExperimental) || @@ -262,6 +264,8 @@ bool AppWindowCreateFunction::RunImpl() { } } + UpdateFrameOptionsForChannel(&create_params); + create_params.creator_process_id = render_view_host_->GetProcess()->GetID(); @@ -282,7 +286,7 @@ bool AppWindowCreateFunction::RunImpl() { base::DictionaryValue* result = new base::DictionaryValue; result->Set("viewId", new base::FundamentalValue(view_id)); result->Set("injectTitlebar", - new base::FundamentalValue(inject_html_titlebar)); + new base::FundamentalValue(inject_html_titlebar_)); result->Set("id", new base::StringValue(app_window->window_key())); app_window->GetSerializedState(result); SetResult(result); @@ -297,4 +301,74 @@ bool AppWindowCreateFunction::RunImpl() { return true; } +AppWindow::Frame AppWindowCreateFunction::GetFrameFromString( + const std::string& frame_string) { + if (frame_string == kHtmlFrameOption && + (GetExtension()->HasAPIPermission(APIPermission::kExperimental) || + CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableExperimentalExtensionApis))) { + inject_html_titlebar_ = true; + return AppWindow::FRAME_NONE; + } + + if (frame_string == kNoneFrameOption) + return AppWindow::FRAME_NONE; + + return AppWindow::FRAME_CHROME; +} + +bool AppWindowCreateFunction::GetFrameOptions( + const app_window::CreateWindowOptions& options, + AppWindow::CreateParams* create_params) { + if (options.frame.get() && options.frame_options.get()) { + error_ = app_window_constants::kFrameOptionsAndFrame; + return false; + } + + if (options.frame.get()) + create_params->frame = GetFrameFromString(*options.frame); + + if (options.frame_options.get()) { + if (GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV) { + app_window::FrameOptions* frame_options = options.frame_options.get(); + + if (frame_options->type.get()) + create_params->frame = GetFrameFromString(*frame_options->type); + + if (frame_options->color.get()) { + if (create_params->frame != AppWindow::FRAME_CHROME) { + error_ = app_window_constants::kColorWithFrameNone; + return false; + } + + if (ExtensionActionFunction::ParseCSSColorString( + *frame_options->color, + &create_params->frame_color)) { + create_params->has_frame_color = true; + } else { + error_ = app_window_constants::kInvalidColorSpecification; + return false; + } + } + } else { + error_ = app_window_constants::kInvalidChannelForFrameOptions; + return false; + } + } + + return true; +} + +void AppWindowCreateFunction::UpdateFrameOptionsForChannel( + apps::AppWindow::CreateParams* create_params) { + if (create_params->frame == AppWindow::FRAME_CHROME && + GetCurrentChannel() > chrome::VersionInfo::CHANNEL_DEV) { + // If not on trunk or dev channel, always use the standard white frame. + // TODO(benwells): Remove this code once we get agreement to use the new + // native style frame. + create_params->has_frame_color = true; + create_params->frame_color = SK_ColorWHITE; + } +} + } // namespace extensions 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 8aaac544..28c3585 100644 --- a/chrome/browser/extensions/api/app_window/app_window_api.h +++ b/chrome/browser/extensions/api/app_window/app_window_api.h @@ -5,12 +5,20 @@ #ifndef CHROME_BROWSER_EXTENSIONS_API_APP_WINDOW_APP_WINDOW_API_H_ #define CHROME_BROWSER_EXTENSIONS_API_APP_WINDOW_APP_WINDOW_API_H_ +#include "apps/app_window.h" #include "chrome/browser/extensions/chrome_extension_function.h" namespace extensions { +namespace api { +namespace app_window { +struct CreateWindowOptions; +} +} + class AppWindowCreateFunction : public ChromeAsyncExtensionFunction { public: + AppWindowCreateFunction(); DECLARE_EXTENSION_FUNCTION("app.window.create", APP_WINDOW_CREATE) void SendDelayedResponse(); @@ -18,6 +26,16 @@ class AppWindowCreateFunction : public ChromeAsyncExtensionFunction { protected: virtual ~AppWindowCreateFunction() {} virtual bool RunImpl() OVERRIDE; + + private: + bool inject_html_titlebar_; + + apps::AppWindow::Frame GetFrameFromString(const std::string& frame_string); + bool GetFrameOptions( + const extensions::api::app_window::CreateWindowOptions& options, + apps::AppWindow::CreateParams* create_params); + void UpdateFrameOptionsForChannel( + apps::AppWindow::CreateParams* create_params); }; } // namespace extensions diff --git a/chrome/browser/extensions/api/extension_action/extension_action_api.cc b/chrome/browser/extensions/api/extension_action/extension_action_api.cc index 1446db7..a34dc7e 100644 --- a/chrome/browser/extensions/api/extension_action/extension_action_api.cc +++ b/chrome/browser/extensions/api/extension_action/extension_action_api.cc @@ -638,36 +638,31 @@ void ExtensionActionFunction::NotifySystemIndicatorChange() { bool ExtensionActionFunction::ParseCSSColorString( const std::string& color_string, SkColor* result) { - std::string formatted_color = "#"; + std::string formatted_color; // Check the string for incorrect formatting. - if (color_string[0] != '#') + if (color_string.empty() || color_string[0] != '#') return false; // Convert the string from #FFF format to #FFFFFF format. if (color_string.length() == 4) { - for (size_t i = 1; i < color_string.length(); i++) { + for (size_t i = 1; i < 4; ++i) { formatted_color += color_string[i]; formatted_color += color_string[i]; } + } else if (color_string.length() == 7) { + formatted_color = color_string.substr(1, 6); } else { - formatted_color = color_string; - } - - if (formatted_color.length() != 7) return false; + } // Convert the string to an integer and make sure it is in the correct value // range. - int color_ints[3] = {0}; - for (int i = 0; i < 3; i++) { - if (!base::HexStringToInt(formatted_color.substr(1 + (2 * i), 2), - color_ints + i)) - return false; - if (color_ints[i] > 255 || color_ints[i] < 0) - return false; - } + std::vector<uint8> color_bytes; + if (!base::HexStringToBytes(formatted_color, &color_bytes)) + return false; - *result = SkColorSetARGB(255, color_ints[0], color_ints[1], color_ints[2]); + DCHECK_EQ(3u, color_bytes.size()); + *result = SkColorSetARGB(255, color_bytes[0], color_bytes[1], color_bytes[2]); return true; } diff --git a/chrome/browser/extensions/api/extension_action/extension_browser_actions_api_unittest.cc b/chrome/browser/extensions/api/extension_action/extension_browser_actions_api_unittest.cc index 79e99ad..e2d615d 100644 --- a/chrome/browser/extensions/api/extension_action/extension_browser_actions_api_unittest.cc +++ b/chrome/browser/extensions/api/extension_action/extension_browser_actions_api_unittest.cc @@ -49,4 +49,8 @@ TEST(ExtensionBrowserActionsApiTest, ChangeBadgeBackgroundCSSInvalid) { RunFailTest("#-22128"); } +TEST(ExtensionBrowserActionsApiTest, ChangeBadgeBackgroundCSSInvalidWithPlus) { + RunFailTest("#+22128"); +} + } // namespace extensions 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 9f05cc0..ed77dad 100644 --- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h +++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.h @@ -125,6 +125,8 @@ class NativeAppWindowCocoa : public apps::NativeAppWindow, virtual void HandleKeyboardEvent( const content::NativeWebKeyboardEvent& event) OVERRIDE; virtual bool IsFrameless() const OVERRIDE; + virtual bool HasFrameColor() const OVERRIDE; + virtual SkColor FrameColor() const OVERRIDE; virtual gfx::Insets GetFrameInsets() const OVERRIDE; // These are used to simulate Mac-style hide/show. Since windows can be hidden 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 a6a01ad..1fcd0a5 100644 --- a/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm +++ b/chrome/browser/ui/cocoa/apps/native_app_window_cocoa.mm @@ -835,6 +835,16 @@ bool NativeAppWindowCocoa::IsFrameless() const { return !has_frame_; } +bool NativeAppWindowCocoa::HasFrameColor() const { + // TODO(benwells): Implement this. + return false; +} + +SkColor NativeAppWindowCocoa::FrameColor() const { + // TODO(benwells): Implement this. + return SkColor(); +} + gfx::Insets NativeAppWindowCocoa::GetFrameInsets() const { if (!has_frame_) return gfx::Insets(); 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 888f106..f8f52bf 100644 --- a/chrome/browser/ui/gtk/apps/native_app_window_gtk.cc +++ b/chrome/browser/ui/gtk/apps/native_app_window_gtk.cc @@ -668,6 +668,14 @@ bool NativeAppWindowGtk::IsFrameless() const { return frameless_; } +bool NativeAppWindowGtk::HasFrameColor() const { + return false; +} + +SkColor NativeAppWindowGtk::FrameColor() const { + return SkColor(); +} + gfx::Insets NativeAppWindowGtk::GetFrameInsets() const { if (frameless_) return gfx::Insets(); 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 6b4cc65..c324ed8 100644 --- a/chrome/browser/ui/gtk/apps/native_app_window_gtk.h +++ b/chrome/browser/ui/gtk/apps/native_app_window_gtk.h @@ -79,6 +79,8 @@ class NativeAppWindowGtk : public apps::NativeAppWindow, virtual void HandleKeyboardEvent( const content::NativeWebKeyboardEvent& event) OVERRIDE; virtual bool IsFrameless() const OVERRIDE; + virtual bool HasFrameColor() const OVERRIDE; + virtual SkColor FrameColor() const OVERRIDE; virtual gfx::Insets GetFrameInsets() const OVERRIDE; virtual void HideWithApp() OVERRIDE; virtual void ShowWithApp() OVERRIDE; diff --git a/chrome/browser/ui/views/apps/native_app_window_views.cc b/chrome/browser/ui/views/apps/native_app_window_views.cc index e465d1f..eb886b4 100644 --- a/chrome/browser/ui/views/apps/native_app_window_views.cc +++ b/chrome/browser/ui/views/apps/native_app_window_views.cc @@ -211,6 +211,8 @@ void NativeAppWindowViews::Init(apps::AppWindow* app_window, const AppWindow::CreateParams& create_params) { app_window_ = app_window; frameless_ = create_params.frame == AppWindow::FRAME_NONE; + has_frame_color_ = create_params.has_frame_color; + frame_color_ = create_params.frame_color; transparent_background_ = create_params.transparent_background; resizable_ = create_params.resizable; Observe(web_contents()); @@ -232,7 +234,7 @@ void NativeAppWindowViews::Init(apps::AppWindow* app_window, window_->AddObserver(this); #if defined(OS_WIN) - if (ShouldUseChromeStyleFrame() && + if (ShouldUseNativeFrame() && chrome::GetHostDesktopTypeForNativeWindow(window_->GetNativeWindow()) != chrome::HOST_DESKTOP_TYPE_ASH) { InstallEasyResizeTargeterOnContainer(); @@ -255,7 +257,7 @@ void NativeAppWindowViews::InitializeDefaultWindow( views::Widget::InitParams init_params(views::Widget::InitParams::TYPE_WINDOW); init_params.delegate = this; - init_params.remove_standard_frame = ShouldUseChromeStyleFrame(); + init_params.remove_standard_frame = !ShouldUseNativeFrame(); #if defined(OS_LINUX) && !defined(OS_CHROMEOS) // On Linux, remove the standard frame. Instead, we will use CustomFrameView // to draw a native-like frame. @@ -376,17 +378,8 @@ void NativeAppWindowViews::InitializePanelWindow( #endif } -bool NativeAppWindowViews::ShouldUseChromeStyleFrame() const { - if (frameless_) - return true; - -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) - // Linux always uses native style frames. - return false; -#endif - - return !CommandLine::ForCurrentProcess()->HasSwitch( - switches::kAppsUseNativeFrame); +bool NativeAppWindowViews::ShouldUseNativeFrame() const { + return !frameless_ & !has_frame_color_; } void NativeAppWindowViews::InstallEasyResizeTargeterOnContainer() const { @@ -415,6 +408,7 @@ apps::AppWindowFrameView* NativeAppWindowViews::CreateAppWindowFrameView() { #endif apps::AppWindowFrameView* frame_view = new apps::AppWindowFrameView(this); frame_view->Init(window_, + frame_color_, resize_inside_bounds_size, resize_outside_bounds_size, resize_outside_scale_for_touch, @@ -638,7 +632,7 @@ bool NativeAppWindowViews::CanMaximize() const { } base::string16 NativeAppWindowViews::GetWindowTitle() const { - return app_window_->GetTitle(); + return base::string16(); } bool NativeAppWindowViews::ShouldShowWindowTitle() const { @@ -730,7 +724,7 @@ views::NonClientFrameView* NativeAppWindowViews::CreateNonClientFrameView( } } #endif - if (ShouldUseChromeStyleFrame()) + if (!ShouldUseNativeFrame()) return CreateAppWindowFrameView(); return views::WidgetDelegateView::CreateNonClientFrameView(widget); } @@ -972,6 +966,10 @@ bool NativeAppWindowViews::IsFrameless() const { return frameless_; } +bool NativeAppWindowViews::HasFrameColor() const { return has_frame_color_; } + +SkColor NativeAppWindowViews::FrameColor() const { return frame_color_; } + gfx::Insets NativeAppWindowViews::GetFrameInsets() const { if (frameless_) return gfx::Insets(); diff --git a/chrome/browser/ui/views/apps/native_app_window_views.h b/chrome/browser/ui/views/apps/native_app_window_views.h index 3146b6e..f056649 100644 --- a/chrome/browser/ui/views/apps/native_app_window_views.h +++ b/chrome/browser/ui/views/apps/native_app_window_views.h @@ -91,7 +91,7 @@ class NativeAppWindowViews : public apps::NativeAppWindow, void OnViewWasResized(); - bool ShouldUseChromeStyleFrame() const; + bool ShouldUseNativeFrame() const; // Installs an EasyResizeWindowTargeter on the containing window, which // allows the window to be resized from within |kResizeInsideBoundsSize| @@ -188,6 +188,8 @@ class NativeAppWindowViews : public apps::NativeAppWindow, virtual void HandleKeyboardEvent( const content::NativeWebKeyboardEvent& event) OVERRIDE; virtual bool IsFrameless() const OVERRIDE; + virtual bool HasFrameColor() const OVERRIDE; + virtual SkColor FrameColor() const OVERRIDE; virtual gfx::Insets GetFrameInsets() const OVERRIDE; virtual void HideWithApp() OVERRIDE; virtual void ShowWithApp() OVERRIDE; @@ -216,6 +218,8 @@ class NativeAppWindowViews : public apps::NativeAppWindow, scoped_ptr<SkRegion> draggable_region_; bool frameless_; + bool has_frame_color_; + SkColor frame_color_; bool transparent_background_; gfx::Size preferred_size_; bool resizable_; diff --git a/chrome/common/extensions/api/app_window.idl b/chrome/common/extensions/api/app_window.idl index fb13c13..73fec64 100644 --- a/chrome/common/extensions/api/app_window.idl +++ b/chrome/common/extensions/api/app_window.idl @@ -13,6 +13,12 @@ namespace app.window { long? height; }; + // TODO(benwells): document this once we are happy with it. + [nodoc] dictionary FrameOptions { + DOMString? type; + DOMString? color; + }; + // State of a window: normal, fullscreen, maximized, minimized. enum State { normal, fullscreen, maximized, minimized }; @@ -76,6 +82,10 @@ namespace app.window { // to disable this style on nested elements. DOMString? frame; + // TODO(benwells): Fix the IDL parsing so this can be: + // (DOMString or FrameOptions)? frame. + [nodoc] FrameOptions? frameOptions; + // Size and position of the content in the window (excluding the titlebar). // If an id is also specified and a window with a matching id has been shown // before, the remembered bounds of the window will be used instead. @@ -232,6 +242,10 @@ namespace app.window { // Is the window always on top? static boolean isAlwaysOnTop(); + // Accessors for testing. + [nodoc] boolean hasFrameColor; + [nodoc] long frameColor; + // Set whether the window should stay above most other windows. Requires the // <code>"alwaysOnTopWindows"</code> permission. static void setAlwaysOnTop(boolean alwaysOnTop); diff --git a/chrome/renderer/resources/extensions/app_window_custom_bindings.js b/chrome/renderer/resources/extensions/app_window_custom_bindings.js index dd9d8e3..22eb37b 100644 --- a/chrome/renderer/resources/extensions/app_window_custom_bindings.js +++ b/chrome/renderer/resources/extensions/app_window_custom_bindings.js @@ -146,6 +146,16 @@ appWindow.registerCustomHook(function(bindingsAPI) { return appWindowData.id; }}); + // These properties are for testing. + Object.defineProperty( + AppWindow.prototype, 'hasFrameColor', {get: function() { + return appWindowData.hasFrameColor; + }}); + + Object.defineProperty(AppWindow.prototype, 'frameColor', {get: function() { + return appWindowData.frameColor; + }}); + appWindowData = { id: params.id || '', bounds: { left: params.bounds.left, top: params.bounds.top, @@ -157,7 +167,9 @@ appWindow.registerCustomHook(function(bindingsAPI) { fullscreen: params.fullscreen, minimized: params.minimized, maximized: params.maximized, - alwaysOnTop: params.alwaysOnTop + alwaysOnTop: params.alwaysOnTop, + hasFrameColor: params.hasFrameColor, + frameColor: params.frameColor }; currentAppWindow = new AppWindow; }); 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 42f8a40..eb2e86b 100644 --- a/chrome/test/data/extensions/platform_apps/window_api/test.js +++ b/chrome/test/data/extensions/platform_apps/window_api/test.js @@ -3,6 +3,7 @@ // found in the LICENSE file. var callbackPass = chrome.test.callbackPass; +var callbackFail = chrome.test.callbackFail; var defaultFuzzFactor = 1; function assertFuzzyEq(expected, actual, fuzzFactor, message) { @@ -559,6 +560,117 @@ function testBadging() { ]); } +function testFrameColors() { + chrome.test.runTests([ + function testWithNoColor() { + chrome.app.window.create('test.html', callbackPass(function(win) { + chrome.test.assertEq(false, win.hasFrameColor); + win.close(); + })); + }, + + function testWithFrameNone() { + chrome.app.window.create('test.html', { + frame: 'none' + }, + callbackPass(function(win) { + chrome.test.assertEq(false, win.hasFrameColor); + win.close(); + })); + }, + + function testWithBlack() { + chrome.app.window.create('test.html', { + frameOptions: { + type: 'chrome', + color: '#000000' + } + }, + callbackPass(function(win) { + chrome.test.assertEq(true, win.hasFrameColor); + chrome.test.assertEq(-16777216, win.frameColor); + win.close(); + })); + }, + + function testWithWhite() { + chrome.app.window.create('test.html', { + frameOptions: { + color: '#FFFFFF' + } + }, + callbackPass(function(win) { + chrome.test.assertEq(true, win.hasFrameColor); + chrome.test.assertEq(-1, win.frameColor); + win.close(); + })); + }, + + function testWithWhiteShorthand() { + chrome.app.window.create('test.html', { + frameOptions: { + color: '#FFF' + } + }, + callbackPass(function(win) { + chrome.test.assertEq(true, win.hasFrameColor); + chrome.test.assertEq(-1, win.frameColor); + win.close(); + })); + }, + + function testWithFrameAndFrameOptions() { + chrome.app.window.create('test.html', { + frame: 'chrome', + frameOptions: { + color: '#FFF' + } + }, + callbackFail('Only one of frame and frameOptions can be supplied.')); + }, + + function testWithFrameNoneAndColor() { + chrome.app.window.create('test.html', { + frameOptions: { + type: 'none', + color: '#FFF' + } + }, + callbackFail('Windows with no frame cannot have a color.')); + }, + + function testWithInvalidColor() { + chrome.app.window.create('test.html', { + frameOptions: { + color: 'DontWorryBeHappy' + } + }, + callbackFail('The color specification could not be parsed.')); + } + ]); +} + +function testFrameColorsInStable() { + chrome.test.runTests([ + function testWithNoColor() { + chrome.app.window.create('test.html', callbackPass(function(win) { + chrome.test.assertEq(true, win.hasFrameColor); + chrome.test.assertEq(-1, win.frameColor); + win.close(); + })); + }, + + function testWithOptionsGivesError() { + chrome.app.window.create('test.html', { + frameOptions: { + color: '#FFF' + } + }, + callbackFail('frameOptions is only available in dev channel.')); + } + ]); +} + chrome.app.runtime.onLaunched.addListener(function() { chrome.test.sendMessage('Launched', function(reply) { window[reply](); |