diff options
31 files changed, 1088 insertions, 179 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index a6bcd76..1805d05 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -11786,6 +11786,11 @@ Some features may be unavailable. Please check that the profile exists and you </message> </if> + <!-- Color chooser dialog default titles (only used on Linux) --> + <message name="IDS_SELECT_COLOR_DIALOG_TITLE" desc="The default title for the Select Color color chooser dialog."> + Select Color + </message> + <!-- File chooser dialog default titles (only used on Linux) --> <message name="IDS_SELECT_FOLDER_DIALOG_TITLE" desc="The default title for the Select Folder file chooser dialog."> Select Folder diff --git a/chrome/browser/ui/android/color_chooser_android.cc b/chrome/browser/ui/android/color_chooser_android.cc new file mode 100644 index 0000000..b108796 --- /dev/null +++ b/chrome/browser/ui/android/color_chooser_android.cc @@ -0,0 +1,14 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/public/browser/color_chooser.h" + +#include "base/logging.h" + +// static +content::ColorChooser* content::ColorChooser::Create( + int identifier, content::WebContents* tab, SkColor initial_color) { + NOTIMPLEMENTED(); + return NULL; +} diff --git a/chrome/browser/ui/base_shell_dialog.h b/chrome/browser/ui/base_shell_dialog.h new file mode 100644 index 0000000..4a5a99c --- /dev/null +++ b/chrome/browser/ui/base_shell_dialog.h @@ -0,0 +1,26 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_BASE_SHELL_DIALOG_H_ +#define CHROME_BROWSER_UI_BASE_SHELL_DIALOG_H_ +#pragma once + +#include "ui/gfx/native_widget_types.h" + +// A base class for shell dialogs. +class BaseShellDialog { + public: + // Returns true if a shell dialog box is currently being shown modally + // to the specified owner. + virtual bool IsRunning(gfx::NativeWindow owning_window) const = 0; + + // Notifies the dialog box that the listener has been destroyed and it should + // no longer be sent notifications. + virtual void ListenerDestroyed() = 0; + + protected: + virtual ~BaseShellDialog() {} +}; + +#endif // CHROME_BROWSER_UI_BASE_SHELL_DIALOG_H_ diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index a229eca..7bbb85d 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc @@ -149,6 +149,7 @@ #include "chrome/common/profiling.h" #include "chrome/common/url_constants.h" #include "chrome/common/web_apps.h" +#include "content/public/browser/color_chooser.h" #include "content/public/browser/devtools_manager.h" #include "content/public/browser/download_item.h" #include "content/public/browser/download_manager.h" @@ -4136,6 +4137,28 @@ content::JavaScriptDialogCreator* Browser::GetJavaScriptDialogCreator() { return GetJavaScriptDialogCreatorInstance(); } +content::ColorChooser* Browser::OpenColorChooser(WebContents* tab, + int color_chooser_id, + const SkColor& color) { +#if defined(OS_WIN) + // On Windows, only create a color chooser if one doesn't exist, because we + // can't close the old color chooser dialog. + if (!color_chooser_.get()) + color_chooser_.reset(content::ColorChooser::Create(color_chooser_id, tab, + color)); +#else + if (color_chooser_.get()) + color_chooser_->End(); + color_chooser_.reset(content::ColorChooser::Create(color_chooser_id, tab, + color)); +#endif + return color_chooser_.get(); +} + +void Browser::DidEndColorChooser() { + color_chooser_.reset(); +} + void Browser::RunFileChooser(WebContents* tab, const content::FileChooserParams& params) { RunFileChooserHelper(tab, params); diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h index 67755e6..c32d58c 100644 --- a/chrome/browser/ui/browser.h +++ b/chrome/browser/ui/browser.h @@ -1024,6 +1024,11 @@ class Browser : public TabHandlerDelegate, virtual void DidNavigateToPendingEntry(content::WebContents* tab) OVERRIDE; virtual content::JavaScriptDialogCreator* GetJavaScriptDialogCreator() OVERRIDE; + virtual content::ColorChooser* OpenColorChooser( + content::WebContents* tab, + int color_chooser_id, + const SkColor& color) OVERRIDE; + virtual void DidEndColorChooser() OVERRIDE; virtual void RunFileChooser( content::WebContents* tab, const content::FileChooserParams& params) OVERRIDE; @@ -1474,6 +1479,10 @@ class Browser : public TabHandlerDelegate, // True if the browser window has been shown at least once. bool window_has_shown_; + // Currently open color chooser. Non-NULL after OpenColorChooser is called and + // before DidEndColorChooser is called. + scoped_ptr<content::ColorChooser> color_chooser_; + DISALLOW_COPY_AND_ASSIGN(Browser); }; diff --git a/chrome/browser/ui/cocoa/color_chooser_mac.mm b/chrome/browser/ui/cocoa/color_chooser_mac.mm new file mode 100644 index 0000000..bf050a6 --- /dev/null +++ b/chrome/browser/ui/cocoa/color_chooser_mac.mm @@ -0,0 +1,138 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/public/browser/color_chooser.h" + +#import <Cocoa/Cocoa.h> + +#import "base/mac/cocoa_protocols.h" +#import "base/memory/scoped_nsobject.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_observer.h" +#include "skia/ext/skia_utils_mac.h" + +class ColorChooserMac; + +// A Listener class to act as a event target for NSColorPanel and send +// the results to the C++ class, ColorChooserMac. +@interface ColorPanelCocoa : NSObject<NSWindowDelegate> { + @private + // We don't call DidChooseColor if the change wasn't caused by the user + // interacting with the panel. + BOOL nonUserChange_; + ColorChooserMac* chooser_; // weak, owns this +} + +- (id)initWithChooser:(ColorChooserMac*)chooser; + +// Called from NSColorPanel. +- (void)didChooseColor:(NSColorPanel*)panel; + +// Sets color to the NSColorPanel as a non user change. +- (void)setColor:(NSColor*)color; + +@end + +class ColorChooserMac : public content::ColorChooser, + public content::WebContentsObserver { + public: + ColorChooserMac( + int identifier, content::WebContents* tab, SkColor initial_color); + virtual ~ColorChooserMac(); + + // Called from ColorPanelCocoa. + void DidChooseColor(SkColor color); + void DidClose(); + + virtual void End() OVERRIDE; + virtual void SetSelectedColor(SkColor color) OVERRIDE; + + private: + scoped_nsobject<ColorPanelCocoa> panel_; +}; + +content::ColorChooser* content::ColorChooser::Create( + int identifier, content::WebContents* tab, SkColor initial_color) { + return new ColorChooserMac(identifier, tab, initial_color); +} + +ColorChooserMac::ColorChooserMac( + int identifier, content::WebContents* tab, SkColor initial_color) + : content::ColorChooser(identifier), + content::WebContentsObserver(tab) { + panel_.reset([[ColorPanelCocoa alloc] initWithChooser:this]); + [panel_ setColor:gfx::SkColorToDeviceNSColor(initial_color)]; + [[NSColorPanel sharedColorPanel] makeKeyAndOrderFront:nil]; +} + +ColorChooserMac::~ColorChooserMac() { + // Always call End() before destroying. + DCHECK(!panel_); +} + +void ColorChooserMac::DidChooseColor(SkColor color) { + if (web_contents()) + web_contents()->DidChooseColorInColorChooser(identifier(), color); +} + +void ColorChooserMac::DidClose() { + End(); +} + +void ColorChooserMac::End() { + panel_.reset(); + if (web_contents()) + web_contents()->DidEndColorChooser(identifier()); +} + +void ColorChooserMac::SetSelectedColor(SkColor color) { + [panel_ setColor:gfx::SkColorToDeviceNSColor(color)]; +} + +@implementation ColorPanelCocoa + +- (id)initWithChooser:(ColorChooserMac*)chooser { + if ((self = [super init])) { + chooser_ = chooser; + NSColorPanel* panel = [NSColorPanel sharedColorPanel]; + [panel setShowsAlpha:NO]; + [panel setDelegate:self]; + [panel setTarget:self]; + [panel setAction:@selector(didChooseColor:)]; + } + return self; +} + +- (void)dealloc { + NSColorPanel* panel = [NSColorPanel sharedColorPanel]; + if ([panel delegate] == self) { + [panel setDelegate:nil]; + [panel setTarget:nil]; + [panel setAction:nil]; + } + + [super dealloc]; +} + +- (void)windowWillClose:(NSNotification*)notification { + chooser_->DidClose(); + nonUserChange_ = NO; +} + +- (void)didChooseColor:(NSColorPanel*)panel { + if (nonUserChange_) { + nonUserChange_ = NO; + return; + } + chooser_->DidChooseColor(gfx::NSDeviceColorToSkColor( + [[panel color] colorUsingColorSpaceName:NSDeviceRGBColorSpace])); + nonUserChange_ = NO; +} + +- (void)setColor:(NSColor*)color { + nonUserChange_ = YES; + [[NSColorPanel sharedColorPanel] setColor:color]; +} + +@end diff --git a/chrome/browser/ui/gtk/color_chooser_gtk.cc b/chrome/browser/ui/gtk/color_chooser_gtk.cc new file mode 100644 index 0000000..8b9384fe --- /dev/null +++ b/chrome/browser/ui/gtk/color_chooser_gtk.cc @@ -0,0 +1,114 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <gtk/gtk.h> + +#include "content/public/browser/color_chooser.h" + +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_observer.h" +#include "grit/generated_resources.h" +#include "ui/base/gtk/gtk_signal.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/gfx/skia_utils_gtk.h" + +class ColorChooserGtk : public content::ColorChooser, + public content::WebContentsObserver { + public: + ColorChooserGtk( + int identifier, content::WebContents* tab, SkColor initial_color); + virtual ~ColorChooserGtk(); + + virtual void End() OVERRIDE; + virtual void SetSelectedColor(SkColor color) OVERRIDE; + + private: + CHROMEGTK_CALLBACK_0(ColorChooserGtk, void, OnColorChooserOk); + CHROMEGTK_CALLBACK_0(ColorChooserGtk, void, OnColorChooserCancel); + CHROMEGTK_CALLBACK_0(ColorChooserGtk, void, OnColorChooserDestroy); + + GtkWidget* color_selection_dialog_; +}; + +content::ColorChooser* content::ColorChooser::Create( + int identifier, content::WebContents* tab, SkColor initial_color) { + return new ColorChooserGtk(identifier, tab, initial_color); +} + +ColorChooserGtk::ColorChooserGtk( + int identifier, content::WebContents* tab, SkColor initial_color) + : content::ColorChooser(identifier), + content::WebContentsObserver(tab) { + color_selection_dialog_ = gtk_color_selection_dialog_new( + l10n_util::GetStringUTF8(IDS_SELECT_COLOR_DIALOG_TITLE).c_str()); + GtkWidget* cancel_button; + GtkColorSelection* color_selection; + GtkWidget* ok_button; + g_object_get(color_selection_dialog_, + "cancel-button", &cancel_button, + "color-selection", &color_selection, + "ok-button", &ok_button, + NULL); + gtk_color_selection_set_has_opacity_control(color_selection, FALSE); + g_signal_connect(ok_button, "clicked", + G_CALLBACK(OnColorChooserOkThunk), this); + g_signal_connect(cancel_button, "clicked", + G_CALLBACK(OnColorChooserCancelThunk), this); + g_signal_connect(color_selection_dialog_, "destroy", + G_CALLBACK(OnColorChooserDestroyThunk), this); + GdkColor gdk_color = gfx::SkColorToGdkColor(initial_color); + gtk_color_selection_set_previous_color(color_selection, &gdk_color); + gtk_color_selection_set_current_color(color_selection, &gdk_color); + gtk_window_present(GTK_WINDOW(color_selection_dialog_)); + g_object_unref(cancel_button); + g_object_unref(color_selection); + g_object_unref(ok_button); +} + +ColorChooserGtk::~ColorChooserGtk() { + // Always call End() before destroying. + DCHECK(!color_selection_dialog_); +} + +void ColorChooserGtk::OnColorChooserOk(GtkWidget* widget) { + GdkColor color; + GtkColorSelection* color_selection; + g_object_get(color_selection_dialog_, + "color-selection", &color_selection, NULL); + gtk_color_selection_get_current_color(color_selection, &color); + web_contents()->DidChooseColorInColorChooser(identifier(), + gfx::GdkColorToSkColor(color)); + g_object_unref(color_selection); + gtk_widget_destroy(color_selection_dialog_); +} + +void ColorChooserGtk::OnColorChooserCancel(GtkWidget* widget) { + gtk_widget_destroy(color_selection_dialog_); +} + +void ColorChooserGtk::OnColorChooserDestroy(GtkWidget* widget) { + color_selection_dialog_ = NULL; + if (web_contents()) + web_contents()->DidEndColorChooser(identifier()); +} + +void ColorChooserGtk::End() { + if (!color_selection_dialog_) + return; + + gtk_widget_destroy(color_selection_dialog_); +} + +void ColorChooserGtk::SetSelectedColor(SkColor color) { + if (!color_selection_dialog_) + return; + + GdkColor gdk_color = gfx::SkColorToGdkColor(color); + GtkColorSelection* color_selection; + g_object_get(color_selection_dialog_, + "color-selection", &color_selection, NULL); + gtk_color_selection_set_previous_color(color_selection, &gdk_color); + gtk_color_selection_set_current_color(color_selection, &gdk_color); + g_object_unref(color_selection); +} diff --git a/chrome/browser/ui/select_file_dialog.h b/chrome/browser/ui/select_file_dialog.h index 17b96be..6c0b5f3 100644 --- a/chrome/browser/ui/select_file_dialog.h +++ b/chrome/browser/ui/select_file_dialog.h @@ -12,6 +12,7 @@ #include "base/file_path.h" #include "base/memory/ref_counted.h" #include "base/string16.h" +#include "chrome/browser/ui/base_shell_dialog.h" #include "ui/gfx/native_widget_types.h" namespace content { @@ -25,21 +26,6 @@ extern std::wstring AppendExtensionIfNeeded(const std::wstring& filename, const std::wstring& filter_selected, const std::wstring& suggested_ext); -// A base class for shell dialogs. -class BaseShellDialog { - public: - // Returns true if a shell dialog box is currently being shown modally - // to the specified owner. - virtual bool IsRunning(gfx::NativeWindow owning_window) const = 0; - - // Notifies the dialog box that the listener has been destroyed and it should - // no longer be sent notifications. - virtual void ListenerDestroyed() = 0; - - protected: - virtual ~BaseShellDialog() {} -}; - // Shows a dialog box for selecting a file or a folder. class SelectFileDialog : public base::RefCountedThreadSafe<SelectFileDialog>, diff --git a/chrome/browser/ui/views/ash/color_chooser_aura.cc b/chrome/browser/ui/views/ash/color_chooser_aura.cc new file mode 100644 index 0000000..b108796 --- /dev/null +++ b/chrome/browser/ui/views/ash/color_chooser_aura.cc @@ -0,0 +1,14 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/public/browser/color_chooser.h" + +#include "base/logging.h" + +// static +content::ColorChooser* content::ColorChooser::Create( + int identifier, content::WebContents* tab, SkColor initial_color) { + NOTIMPLEMENTED(); + return NULL; +} diff --git a/chrome/browser/ui/views/base_shell_dialog_win.cc b/chrome/browser/ui/views/base_shell_dialog_win.cc new file mode 100644 index 0000000..ffb77ea --- /dev/null +++ b/chrome/browser/ui/views/base_shell_dialog_win.cc @@ -0,0 +1,104 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/views/base_shell_dialog_win.h" + +#include <algorithm> + +#include "base/threading/thread.h" + +// Helpers to show certain types of Windows shell dialogs in a way that doesn't +// block the UI of the entire app. + +class ShellDialogThread : public base::Thread { + public: + ShellDialogThread() : base::Thread("Chrome_ShellDialogThread") { } + ~ShellDialogThread(); + + protected: + void Init(); + + void CleanUp(); + + private: + DISALLOW_COPY_AND_ASSIGN(ShellDialogThread); +}; + +ShellDialogThread::~ShellDialogThread() { + Stop(); +} + +void ShellDialogThread::Init() { + // Initializes the COM library on the current thread. + CoInitialize(NULL); +} + +void ShellDialogThread::CleanUp() { + // Closes the COM library on the current thread. CoInitialize must + // be balanced by a corresponding call to CoUninitialize. + CoUninitialize(); +} + +// static +BaseShellDialogImpl::Owners BaseShellDialogImpl::owners_; +int BaseShellDialogImpl::instance_count_ = 0; + +BaseShellDialogImpl::BaseShellDialogImpl() { + ++instance_count_; +} + +BaseShellDialogImpl::~BaseShellDialogImpl() { + // All runs should be complete by the time this is called! + if (--instance_count_ == 0) + DCHECK(owners_.empty()); +} + +BaseShellDialogImpl::RunState BaseShellDialogImpl::BeginRun(HWND owner) { + // Cannot run a modal shell dialog if one is already running for this owner. + DCHECK(!IsRunningDialogForOwner(owner)); + // The owner must be a top level window, otherwise we could end up with two + // entries in our map for the same top level window. + DCHECK(!owner || owner == GetAncestor(owner, GA_ROOT)); + RunState run_state; + run_state.dialog_thread = CreateDialogThread(); + run_state.owner = owner; + if (owner) { + owners_.insert(owner); + DisableOwner(owner); + } + return run_state; +} + +void BaseShellDialogImpl::EndRun(RunState run_state) { + if (run_state.owner) { + DCHECK(IsRunningDialogForOwner(run_state.owner)); + EnableOwner(run_state.owner); + DCHECK(owners_.find(run_state.owner) != owners_.end()); + owners_.erase(run_state.owner); + } + DCHECK(run_state.dialog_thread); + delete run_state.dialog_thread; +} + +bool BaseShellDialogImpl::IsRunningDialogForOwner(HWND owner) const { + return (owner && owners_.find(owner) != owners_.end()); +} + +void BaseShellDialogImpl::DisableOwner(HWND owner) { + if (IsWindow(owner)) + EnableWindow(owner, FALSE); +} + +// static +base::Thread* BaseShellDialogImpl::CreateDialogThread() { + base::Thread* thread = new ShellDialogThread; + bool started = thread->Start(); + DCHECK(started); + return thread; +} + +void BaseShellDialogImpl::EnableOwner(HWND owner) { + if (IsWindow(owner)) + EnableWindow(owner, TRUE); +} diff --git a/chrome/browser/ui/views/base_shell_dialog_win.h b/chrome/browser/ui/views/base_shell_dialog_win.h new file mode 100644 index 0000000..fb84c28 --- /dev/null +++ b/chrome/browser/ui/views/base_shell_dialog_win.h @@ -0,0 +1,93 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_VIEWS_BASE_SHELL_DIALOG_WIN_H_ +#define CHROME_BROWSER_UI_VIEWS_BASE_SHELL_DIALOG_WIN_H_ +#pragma once + +#include <shlobj.h> +#include <set> + +#include "chrome/browser/ui/base_shell_dialog.h" + +namespace base { +class Thread; +} + +/////////////////////////////////////////////////////////////////////////////// +// A base class for all shell dialog implementations that handles showing a +// shell dialog modally on its own thread. +class BaseShellDialogImpl { + public: + BaseShellDialogImpl(); + virtual ~BaseShellDialogImpl(); + + protected: + // Represents a run of a dialog. + struct RunState { + // Owning HWND, may be null. + HWND owner; + + // Thread dialog is run on. + base::Thread* dialog_thread; + }; + + // Called at the beginning of a modal dialog run. Disables the owner window + // and tracks it. Returns the message loop of the thread that the dialog will + // be run on. + RunState BeginRun(HWND owner); + + // Cleans up after a dialog run. If the run_state has a valid HWND this makes + // sure that the window is enabled. This is essential because BeginRun + // aggressively guards against multiple modal dialogs per HWND. Must be called + // on the UI thread after the result of the dialog has been determined. + // + // In addition this deletes the Thread in RunState. + void EndRun(RunState run_state); + + // Returns true if a modal shell dialog is currently active for the specified + // owner. Must be called on the UI thread. + bool IsRunningDialogForOwner(HWND owner) const; + + // Disables the window |owner|. Can be run from either the ui or the dialog + // thread. Can be called on either the UI or the dialog thread. This function + // is called on the dialog thread after the modal Windows Common dialog + // functions return because Windows automatically re-enables the owning + // window when those functions return, but we don't actually want them to be + // re-enabled until the response of the dialog propagates back to the UI + // thread, so we disable the owner manually after the Common dialog function + // returns. + void DisableOwner(HWND owner); + + private: + typedef std::set<HWND> Owners; + + // Creates a thread to run a shell dialog on. Each dialog requires its own + // thread otherwise in some situations where a singleton owns a single + // instance of this object we can have a situation where a modal dialog in + // one window blocks the appearance of a modal dialog in another. + static base::Thread* CreateDialogThread(); + + // Enables the window |owner_|. Can only be run from the ui thread. + void EnableOwner(HWND owner); + + // A list of windows that currently own active shell dialogs for this + // instance. For example, if the DownloadManager owns an instance of this + // object and there are two browser windows open both with Save As dialog + // boxes active, this list will consist of the two browser windows' HWNDs. + // The derived class must call EndRun once the dialog is done showing to + // remove the owning HWND from this list. + // This object is static since it is maintained for all instances of this + // object - i.e. you can't have two file pickers open for the + // same owner, even though they might be represented by different instances + // of this object. + // This set only contains non-null HWNDs. NULL hwnds are not added to this + // list. + static Owners owners_; + static int instance_count_; + + DISALLOW_COPY_AND_ASSIGN(BaseShellDialogImpl); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_BASE_SHELL_DIALOG_WIN_H_ diff --git a/chrome/browser/ui/views/color_chooser_dialog.cc b/chrome/browser/ui/views/color_chooser_dialog.cc new file mode 100644 index 0000000..1a3c129 --- /dev/null +++ b/chrome/browser/ui/views/color_chooser_dialog.cc @@ -0,0 +1,81 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/views/color_chooser_dialog.h" + +#include <commdlg.h> + +#include "base/bind.h" +#include "base/message_loop.h" +#include "base/threading/thread.h" +#include "content/public/browser/browser_thread.h" +#include "skia/ext/skia_utils_win.h" + +using content::BrowserThread; + +// static +COLORREF ColorChooserDialog::g_custom_colors[16]; + +ColorChooserDialog::ExecuteOpenParams::ExecuteOpenParams(SkColor color, + RunState run_state, + HWND owner) + : color(color), + run_state(run_state), + owner(owner) { +} + +ColorChooserDialog::ColorChooserDialog(Listener* listener, + SkColor initial_color, + gfx::NativeWindow owning_window) + : listener_(listener) { + DCHECK(listener_); + CopyCustomColors(g_custom_colors, custom_colors_); + ExecuteOpenParams execute_params(initial_color, BeginRun(owning_window), + owning_window); + execute_params.run_state.dialog_thread->message_loop()->PostTask(FROM_HERE, + base::Bind(&ColorChooserDialog::ExecuteOpen, this, execute_params)); +} + +ColorChooserDialog::~ColorChooserDialog() { +} + +bool ColorChooserDialog::IsRunning(HWND owning_hwnd) const { + return listener_ && IsRunningDialogForOwner(owning_hwnd); +} + +void ColorChooserDialog::ListenerDestroyed() { + // Our associated listener has gone away, so we shouldn't call back to it if + // our worker thread returns after the listener is dead. + listener_ = NULL; +} + +void ColorChooserDialog::ExecuteOpen(const ExecuteOpenParams& params) { + CHOOSECOLOR cc; + cc.lStructSize = sizeof(CHOOSECOLOR); + cc.hwndOwner = params.owner; + cc.rgbResult = skia::SkColorToCOLORREF(params.color); + cc.lpCustColors = custom_colors_; + cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT; + bool success = !!ChooseColor(&cc); + DisableOwner(cc.hwndOwner); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&ColorChooserDialog::DidCloseDialog, this, success, + skia::COLORREFToSkColor(cc.rgbResult), params.run_state)); +} + +void ColorChooserDialog::DidCloseDialog(bool chose_color, + SkColor color, + RunState run_state) { + if (!listener_) + return; + EndRun(run_state); + CopyCustomColors(custom_colors_, g_custom_colors); + if (chose_color) + listener_->DidChooseColor(color); + listener_->DidEnd(); +} + +void ColorChooserDialog::CopyCustomColors(COLORREF* src, COLORREF* dst) { + memcpy(dst, src, sizeof(COLORREF) * arraysize(g_custom_colors)); +} diff --git a/chrome/browser/ui/views/color_chooser_dialog.h b/chrome/browser/ui/views/color_chooser_dialog.h new file mode 100644 index 0000000..8eef9ba --- /dev/null +++ b/chrome/browser/ui/views/color_chooser_dialog.h @@ -0,0 +1,79 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_VIEWS_COLOR_CHOOSER_DIALOG_H_ +#define CHROME_BROWSER_UI_VIEWS_COLOR_CHOOSER_DIALOG_H_ +#pragma once + +#include "base/memory/ref_counted.h" +#include "chrome/browser/ui/base_shell_dialog.h" +#include "chrome/browser/ui/views/color_chooser_dialog.h" +#include "chrome/browser/ui/views/base_shell_dialog_win.h" +#include "third_party/skia/include/core/SkColor.h" + +class ColorChooserDialog + : public base::RefCountedThreadSafe<ColorChooserDialog>, + public BaseShellDialog, + public BaseShellDialogImpl { + public: + // An interface implemented by a Listener object wishing to know about the + // the results from the color chooser dialog. + class Listener { + public: + virtual ~Listener() {} + virtual void DidChooseColor(SkColor color) = 0; + virtual void DidEnd() = 0; + }; + + ColorChooserDialog(Listener* listener, + SkColor initial_color, + gfx::NativeWindow owning_window); + virtual ~ColorChooserDialog(); + + // BaseShellDialog: + virtual bool IsRunning(HWND owning_hwnd) const OVERRIDE; + virtual void ListenerDestroyed() OVERRIDE; + + private: + struct ExecuteOpenParams { + ExecuteOpenParams(SkColor color, RunState run_state, HWND owner); + SkColor color; + RunState run_state; + HWND owner; + }; + + // Called on the dialog thread to show the actual color chooser. This is + // shown modal to |params.owner|. Once it's closed, calls back to + // DidCloseDialog() on the UI thread. + void ExecuteOpen(const ExecuteOpenParams& params); + + // Called on the UI thread when a color chooser is closed. |chose_color| is + // true if the user actually chose a color, in which case |color| is the + // chosen color. Calls back to the |listener_| (if applicable) to notify it + // of the results, and copies the modified array of |custom_colors_| back to + // |g_custom_colors| so future dialogs will see the changes. + void DidCloseDialog(bool chose_color, SkColor color, RunState run_state); + + // Copies the array of colors in |src| to |dst|. + void CopyCustomColors(COLORREF*, COLORREF*); + + // The user's custom colors. Kept process-wide so that they can be persisted + // from one dialog invocation to the next. + static COLORREF g_custom_colors[16]; + + // A copy of the custom colors for the current dialog to display and modify. + // This allows us to safely access the colors even if multiple windows are + // simultaneously showing color choosers (which would cause thread safety + // problems if we gave them direct handles to |g_custom_colors|). + COLORREF custom_colors_[16]; + + // The listener to notify when the user closes the dialog. This may be set to + // NULL before the color chooser is closed, signalling that the listener no + // longer cares about the outcome. + Listener* listener_; + + DISALLOW_COPY_AND_ASSIGN(ColorChooserDialog); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_COLOR_CHOOSER_DIALOG_H_ diff --git a/chrome/browser/ui/views/color_chooser_win.cc b/chrome/browser/ui/views/color_chooser_win.cc new file mode 100644 index 0000000..04fc540 --- /dev/null +++ b/chrome/browser/ui/views/color_chooser_win.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <windows.h> + +#include "chrome/browser/platform_util.h" +#include "chrome/browser/ui/views/color_chooser_dialog.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/color_chooser.h" +#include "content/public/browser/render_widget_host_view.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_observer.h" + +class ColorChooserWin : public content::ColorChooser, + public ColorChooserDialog::Listener, + public content::WebContentsObserver { + public: + ColorChooserWin(int identifier, + content::WebContents* tab, + SkColor initial_color); + ~ColorChooserWin(); + + // content::ColorChooser: + virtual void End() OVERRIDE {} + virtual void SetSelectedColor(SkColor color) OVERRIDE {} + + // ColorChooserDialog::Listener: + virtual void DidChooseColor(SkColor color); + virtual void DidEnd(); + + private: + scoped_refptr<ColorChooserDialog> color_chooser_dialog_; +}; + +content::ColorChooser* content::ColorChooser::Create(int identifier, + content::WebContents* tab, + SkColor initial_color) { + return new ColorChooserWin(identifier, tab, initial_color); +} + +ColorChooserWin::ColorChooserWin(int identifier, + content::WebContents* tab, + SkColor initial_color) + : content::ColorChooser(identifier), + content::WebContentsObserver(tab) { + gfx::NativeWindow owning_window = platform_util::GetTopLevel( + web_contents()->GetRenderViewHost()->GetView()->GetNativeView()); + color_chooser_dialog_ = new ColorChooserDialog(this, + initial_color, + owning_window); +} + +ColorChooserWin::~ColorChooserWin() { + // Always call End() before destroying. + DCHECK(!color_chooser_dialog_); +} + +void ColorChooserWin::DidChooseColor(SkColor color) { + if (web_contents()) + web_contents()->DidChooseColorInColorChooser(identifier(), color); +} + +void ColorChooserWin::DidEnd() { + if (color_chooser_dialog_.get()) { + color_chooser_dialog_->ListenerDestroyed(); + color_chooser_dialog_ = NULL; + } + if (web_contents()) + web_contents()->DidEndColorChooser(identifier()); +} diff --git a/chrome/browser/ui/views/select_file_dialog_win.cc b/chrome/browser/ui/views/select_file_dialog_win.cc index 5414b42..80dedf2 100644 --- a/chrome/browser/ui/views/select_file_dialog_win.cc +++ b/chrome/browser/ui/views/select_file_dialog_win.cc @@ -22,6 +22,7 @@ #include "base/win/registry.h" #include "base/win/scoped_comptr.h" #include "base/win/windows_version.h" +#include "chrome/browser/ui/views/base_shell_dialog_win.h" #include "content/public/browser/browser_thread.h" #include "grit/generated_resources.h" #include "grit/ui_strings.h" @@ -389,169 +390,6 @@ bool SaveFileAs(HWND owner, } // namespace -// Helpers to show certain types of Windows shell dialogs in a way that doesn't -// block the UI of the entire app. - -class ShellDialogThread : public base::Thread { - public: - ShellDialogThread() : base::Thread("Chrome_ShellDialogThread") { } - ~ShellDialogThread() { - Stop(); - } - - protected: - void Init() { - // Initializes the COM library on the current thread. - CoInitialize(NULL); - } - - void CleanUp() { - // Closes the COM library on the current thread. CoInitialize must - // be balanced by a corresponding call to CoUninitialize. - CoUninitialize(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(ShellDialogThread); -}; - -/////////////////////////////////////////////////////////////////////////////// -// A base class for all shell dialog implementations that handles showing a -// shell dialog modally on its own thread. -class BaseShellDialogImpl { - public: - BaseShellDialogImpl(); - virtual ~BaseShellDialogImpl(); - - protected: - // Represents a run of a dialog. - struct RunState { - // Owning HWND, may be null. - HWND owner; - - // Thread dialog is run on. - base::Thread* dialog_thread; - }; - - // Called at the beginning of a modal dialog run. Disables the owner window - // and tracks it. Returns the message loop of the thread that the dialog will - // be run on. - RunState BeginRun(HWND owner); - - // Cleans up after a dialog run. If the run_state has a valid HWND this makes - // sure that the window is enabled. This is essential because BeginRun - // aggressively guards against multiple modal dialogs per HWND. Must be called - // on the UI thread after the result of the dialog has been determined. - // - // In addition this deletes the Thread in RunState. - void EndRun(RunState run_state); - - // Returns true if a modal shell dialog is currently active for the specified - // owner. Must be called on the UI thread. - bool IsRunningDialogForOwner(HWND owner) const; - - // Disables the window |owner|. Can be run from either the ui or the dialog - // thread. Can be called on either the UI or the dialog thread. This function - // is called on the dialog thread after the modal Windows Common dialog - // functions return because Windows automatically re-enables the owning - // window when those functions return, but we don't actually want them to be - // re-enabled until the response of the dialog propagates back to the UI - // thread, so we disable the owner manually after the Common dialog function - // returns. - void DisableOwner(HWND owner); - - private: - // Creates a thread to run a shell dialog on. Each dialog requires its own - // thread otherwise in some situations where a singleton owns a single - // instance of this object we can have a situation where a modal dialog in - // one window blocks the appearance of a modal dialog in another. - static base::Thread* CreateDialogThread(); - - // Enables the window |owner_|. Can only be run from the ui thread. - void EnableOwner(HWND owner); - - // A list of windows that currently own active shell dialogs for this - // instance. For example, if the DownloadManager owns an instance of this - // object and there are two browser windows open both with Save As dialog - // boxes active, this list will consist of the two browser windows' HWNDs. - // The derived class must call EndRun once the dialog is done showing to - // remove the owning HWND from this list. - // This object is static since it is maintained for all instances of this - // object - i.e. you can't have two file pickers open for the - // same owner, even though they might be represented by different instances - // of this object. - // This set only contains non-null HWNDs. NULL hwnds are not added to this - // list. - typedef std::set<HWND> Owners; - static Owners owners_; - static int instance_count_; - - DISALLOW_COPY_AND_ASSIGN(BaseShellDialogImpl); -}; - -// static -BaseShellDialogImpl::Owners BaseShellDialogImpl::owners_; -int BaseShellDialogImpl::instance_count_ = 0; - -BaseShellDialogImpl::BaseShellDialogImpl() { - ++instance_count_; -} - -BaseShellDialogImpl::~BaseShellDialogImpl() { - // All runs should be complete by the time this is called! - if (--instance_count_ == 0) - DCHECK(owners_.empty()); -} - -BaseShellDialogImpl::RunState BaseShellDialogImpl::BeginRun(HWND owner) { - // Cannot run a modal shell dialog if one is already running for this owner. - DCHECK(!IsRunningDialogForOwner(owner)); - // The owner must be a top level window, otherwise we could end up with two - // entries in our map for the same top level window. - DCHECK(!owner || owner == GetAncestor(owner, GA_ROOT)); - RunState run_state; - run_state.dialog_thread = CreateDialogThread(); - run_state.owner = owner; - if (owner) { - owners_.insert(owner); - DisableOwner(owner); - } - return run_state; -} - -void BaseShellDialogImpl::EndRun(RunState run_state) { - if (run_state.owner) { - DCHECK(IsRunningDialogForOwner(run_state.owner)); - EnableOwner(run_state.owner); - DCHECK(owners_.find(run_state.owner) != owners_.end()); - owners_.erase(run_state.owner); - } - DCHECK(run_state.dialog_thread); - delete run_state.dialog_thread; -} - -bool BaseShellDialogImpl::IsRunningDialogForOwner(HWND owner) const { - return (owner && owners_.find(owner) != owners_.end()); -} - -void BaseShellDialogImpl::DisableOwner(HWND owner) { - if (IsWindow(owner)) - EnableWindow(owner, FALSE); -} - -// static -base::Thread* BaseShellDialogImpl::CreateDialogThread() { - base::Thread* thread = new ShellDialogThread; - bool started = thread->Start(); - DCHECK(started); - return thread; -} - -void BaseShellDialogImpl::EnableOwner(HWND owner) { - if (IsWindow(owner)) - EnableWindow(owner, TRUE); -} - // Implementation of SelectFileDialog that shows a Windows common dialog for // choosing a file or folder. class SelectFileDialogImpl : public SelectFileDialog, diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 1c0de07..fdafa2b 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -2272,6 +2272,7 @@ 'browser/ui/alternate_error_tab_observer.cc', 'browser/ui/alternate_error_tab_observer.h', 'browser/ui/android/certificate_viewer_android.cc', + 'browser/ui/android/color_chooser_android.cc', 'browser/ui/android/extensions/extension_view_android.cc', 'browser/ui/android/extensions/extension_view_android.h', 'browser/ui/android/external_protocol_dialog_android.cc', @@ -2292,6 +2293,7 @@ 'browser/ui/auto_login_info_bar_delegate.h', 'browser/ui/auto_login_prompter.cc', 'browser/ui/auto_login_prompter.h', + 'browser/ui/base_shell_dialog.h', 'browser/ui/base_window.h', 'browser/ui/blocked_content/blocked_content_container.cc', 'browser/ui/blocked_content/blocked_content_container.h', @@ -2320,6 +2322,7 @@ 'browser/ui/browser_tab_restore_service_delegate.cc', 'browser/ui/browser_tab_restore_service_delegate.h', 'browser/ui/browser_window.h', + 'browser/ui/color_chooser.h', 'browser/ui/certificate_dialogs.cc', 'browser/ui/certificate_dialogs.h', 'browser/ui/cocoa/about_ipc_controller.h', @@ -2431,6 +2434,7 @@ 'browser/ui/cocoa/chrome_event_processing_window.mm', 'browser/ui/cocoa/clickhold_button_cell.h', 'browser/ui/cocoa/clickhold_button_cell.mm', + 'browser/ui/cocoa/color_chooser_mac.mm', 'browser/ui/cocoa/command_observer_bridge.h', 'browser/ui/cocoa/command_observer_bridge.mm', 'browser/ui/cocoa/confirm_bubble_controller.h', @@ -2858,6 +2862,7 @@ 'browser/ui/gtk/collected_cookies_gtk.h', 'browser/ui/gtk/confirm_bubble_view.cc', 'browser/ui/gtk/confirm_bubble_view.h', + 'browser/ui/gtk/color_chooser_gtk.cc', 'browser/ui/gtk/constrained_html_delegate_gtk.cc', 'browser/ui/gtk/constrained_window_gtk.cc', 'browser/ui/gtk/constrained_window_gtk.h', @@ -3214,6 +3219,7 @@ 'browser/ui/views/ash/caps_lock_handler.h', 'browser/ui/views/ash/chrome_shell_delegate.cc', 'browser/ui/views/ash/chrome_shell_delegate.h', + 'browser/ui/views/ash/color_chooser_aura.cc', 'browser/ui/views/ash/gesture_prefs_aura.cc', 'browser/ui/views/ash/ime_controller_chromeos.cc', 'browser/ui/views/ash/ime_controller_chromeos.h', @@ -3248,6 +3254,8 @@ 'browser/ui/views/avatar_menu_bubble_view.h', 'browser/ui/views/avatar_menu_button.cc', 'browser/ui/views/avatar_menu_button.h', + 'browser/ui/views/base_shell_dialog_win.cc', + 'browser/ui/views/base_shell_dialog_win.h', 'browser/ui/views/bookmarks/bookmark_bar_instructions_view.cc', 'browser/ui/views/bookmarks/bookmark_bar_instructions_view.h', 'browser/ui/views/bookmarks/bookmark_bar_view.cc', @@ -3490,6 +3498,9 @@ 'browser/ui/views/select_file_dialog_extension.cc', 'browser/ui/views/select_file_dialog_extension.h', 'browser/ui/views/select_file_dialog_win.cc', + 'browser/ui/views/color_chooser_win.cc', + 'browser/ui/views/color_chooser_dialog.cc', + 'browser/ui/views/color_chooser_dialog.h', 'browser/ui/views/simple_message_box_win.cc', 'browser/ui/views/simple_message_box_views.cc', 'browser/ui/views/simple_message_box_views.h', diff --git a/content/browser/tab_contents/tab_contents.cc b/content/browser/tab_contents/tab_contents.cc index cf3fd3d..1ee9287 100644 --- a/content/browser/tab_contents/tab_contents.cc +++ b/content/browser/tab_contents/tab_contents.cc @@ -38,6 +38,7 @@ #include "content/common/view_messages.h" #include "content/port/browser/render_widget_host_view_port.h" #include "content/public/browser/browser_context.h" +#include "content/public/browser/color_chooser.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/devtools_agent_host_registry.h" #include "content/public/browser/download_manager.h" @@ -272,7 +273,8 @@ TabContents::TabContents(content::BrowserContext* browser_context, temporary_zoom_settings_(false), content_restrictions_(0), view_type_(content::VIEW_TYPE_TAB_CONTENTS), - has_opener_(false) { + has_opener_(false), + color_chooser_(NULL) { render_manager_.Init(browser_context, site_instance, routing_id); view_.reset(content::GetContentClient()->browser()-> @@ -312,6 +314,9 @@ TabContents::~TabContents() { if (dialog_creator_) dialog_creator_->ResetJavaScriptState(this); + if (color_chooser_) + color_chooser_->End(); + NotifyDisconnected(); // Notify any observer that have a reference on this tab contents. @@ -558,6 +563,10 @@ bool TabContents::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(ViewHostMsg_Find_Reply, OnFindReply) IPC_MESSAGE_HANDLER(ViewHostMsg_CrashedPlugin, OnCrashedPlugin) IPC_MESSAGE_HANDLER(ViewHostMsg_AppCacheAccessed, OnAppCacheAccessed) + IPC_MESSAGE_HANDLER(ViewHostMsg_OpenColorChooser, OnOpenColorChooser) + IPC_MESSAGE_HANDLER(ViewHostMsg_EndColorChooser, OnEndColorChooser) + IPC_MESSAGE_HANDLER(ViewHostMsg_SetSelectedColorInColorChooser, + OnSetSelectedColorInColorChooser) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP_EX() @@ -1381,6 +1390,20 @@ bool TabContents::HasOpener() const { return has_opener_; } +void TabContents::DidChooseColorInColorChooser(int color_chooser_id, + const SkColor& color) { + GetRenderViewHost()->Send(new ViewMsg_DidChooseColorResponse( + GetRenderViewHost()->GetRoutingID(), color_chooser_id, color)); +} + +void TabContents::DidEndColorChooser(int color_chooser_id) { + GetRenderViewHost()->Send(new ViewMsg_DidEndColorChooser( + GetRenderViewHost()->GetRoutingID(), color_chooser_id)); + if (delegate_) + delegate_->DidEndColorChooser(); + color_chooser_ = NULL; +} + bool TabContents::FocusLocationBarByDefault() { content::WebUI* web_ui = GetWebUIForCurrentState(); if (web_ui) @@ -1690,6 +1713,24 @@ void TabContents::OnAppCacheAccessed(const GURL& manifest_url, AppCacheAccessed(manifest_url, blocked_by_policy)); } +void TabContents::OnOpenColorChooser(int color_chooser_id, + const SkColor& color) { + color_chooser_ = delegate_->OpenColorChooser(this, color_chooser_id, color); +} + +void TabContents::OnEndColorChooser(int color_chooser_id) { + if (color_chooser_ && + color_chooser_id == color_chooser_->identifier()) + color_chooser_->End(); +} + +void TabContents::OnSetSelectedColorInColorChooser(int color_chooser_id, + const SkColor& color) { + if (color_chooser_ && + color_chooser_id == color_chooser_->identifier()) + color_chooser_->SetSelectedColor(color); +} + // Notifies the RenderWidgetHost instance about the fact that the page is // loading, or done loading and calls the base implementation. void TabContents::SetIsLoading(bool is_loading, diff --git a/content/browser/tab_contents/tab_contents.h b/content/browser/tab_contents/tab_contents.h index 0878483..557f0ce 100644 --- a/content/browser/tab_contents/tab_contents.h +++ b/content/browser/tab_contents/tab_contents.h @@ -36,6 +36,7 @@ class SessionStorageNamespaceImpl; struct ViewHostMsg_DidFailProvisionalLoadWithError_Params; namespace content { +class ColorChooser; class DownloadItem; class JavaScriptDialogCreator; class RenderViewHost; @@ -223,6 +224,9 @@ class CONTENT_EXPORT TabContents virtual content::WebUI* GetWebUIForCurrentState() OVERRIDE; virtual bool GotResponseToLockMouseRequest(bool allowed) OVERRIDE; virtual bool HasOpener() const OVERRIDE; + virtual void DidChooseColorInColorChooser(int color_chooser_id, + const SkColor&) OVERRIDE; + virtual void DidEndColorChooser(int color_chooser_id) OVERRIDE; // Implementation of PageNavigator. virtual content::WebContents* OpenURL( @@ -446,6 +450,11 @@ class CONTENT_EXPORT TabContents bool final_update); void OnCrashedPlugin(const FilePath& plugin_path); void OnAppCacheAccessed(const GURL& manifest_url, bool blocked_by_policy); + void OnOpenColorChooser(int color_chooser_id, + const SkColor& color); + void OnEndColorChooser(int color_chooser_id); + void OnSetSelectedColorInColorChooser(int color_chooser_id, + const SkColor& color); // Changes the IsLoading state and notifies delegate as needed // |details| is used to provide details on the load that just finished @@ -670,6 +679,9 @@ class CONTENT_EXPORT TabContents // Is there an opener associated with this? bool has_opener_; + // Color chooser that was opened by this tab. + content::ColorChooser* color_chooser_; + DISALLOW_COPY_AND_ASSIGN(TabContents); }; diff --git a/content/common/view_messages.h b/content/common/view_messages.h index 2cecf9e..41f6e83 100644 --- a/content/common/view_messages.h +++ b/content/common/view_messages.h @@ -1010,6 +1010,12 @@ IPC_MESSAGE_ROUTED0(ViewMsg_UpdateTargetURL_ACK) IPC_MESSAGE_ROUTED1(ViewMsg_SetAltErrorPageURL, GURL) +// Notifies the color chooser client that the user selected a color. +IPC_MESSAGE_ROUTED2(ViewMsg_DidChooseColorResponse, unsigned, SkColor) + +// Notifies the color chooser client that the color chooser has ended. +IPC_MESSAGE_ROUTED1(ViewMsg_DidEndColorChooser, unsigned) + IPC_MESSAGE_ROUTED1(ViewMsg_RunFileChooserResponse, std::vector<content::SelectedFileInfo>) @@ -1691,6 +1697,19 @@ IPC_MESSAGE_ROUTED2(ViewHostMsg_SelectionBoundsChanged, gfx::Rect /* start rect */, gfx::Rect /* end rect */) +// Asks the browser to open the color chooser. +IPC_MESSAGE_ROUTED2(ViewHostMsg_OpenColorChooser, + int /* id */, + SkColor /* color */) + +// Asks the browser to end the color chooser. +IPC_MESSAGE_ROUTED1(ViewHostMsg_EndColorChooser, int /* id */) + +// Change the selected color in the color chooser. +IPC_MESSAGE_ROUTED2(ViewHostMsg_SetSelectedColorInColorChooser, + int /* id */, + SkColor /* color */) + // Asks the browser to display the file chooser. The result is returned in a // ViewHost_RunFileChooserResponse message. IPC_MESSAGE_ROUTED1(ViewHostMsg_RunFileChooser, diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 8ee5461..bde2984 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -46,6 +46,7 @@ 'public/browser/cert_store.h', 'public/browser/child_process_data.h', 'public/browser/child_process_security_policy.h', + 'public/browser/color_chooser.h', 'public/browser/content_browser_client.h', 'public/browser/content_ipc_logging.h', 'public/browser/devtools_agent_host_registry.h', diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index ca7fc6c..8080d3b 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -177,6 +177,8 @@ 'renderer/renderer_webapplicationcachehost_impl.h', 'renderer/renderer_webcookiejar_impl.cc', 'renderer/renderer_webcookiejar_impl.h', + 'renderer/renderer_webcolorchooser_impl.cc', + 'renderer/renderer_webcolorchooser_impl.h', 'renderer/renderer_webkitplatformsupport_impl.cc', 'renderer/renderer_webkitplatformsupport_impl.h', 'renderer/renderer_webstoragearea_impl.cc', diff --git a/content/public/browser/color_chooser.h b/content/public/browser/color_chooser.h new file mode 100644 index 0000000..7519da5 --- /dev/null +++ b/content/public/browser/color_chooser.h @@ -0,0 +1,47 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_PUBLIC_BROWSER_COLOR_CHOOSER_H_ +#define CONTENT_PUBLIC_BROWSER_COLOR_CHOOSER_H_ +#pragma once + +#include "third_party/skia/include/core/SkColor.h" + +namespace content { + +class RenderViewHost; +class WebContents; + +// Abstraction object for color choosers for each platform. +class ColorChooser { + public: + static ColorChooser* Create(int identifier, + WebContents* tab, + SkColor initial_color); + ColorChooser(int identifier) : identifier_(identifier) {} + virtual ~ColorChooser() {} + + // Returns a unique identifier for this chooser. Identifiers are unique + // across a renderer process. This avoids race conditions in synchronizing + // the browser and renderer processes. For example, if a renderer closes one + // chooser and opens another, and simultaneously the user picks a color in the + // first chooser, the IDs can be used to drop the "chose a color" message + // rather than erroneously tell the renderer that the user picked a color in + // the second chooser. + int identifier() const { return identifier_; } + + // Ends connection with color chooser. Closes color chooser depending on the + // platform. + virtual void End() = 0; + + // Sets the selected color. + virtual void SetSelectedColor(SkColor color) = 0; + +private: + int identifier_; +}; + +} + +#endif // CONTENT_PUBLIC_BROWSER_COLOR_CHOOSER_H_ diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h index abd656a..80e4929 100644 --- a/content/public/browser/web_contents.h +++ b/content/public/browser/web_contents.h @@ -15,6 +15,7 @@ #include "content/public/browser/save_page_type.h" #include "content/public/browser/web_ui.h" #include "content/public/common/view_type.h" +#include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/native_widget_types.h" #include "webkit/glue/window_open_disposition.h" @@ -356,6 +357,13 @@ class WebContents : public PageNavigator { // locked. virtual bool GotResponseToLockMouseRequest(bool allowed) = 0; + // Called when the user has selected a color in the color chooser. + virtual void DidChooseColorInColorChooser(int color_chooser_id, + const SkColor&) = 0; + + // Called when the color chooser has ended. + virtual void DidEndColorChooser(int color_chooser_id) = 0; + // Returns true if the location bar should be focused by default rather than // the page contents. The view calls this function when the tab is focused // to see what it should do. diff --git a/content/public/browser/web_contents_delegate.cc b/content/public/browser/web_contents_delegate.cc index 39f8448..0e24df0 100644 --- a/content/public/browser/web_contents_delegate.cc +++ b/content/public/browser/web_contents_delegate.cc @@ -134,6 +134,12 @@ bool WebContentsDelegate::IsFullscreenForTab(const WebContents* tab) const { return false; } +content::ColorChooser* WebContentsDelegate::OpenColorChooser(WebContents* tab, + int color_chooser_id, + const SkColor& color) { + return NULL; +} + void WebContentsDelegate::WebIntentDispatch( WebContents* tab, WebIntentsDispatcher* intents_dispatcher) { diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h index d5111fd..2d1669c 100644 --- a/content/public/browser/web_contents_delegate.h +++ b/content/public/browser/web_contents_delegate.h @@ -15,6 +15,7 @@ #include "content/public/browser/navigation_type.h" #include "content/public/common/page_transition_types.h" #include "content/public/common/window_container_type.h" +#include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/native_widget_types.h" #include "webkit/glue/window_open_disposition.h" @@ -29,6 +30,7 @@ class ListValue; namespace content { class BrowserContext; +class ColorChooser; class DownloadItem; class JavaScriptDialogCreator; class RenderViewHost; @@ -310,6 +312,13 @@ class CONTENT_EXPORT WebContentsDelegate { // NULL in which case dialogs aren't shown. virtual JavaScriptDialogCreator* GetJavaScriptDialogCreator(); + // Called when color chooser should open. Returns the opened color chooser. + virtual content::ColorChooser* OpenColorChooser(WebContents* tab, + int color_chooser_id, + const SkColor& color); + + virtual void DidEndColorChooser() {} + // Called when a file selection is to be done. virtual void RunFileChooser(WebContents* tab, const FileChooserParams& params) {} diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index b1bb198..52caa04 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc @@ -81,6 +81,7 @@ #include "content/renderer/render_widget_fullscreen_pepper.h" #include "content/renderer/renderer_accessibility.h" #include "content/renderer/renderer_webapplicationcachehost_impl.h" +#include "content/renderer/renderer_webcolorchooser_impl.h" #include "content/renderer/renderer_webstoragenamespace_impl.h" #include "content/renderer/text_input_client_observer.h" #include "content/renderer/v8_value_converter_impl.h" @@ -1741,6 +1742,15 @@ bool RenderViewImpl::handleCurrentKeyboardEvent() { return did_execute_command; } +WebKit::WebColorChooser* RenderViewImpl::createColorChooser( + WebKit::WebColorChooserClient* client, + const WebKit::WebColor& initial_color) { + RendererWebColorChooserImpl* color_chooser = + new RendererWebColorChooserImpl(this, client); + color_chooser->Open(static_cast<SkColor>(initial_color)); + return color_chooser; +} + bool RenderViewImpl::runFileChooser( const WebKit::WebFileChooserParams& params, WebFileChooserCompletion* chooser_completion) { diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h index a104ebe..f523434 100644 --- a/content/renderer/render_view_impl.h +++ b/content/renderer/render_view_impl.h @@ -75,6 +75,7 @@ class PepperDeviceTest; struct PP_NetAddress_Private; class RenderWidgetFullscreenPepper; class RendererAccessibility; +class RendererWebColorChooserImpl; class SkBitmap; class InputTagSpeechDispatcher; struct ViewMsg_Navigate_Params; @@ -376,6 +377,8 @@ class RenderViewImpl : public RenderWidget, virtual void didChangeSelection(bool is_selection_empty); virtual void didExecuteCommand(const WebKit::WebString& command_name); virtual bool handleCurrentKeyboardEvent(); + virtual WebKit::WebColorChooser* createColorChooser( + WebKit::WebColorChooserClient*, const WebKit::WebColor& initial_color); virtual bool runFileChooser( const WebKit::WebFileChooserParams& params, WebKit::WebFileChooserCompletion* chooser_completion); diff --git a/content/renderer/renderer_webcolorchooser_impl.cc b/content/renderer/renderer_webcolorchooser_impl.cc new file mode 100644 index 0000000..9a50b19 --- /dev/null +++ b/content/renderer/renderer_webcolorchooser_impl.cc @@ -0,0 +1,70 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/renderer_webcolorchooser_impl.h" + +#include "content/common/view_messages.h" +#include "content/renderer/render_view_impl.h" + +static int GenerateColorChooserIdentifier() { + static int next = 0; + return ++next; +} + +RendererWebColorChooserImpl::RendererWebColorChooserImpl( + RenderViewImpl* render_view, + WebKit::WebColorChooserClient* client) + : content::RenderViewObserver(render_view), + identifier_(GenerateColorChooserIdentifier()), + client_(client) { +} + +RendererWebColorChooserImpl::~RendererWebColorChooserImpl() { +} + +bool RendererWebColorChooserImpl::OnMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(RendererWebColorChooserImpl, message) + IPC_MESSAGE_HANDLER(ViewMsg_DidChooseColorResponse, + OnDidChooseColorResponse) + IPC_MESSAGE_HANDLER(ViewMsg_DidEndColorChooser, + OnDidEndColorChooser) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void RendererWebColorChooserImpl::FrameWillClose(WebKit::WebFrame* frame) { + endChooser(); + client_->didEndChooser(); +} + +void RendererWebColorChooserImpl::setSelectedColor(WebKit::WebColor color) { + Send(new ViewHostMsg_SetSelectedColorInColorChooser(routing_id(), identifier_, + static_cast<SkColor>(color))); +} + +void RendererWebColorChooserImpl::endChooser() { + Send(new ViewHostMsg_EndColorChooser(routing_id(), identifier_)); +} + +void RendererWebColorChooserImpl::Open(SkColor initial_color) { + Send(new ViewHostMsg_OpenColorChooser(routing_id(), identifier_, + initial_color)); +} + +void RendererWebColorChooserImpl::OnDidChooseColorResponse( + int color_chooser_id, + const SkColor& color) { + DCHECK(identifier_ == color_chooser_id); + + client_->didChooseColor(static_cast<WebKit::WebColor>(color)); +} + +void RendererWebColorChooserImpl::OnDidEndColorChooser(int color_chooser_id) { + if (identifier_ != color_chooser_id) + return; + client_->didEndChooser(); +} diff --git a/content/renderer/renderer_webcolorchooser_impl.h b/content/renderer/renderer_webcolorchooser_impl.h new file mode 100644 index 0000000..afe0871 --- /dev/null +++ b/content/renderer/renderer_webcolorchooser_impl.h @@ -0,0 +1,50 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_RENDERER_WEBCOLORCHOOSER_IMPL_H_ +#define CONTENT_RENDERER_RENDERER_WEBCOLORCHOOSER_IMPL_H_ +#pragma once + +#include "base/memory/scoped_ptr.h" +#include "content/public/renderer/render_view_observer.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebColorChooser.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebColorChooserClient.h" +#include "third_party/skia/include/core/SkColor.h" + +namespace WebKit { +class WebFrame; +} + +class RenderViewImpl; + +class RendererWebColorChooserImpl : public WebKit::WebColorChooser, + public content::RenderViewObserver { + public: + explicit RendererWebColorChooserImpl(RenderViewImpl* sender, + WebKit::WebColorChooserClient*); + virtual ~RendererWebColorChooserImpl(); + + virtual void setSelectedColor(const WebKit::WebColor); + virtual void endChooser(); + + void Open(SkColor initial_color); + + WebKit::WebColorChooserClient* client() { return client_; } + + private: + // RenderViewObserver implementation. + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + virtual void FrameWillClose(WebKit::WebFrame* frame) OVERRIDE; + + void OnDidChooseColorResponse(int color_chooser_id, + const SkColor& color); + void OnDidEndColorChooser(int color_chooser_id); + + int identifier_; + WebKit::WebColorChooserClient* client_; + + DISALLOW_COPY_AND_ASSIGN(RendererWebColorChooserImpl); +}; + +#endif // CONTENT_RENDERER_RENDERER_WEBCOLORCHOOSER_IMPL_H_ diff --git a/skia/ext/skia_utils_mac.h b/skia/ext/skia_utils_mac.h index 8d11165..683ddf0 100644 --- a/skia/ext/skia_utils_mac.h +++ b/skia/ext/skia_utils_mac.h @@ -64,8 +64,13 @@ SkColor CGColorRefToSkColor(CGColorRef color); // Converts ARGB to CGColorRef. CGColorRef SkColorToCGColorRef(SkColor color); +// Converts NSColor to ARGB. Returns raw rgb values and does no colorspace +// conversion. Only valid for colors in calibrated and device color spaces. +SK_API SkColor NSDeviceColorToSkColor(NSColor* color); + // Converts ARGB to NSColor. SK_API NSColor* SkColorToCalibratedNSColor(SkColor color); +SK_API NSColor* SkColorToDeviceNSColor(SkColor color); // Converts a CGImage to a SkBitmap. SK_API SkBitmap CGImageToSkBitmap(CGImageRef image); diff --git a/skia/ext/skia_utils_mac.mm b/skia/ext/skia_utils_mac.mm index d639460..0c82a95e 100644 --- a/skia/ext/skia_utils_mac.mm +++ b/skia/ext/skia_utils_mac.mm @@ -190,6 +190,19 @@ CGColorRef SkColorToCGColorRef(SkColor color) { SkColorGetA(color) / 255.0); } +// Converts NSColor to ARGB +SkColor NSDeviceColorToSkColor(NSColor* color) { + DCHECK([color colorSpace] == [NSColorSpace genericRGBColorSpace] || + [color colorSpace] == [NSColorSpace deviceRGBColorSpace]); + CGFloat red, green, blue, alpha; + color = [color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]]; + [color getRed:&red green:&green blue:&blue alpha:&alpha]; + return SkColorSetARGB(SkScalarRound(255.0 * alpha), + SkScalarRound(255.0 * red), + SkScalarRound(255.0 * green), + SkScalarRound(255.0 * blue)); +} + // Converts ARGB to NSColor. NSColor* SkColorToCalibratedNSColor(SkColor color) { return [NSColor colorWithCalibratedRed:SkColorGetR(color) / 255.0 @@ -198,6 +211,13 @@ NSColor* SkColorToCalibratedNSColor(SkColor color) { alpha:SkColorGetA(color) / 255.0]; } +NSColor* SkColorToDeviceNSColor(SkColor color) { + return [NSColor colorWithDeviceRed:SkColorGetR(color) / 255.0 + green:SkColorGetG(color) / 255.0 + blue:SkColorGetB(color) / 255.0 + alpha:SkColorGetA(color) / 255.0]; +} + SkBitmap CGImageToSkBitmap(CGImageRef image) { if (!image) return SkBitmap(); |