diff options
Diffstat (limited to 'chrome/browser/gtk')
-rw-r--r-- | chrome/browser/gtk/accessibility_event_router_gtk.cc | 286 | ||||
-rw-r--r-- | chrome/browser/gtk/accessibility_event_router_gtk.h | 130 | ||||
-rw-r--r-- | chrome/browser/gtk/accessible_widget_helper_gtk.cc | 41 | ||||
-rw-r--r-- | chrome/browser/gtk/accessible_widget_helper_gtk.h | 58 | ||||
-rw-r--r-- | chrome/browser/gtk/location_bar_view_gtk.cc | 10 | ||||
-rw-r--r-- | chrome/browser/gtk/options/advanced_contents_gtk.cc | 49 | ||||
-rw-r--r-- | chrome/browser/gtk/options/content_page_gtk.h | 2 | ||||
-rw-r--r-- | chrome/browser/gtk/options/general_page_gtk.cc | 4 | ||||
-rw-r--r-- | chrome/browser/gtk/options/options_window_gtk.cc | 30 |
9 files changed, 606 insertions, 4 deletions
diff --git a/chrome/browser/gtk/accessibility_event_router_gtk.cc b/chrome/browser/gtk/accessibility_event_router_gtk.cc new file mode 100644 index 0000000..c020721 --- /dev/null +++ b/chrome/browser/gtk/accessibility_event_router_gtk.cc @@ -0,0 +1,286 @@ +// Copyright (c) 2010 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/gtk/accessibility_event_router_gtk.h" + +#include "base/basictypes.h" +#include "base/stl_util-inl.h" +#include "chrome/browser/extensions/extension_accessibility_api.h" +#include "chrome/browser/gtk/gtk_chrome_link_button.h" +#include "chrome/browser/profile.h" +#include "chrome/common/notification_type.h" + +namespace { + +// +// Callbacks triggered by signals on gtk widgets. +// + +gboolean OnWidgetFocused(GSignalInvocationHint *ihint, + guint n_param_values, + const GValue* param_values, + gpointer user_data) { + GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); + reinterpret_cast<AccessibilityEventRouter *>(user_data) + ->DispatchAccessibilityNotification( + widget, NotificationType::ACCESSIBILITY_CONTROL_FOCUSED); + return true; +} + +gboolean OnButtonClicked(GSignalInvocationHint *ihint, + guint n_param_values, + const GValue* param_values, + gpointer user_data) { + GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); + // Skip toggle buttons because we're also listening on "toggle" events. + if (GTK_IS_TOGGLE_BUTTON(widget)) + return true; + reinterpret_cast<AccessibilityEventRouter *>(user_data) + ->DispatchAccessibilityNotification( + widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION); + return true; +} + +gboolean OnButtonToggled(GSignalInvocationHint *ihint, + guint n_param_values, + const GValue* param_values, + gpointer user_data) { + GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); + bool checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); + // Skip propagating an "uncheck" event for a radio button because it's + // redundant; there will always be a corresponding "check" event for + // a different radio button the group. + if (GTK_IS_RADIO_BUTTON(widget) && !checked) + return true; + reinterpret_cast<AccessibilityEventRouter *>(user_data) + ->DispatchAccessibilityNotification( + widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION); + return true; +} + +gboolean OnSwitchPage(GSignalInvocationHint *ihint, + guint n_param_values, + const GValue* param_values, + gpointer user_data) { + GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); + reinterpret_cast<AccessibilityEventRouter *>(user_data) + ->DispatchAccessibilityNotification( + widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION); + return true; +} + +} // anonymous namespace + +AccessibilityEventRouter::AccessibilityEventRouter() { + // We don't want our event listeners to be installed if accessibility is + // disabled. Install listeners so we can install and uninstall them as + // needed, then install them now if it's currently enabled. + ExtensionAccessibilityEventRouter *accessibility_event_router = + ExtensionAccessibilityEventRouter::GetInstance(); + accessibility_event_router->AddOnEnabledListener( + NewCallback(this, + &AccessibilityEventRouter::InstallEventListeners)); + accessibility_event_router->AddOnDisabledListener( + NewCallback(this, + &AccessibilityEventRouter::RemoveEventListeners)); + if (accessibility_event_router->IsAccessibilityEnabled()) { + InstallEventListeners(); + } +} + +// static +AccessibilityEventRouter* AccessibilityEventRouter::GetInstance() { + return Singleton<AccessibilityEventRouter>::get(); +} + +void AccessibilityEventRouter::InstallEventListeners() { + // Create and destroy a GtkNotebook to ensure this module is loaded, + // otherwise we can't lookup its signals. All of the other modules we + // need will already be loaded by the time we get here. + g_object_unref(g_object_ref_sink(gtk_notebook_new())); + + // Add signal emission hooks for the events we're interested in. + focus_hook_ = g_signal_add_emission_hook( + g_signal_lookup("focus-in-event", GTK_TYPE_WIDGET), + 0, OnWidgetFocused, (gpointer)this, NULL); + click_hook_ = g_signal_add_emission_hook( + g_signal_lookup("clicked", GTK_TYPE_BUTTON), + 0, OnButtonClicked, (gpointer)this, NULL); + toggle_hook_ = g_signal_add_emission_hook( + g_signal_lookup("toggled", GTK_TYPE_TOGGLE_BUTTON), + 0, OnButtonToggled, (gpointer)this, NULL); + switch_page_hook_ = g_signal_add_emission_hook( + g_signal_lookup("switch-page", GTK_TYPE_NOTEBOOK), + 0, OnSwitchPage, (gpointer)this, NULL); +} + +void AccessibilityEventRouter::RemoveEventListeners() { + g_signal_remove_emission_hook( + g_signal_lookup("focus-in-event", GTK_TYPE_WIDGET), focus_hook_); + g_signal_remove_emission_hook( + g_signal_lookup("clicked", GTK_TYPE_BUTTON), click_hook_); + g_signal_remove_emission_hook( + g_signal_lookup("toggled", GTK_TYPE_TOGGLE_BUTTON), toggle_hook_); + g_signal_remove_emission_hook( + g_signal_lookup("switch-page", GTK_TYPE_NOTEBOOK), switch_page_hook_); +} + +void AccessibilityEventRouter::AddRootWidget( + GtkWidget* root_widget, Profile* profile) { + root_widget_profile_map_[root_widget] = profile; +} + +void AccessibilityEventRouter::RemoveRootWidget(GtkWidget* root_widget) { + DCHECK(root_widget_profile_map_.find(root_widget) != + root_widget_profile_map_.end()); + root_widget_profile_map_.erase(root_widget); +} + +void AccessibilityEventRouter::IgnoreWidget(GtkWidget* widget) { + widget_info_map_[widget].ignore = true; +} + +void AccessibilityEventRouter::SetWidgetName( + GtkWidget* widget, std::string name) { + widget_info_map_[widget].name = name; +} + +void AccessibilityEventRouter::RemoveWidget(GtkWidget* widget) { + DCHECK(widget_info_map_.find(widget) != widget_info_map_.end()); + widget_info_map_.erase(widget); +} + +bool AccessibilityEventRouter::IsWidgetAccessible( + GtkWidget* widget, Profile** profile) { + // First see if it's a descendant of a root widget. + bool is_accessible = false; + for (base::hash_map<GtkWidget*, Profile*>::const_iterator iter = + root_widget_profile_map_.begin(); + iter != root_widget_profile_map_.end(); + ++iter) { + if (gtk_widget_is_ancestor(widget, iter->first)) { + is_accessible = true; + if (profile) + *profile = iter->second; + break; + } + } + if (!is_accessible) + return false; + + // Now make sure it's not marked as a widget to be ignored. + base::hash_map<GtkWidget*, WidgetInfo>::const_iterator iter = + widget_info_map_.find(widget); + if (iter != widget_info_map_.end() && iter->second.ignore) { + is_accessible = false; + } + + return is_accessible; +} + +std::string AccessibilityEventRouter::GetWidgetName(GtkWidget* widget) { + base::hash_map<GtkWidget*, WidgetInfo>::const_iterator iter = + widget_info_map_.find(widget); + if (iter != widget_info_map_.end()) { + return iter->second.name; + } else { + return ""; + } +} + +void AccessibilityEventRouter::DispatchAccessibilityNotification( + GtkWidget* widget, NotificationType type) { + Profile *profile; + if (!IsWidgetAccessible(widget, &profile)) + return; + + // The order of these checks matters, because, for example, a radio button + // is a subclass of button. We need to catch the most specific type that + // we can handle for each object. + if (GTK_IS_RADIO_BUTTON(widget)) { + SendRadioButtonNotification(widget, type, profile); + } else if (GTK_IS_TOGGLE_BUTTON(widget)) { + SendCheckboxNotification(widget, type, profile); + } else if (GTK_IS_BUTTON(widget)) { + SendButtonNotification(widget, type, profile); + } else if (GTK_IS_ENTRY(widget)) { + SendTextBoxNotification(widget, type, profile); + } else if (GTK_IS_NOTEBOOK(widget)) { + SendTabNotification(widget, type, profile); + } +} + +void AccessibilityEventRouter::SendRadioButtonNotification( + GtkWidget* widget, NotificationType type, Profile* profile) { + // Get the radio button name + std::string button_name = GetWidgetName(widget); + if (button_name.empty() && gtk_button_get_label(GTK_BUTTON(widget))) + button_name = gtk_button_get_label(GTK_BUTTON(widget)); + + // Get its state + bool checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); + + // Get the index of this radio button and the total number of + // radio buttons in the group. + int item_count = 0; + int item_index = -1; + for (GSList* group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(widget)); + group; + group = group->next) { + if (group->data == widget) { + item_index = item_count; + } + item_count++; + } + item_index = item_count - 1 - item_index; + + AccessibilityRadioButtonInfo info( + profile, button_name, checked, item_index, item_count); + SendAccessibilityNotification(type, &info); +} + +void AccessibilityEventRouter::SendCheckboxNotification( + GtkWidget* widget, NotificationType type, Profile* profile) { + std::string button_name = GetWidgetName(widget); + if (button_name.empty() && gtk_button_get_label(GTK_BUTTON(widget))) + button_name = gtk_button_get_label(GTK_BUTTON(widget)); + bool checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); + AccessibilityCheckboxInfo info(profile, button_name, checked); + SendAccessibilityNotification(type, &info); +} + +void AccessibilityEventRouter::SendButtonNotification( + GtkWidget* widget, NotificationType type, Profile* profile) { + std::string button_name = GetWidgetName(widget); + if (button_name.empty() && gtk_button_get_label(GTK_BUTTON(widget))) + button_name = gtk_button_get_label(GTK_BUTTON(widget)); + AccessibilityButtonInfo info(profile, button_name); + SendAccessibilityNotification(type, &info); +} + +void AccessibilityEventRouter::SendTextBoxNotification( + GtkWidget* widget, NotificationType type, Profile* profile) { + std::string name = GetWidgetName(widget); + std::string value = gtk_entry_get_text(GTK_ENTRY(widget)); + gint start_pos; + gint end_pos; + gtk_editable_get_selection_bounds(GTK_EDITABLE(widget), &start_pos, &end_pos); + AccessibilityTextBoxInfo info(profile, name, false); + info.SetValue(value, start_pos, end_pos); + SendAccessibilityNotification(type, &info); +} + +void AccessibilityEventRouter::SendTabNotification( + GtkWidget* widget, NotificationType type, Profile* profile) { + int index = gtk_notebook_get_current_page(GTK_NOTEBOOK(widget)); + int page_count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(widget)); + GtkWidget* page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(widget), index); + GtkWidget* label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(widget), page); + std::string name = GetWidgetName(widget); + if (name.empty() && gtk_label_get_text(GTK_LABEL(label))) { + name = gtk_label_get_text(GTK_LABEL(label)); + } + AccessibilityTabInfo info(profile, name, index, page_count); + SendAccessibilityNotification(type, &info); +} diff --git a/chrome/browser/gtk/accessibility_event_router_gtk.h b/chrome/browser/gtk/accessibility_event_router_gtk.h new file mode 100644 index 0000000..d0fd99d --- /dev/null +++ b/chrome/browser/gtk/accessibility_event_router_gtk.h @@ -0,0 +1,130 @@ +// Copyright (c) 2010 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_GTK_ACCESSIBILITY_EVENT_ROUTER_GTK_H_ +#define CHROME_BROWSER_GTK_ACCESSIBILITY_EVENT_ROUTER_GTK_H_ + +#include <gtk/gtk.h> + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/hash_tables.h" +#include "base/singleton.h" +#include "chrome/common/accessibility_events.h" + +class Profile; + +// Allows us to use (GtkWidget*) in a hash_map with gcc. +namespace __gnu_cxx { +template<> +struct hash<GtkWidget*> { + size_t operator()(GtkWidget* widget) const { + return reinterpret_cast<size_t>(widget); + } +}; +} // namespace __gnu_cxx + +// Singleton class that adds a signal emission hook to many gtk events, and +// then sends an accessibility notification whenever a relevant event is +// sent to an accessible control. +// +// Gtk widgets are not accessible by default. When you register a root widget, +// that widget and all of its descendants will start sending accessibility +// event notifications. You can then override the default behavior for +// specific descendants using other methods. +// +// You can use Profile::PauseAccessibilityEvents to prevent a flurry +// of accessibility events when a window is being created or initialized. +class AccessibilityEventRouter { + public: + // Internal information about a particular widget to override the + // information we get directly from gtk. + struct WidgetInfo { + // If nonempty, will use this name instead of the widget's label. + std::string name; + + // If true, will ignore this widget and not send accessibility events. + bool ignore; + }; + + // Get the single instance of this class. + static AccessibilityEventRouter* GetInstance(); + + // Start sending accessibility events for this widget and all of its + // descendants. Notifications will go to the specified profile. + void AddRootWidget(GtkWidget* root_widget, Profile* profile); + + // Stop sending accessibility events for this widget and all of its + // descendants. + void RemoveRootWidget(GtkWidget* root_widget); + + // Don't send any events for this widget. + void IgnoreWidget(GtkWidget* widget); + + // Use the following string as the name of this widget, instead of the + // gtk label associated with the widget. + void SetWidgetName(GtkWidget* widget, std::string name); + + // Forget all information about this widget. + void RemoveWidget(GtkWidget* widget); + + // + // The following methods are only for use by gtk signal handlers. + // + + // Returns true if this widget is a descendant of one of our registered + // root widgets and not in the set of ignored widgets. If |profile| is + // not null, return the profile where notifications associated with this + // widget should be sent. + bool IsWidgetAccessible(GtkWidget* widget, Profile** profile); + + // Return the name of a widget. + std::string GetWidgetName(GtkWidget* widget); + + // Called by the signal handler. Checks the type of the widget and + // calls one of the more specific Send*Notification methods, below. + void DispatchAccessibilityNotification( + GtkWidget* widget, NotificationType type); + + // Each of these methods constructs an AccessibilityControlInfo object + // and sends a notification of a specific accessibility event. + void SendRadioButtonNotification( + GtkWidget* widget, NotificationType type, Profile* profile); + void SendCheckboxNotification( + GtkWidget* widget, NotificationType type, Profile* profile); + void SendButtonNotification( + GtkWidget* widget, NotificationType type, Profile* profile); + void SendTextBoxNotification( + GtkWidget* widget, NotificationType type, Profile* profile); + void SendTabNotification( + GtkWidget* widget, NotificationType type, Profile* profile); + + void InstallEventListeners(); + void RemoveEventListeners(); + + private: + AccessibilityEventRouter(); + virtual ~AccessibilityEventRouter() {} + + friend struct DefaultSingletonTraits<AccessibilityEventRouter>; + + // The set of all root widgets; only descendants of these will generate + // accessibility notifications. + base::hash_map<GtkWidget*, Profile*> root_widget_profile_map_; + + // Extra information about specific widgets. + base::hash_map<GtkWidget*, WidgetInfo> widget_info_map_; + + // Installed event listener hook ids so we can remove them later. + gulong focus_hook_; + gulong click_hook_; + gulong toggle_hook_; + gulong switch_page_hook_; + + std::vector<gulong> event_listener_hook_ids_; +}; + +#endif // CHROME_BROWSER_GTK_ACCESSIBILITY_EVENT_ROUTER_GTK_H_ diff --git a/chrome/browser/gtk/accessible_widget_helper_gtk.cc b/chrome/browser/gtk/accessible_widget_helper_gtk.cc new file mode 100644 index 0000000..64710b1 --- /dev/null +++ b/chrome/browser/gtk/accessible_widget_helper_gtk.cc @@ -0,0 +1,41 @@ +// Copyright (c) 2010 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/gtk/accessible_widget_helper_gtk.h" + +#include "app/l10n_util.h" +#include "chrome/browser/profile.h" + +AccessibleWidgetHelper::AccessibleWidgetHelper( + GtkWidget* root_widget, Profile* profile) + : accessibility_event_router_(AccessibilityEventRouter::GetInstance()), + root_widget_(root_widget) { + accessibility_event_router_->AddRootWidget(root_widget_, profile); +} + +AccessibleWidgetHelper::~AccessibleWidgetHelper() { + if (root_widget_) + accessibility_event_router_->RemoveRootWidget(root_widget_); + for (unsigned int i = 0; i < managed_widgets_.size(); i++) { + accessibility_event_router_->RemoveWidget(managed_widgets_[i]); + } +} + +void AccessibleWidgetHelper::IgnoreWidget(GtkWidget* widget) { + accessibility_event_router_->IgnoreWidget(widget); + managed_widgets_.push_back(widget); +} + +void AccessibleWidgetHelper::SetWidgetName( + GtkWidget* widget, std::string name) { + accessibility_event_router_->SetWidgetName(widget, name); + managed_widgets_.push_back(widget); +} + +void AccessibleWidgetHelper::SetWidgetName( + GtkWidget* widget, int string_id) { + std::string name = l10n_util::GetStringUTF8(string_id); + accessibility_event_router_->SetWidgetName(widget, name); + managed_widgets_.push_back(widget); +} diff --git a/chrome/browser/gtk/accessible_widget_helper_gtk.h b/chrome/browser/gtk/accessible_widget_helper_gtk.h new file mode 100644 index 0000000..775b92e --- /dev/null +++ b/chrome/browser/gtk/accessible_widget_helper_gtk.h @@ -0,0 +1,58 @@ +// Copyright (c) 2010 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_GTK_ACCESSIBLE_WIDGET_HELPER_GTK_H_ +#define CHROME_BROWSER_GTK_ACCESSIBLE_WIDGET_HELPER_GTK_H_ + +#include <gtk/gtk.h> + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/singleton.h" +#include "chrome/browser/gtk/accessibility_event_router_gtk.h" +#include "chrome/common/accessibility_events.h" + +class Profile; + +// Helper class that helps to manage the accessibility information for all +// of the widgets in a container. Create an instance of this class for +// each container GtkWidget (like a dialog) that should send accessibility +// events for all of its descendants. +// +// Most controls have default behavior for accessibility; when this needs +// to be augmented, call one of the methods below to ignore a particular +// widget or change its details. +// +// All of the information managed by this class is registered with the +// (global) AccessibilityEventRouter and unregistered when this object is +// destroyed. +class AccessibleWidgetHelper { + public: + // Contruct an AccessibleWidgetHelper that makes the given root widget + // accessible for the lifetime of this object, sending accessibility + // notifications to the given profile. + AccessibleWidgetHelper(GtkWidget* root_widget, Profile* profile); + + virtual ~AccessibleWidgetHelper(); + + // Do not send accessibility events for this widget + void IgnoreWidget(GtkWidget* widget); + + // Use the following string as the name of this widget, instead of the + // gtk label associated with the widget. + void SetWidgetName(GtkWidget* widget, std::string name); + + // Use the following string as the name of this widget, instead of the + // gtk label associated with the widget. + void SetWidgetName(GtkWidget* widget, int string_id); + + private: + AccessibilityEventRouter* accessibility_event_router_; + GtkWidget* root_widget_; + std::vector<GtkWidget*> managed_widgets_; +}; + +#endif // CHROME_BROWSER_GTK_ACCESSIBLE_WIDGET_HELPER_GTK_H_ diff --git a/chrome/browser/gtk/location_bar_view_gtk.cc b/chrome/browser/gtk/location_bar_view_gtk.cc index 2842fcd..a52105d 100644 --- a/chrome/browser/gtk/location_bar_view_gtk.cc +++ b/chrome/browser/gtk/location_bar_view_gtk.cc @@ -19,6 +19,7 @@ #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/command_updater.h" +#include "chrome/browser/extensions/extension_accessibility_api_constants.h" #include "chrome/browser/extensions/extension_action_context_menu_model.h" #include "chrome/browser/extensions/extension_browser_event_router.h" #include "chrome/browser/extensions/extension_tabs_module.h" @@ -32,6 +33,7 @@ #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/search_engines/template_url_model.h" #include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/accessibility_events.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_action.h" #include "chrome/common/gtk_util.h" @@ -449,6 +451,14 @@ void LocationBarViewGtk::OnKillFocus() { } void LocationBarViewGtk::OnSetFocus() { + AccessibilityTextBoxInfo info( + profile_, + l10n_util::GetStringUTF8(IDS_ACCNAME_LOCATION).c_str(), + false); + NotificationService::current()->Notify( + NotificationType::ACCESSIBILITY_CONTROL_FOCUSED, + Source<Profile>(profile_), + Details<AccessibilityTextBoxInfo>(&info)); } SkBitmap LocationBarViewGtk::GetFavIcon() const { diff --git a/chrome/browser/gtk/options/advanced_contents_gtk.cc b/chrome/browser/gtk/options/advanced_contents_gtk.cc index d493a39..2ceb997 100644 --- a/chrome/browser/gtk/options/advanced_contents_gtk.cc +++ b/chrome/browser/gtk/options/advanced_contents_gtk.cc @@ -18,6 +18,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/download/download_manager.h" #include "chrome/browser/fonts_languages_window.h" +#include "chrome/browser/gtk/accessible_widget_helper_gtk.h" #include "chrome/browser/gtk/gtk_chrome_link_button.h" #include "chrome/browser/gtk/options/cookies_view.h" #include "chrome/browser/gtk/options/options_layout_gtk.h" @@ -82,6 +83,7 @@ GtkWidget* AddCheckButtonWithWrappedLabel(int string_id, GtkWidget* checkbox = CreateCheckButtonWithWrappedLabel(string_id); gtk_box_pack_start(GTK_BOX(container), checkbox, FALSE, FALSE, 0); g_signal_connect(checkbox, "toggled", handler, data); + return checkbox; } @@ -167,13 +169,19 @@ class DownloadSection : public OptionsPageBase { // then turning around and saving them again. bool pref_changing_; + scoped_ptr<AccessibleWidgetHelper> accessible_widget_helper_; + DISALLOW_COPY_AND_ASSIGN(DownloadSection); }; DownloadSection::DownloadSection(Profile* profile) - : OptionsPageBase(profile), pref_changing_(true) { + : OptionsPageBase(profile), + pref_changing_(true) { page_ = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); + accessible_widget_helper_.reset(new AccessibleWidgetHelper( + page_, profile)); + // Download location options. download_location_button_ = gtk_file_chooser_button_new( l10n_util::GetStringUTF8( @@ -212,6 +220,9 @@ DownloadSection::DownloadSection(Profile* profile) FALSE, FALSE, 0); g_signal_connect(download_ask_for_save_location_checkbox_, "clicked", G_CALLBACK(OnDownloadAskForSaveLocationChanged), this); + accessible_widget_helper_->SetWidgetName( + download_ask_for_save_location_checkbox_, + IDS_OPTIONS_DOWNLOADLOCATION_ASKFORSAVELOCATION); // Option for resetting file handlers. reset_file_handlers_label_ = CreateWrappedLabel( @@ -359,6 +370,7 @@ NetworkSection::NetworkSection(Profile* profile) IDS_OPTIONS_PROXIES_CONFIGURE_BUTTON).c_str()); g_signal_connect(change_proxies_button, "clicked", G_CALLBACK(OnChangeProxiesButtonClicked), this); + // Stick it in an hbox so it doesn't expand to the whole width. GtkWidget* button_hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(button_hbox), @@ -526,6 +538,8 @@ class PrivacySection : public OptionsPageBase { // then turning around and saving them again. bool pref_changing_; + scoped_ptr<AccessibleWidgetHelper> accessible_widget_helper_; + DISALLOW_COPY_AND_ASSIGN(PrivacySection); }; @@ -534,6 +548,9 @@ PrivacySection::PrivacySection(Profile* profile) pref_changing_(true) { page_ = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); + accessible_widget_helper_.reset(new AccessibleWidgetHelper( + page_, profile)); + GtkWidget* section_description_label = CreateWrappedLabel( IDS_OPTIONS_DISABLE_SERVICES); gtk_misc_set_alignment(GTK_MISC(section_description_label), 0, 0); @@ -557,6 +574,8 @@ PrivacySection::PrivacySection(Profile* profile) FALSE, FALSE, 0); g_signal_connect(enable_link_doctor_checkbox_, "clicked", G_CALLBACK(OnEnableLinkDoctorChange), this); + accessible_widget_helper_->SetWidgetName( + enable_link_doctor_checkbox_, IDS_OPTIONS_LINKDOCTOR_PREF); enable_suggest_checkbox_ = CreateCheckButtonWithWrappedLabel( IDS_OPTIONS_SUGGEST_PREF); @@ -564,6 +583,8 @@ PrivacySection::PrivacySection(Profile* profile) FALSE, FALSE, 0); g_signal_connect(enable_suggest_checkbox_, "clicked", G_CALLBACK(OnEnableSuggestChange), this); + accessible_widget_helper_->SetWidgetName( + enable_suggest_checkbox_, IDS_OPTIONS_SUGGEST_PREF); enable_dns_prefetching_checkbox_ = CreateCheckButtonWithWrappedLabel( IDS_NETWORK_DNS_PREFETCH_ENABLED_DESCRIPTION); @@ -571,6 +592,9 @@ PrivacySection::PrivacySection(Profile* profile) FALSE, FALSE, 0); g_signal_connect(enable_dns_prefetching_checkbox_, "clicked", G_CALLBACK(OnDNSPrefetchingChange), this); + accessible_widget_helper_->SetWidgetName( + enable_dns_prefetching_checkbox_, + IDS_NETWORK_DNS_PREFETCH_ENABLED_DESCRIPTION); enable_safe_browsing_checkbox_ = CreateCheckButtonWithWrappedLabel( IDS_OPTIONS_SAFEBROWSING_ENABLEPROTECTION); @@ -578,6 +602,9 @@ PrivacySection::PrivacySection(Profile* profile) FALSE, FALSE, 0); g_signal_connect(enable_safe_browsing_checkbox_, "clicked", G_CALLBACK(OnSafeBrowsingChange), this); + accessible_widget_helper_->SetWidgetName( + enable_safe_browsing_checkbox_, + IDS_OPTIONS_SAFEBROWSING_ENABLEPROTECTION); #if defined(GOOGLE_CHROME_BUILD) reporting_enabled_checkbox_ = CreateCheckButtonWithWrappedLabel( @@ -586,6 +613,8 @@ PrivacySection::PrivacySection(Profile* profile) FALSE, FALSE, 0); g_signal_connect(reporting_enabled_checkbox_, "clicked", G_CALLBACK(OnLoggingChange), this); + accessible_widget_helper_->SetWidgetName( + reporting_enabled_checkbox_, IDS_OPTIONS_ENABLE_LOGGING); #endif GtkWidget* cookie_description_label = gtk_label_new( @@ -620,6 +649,7 @@ PrivacySection::PrivacySection(Profile* profile) IDS_OPTIONS_COOKIES_SHOWCOOKIES_WEBSITE_PERMISSIONS).c_str()); g_signal_connect(show_cookies_button, "clicked", G_CALLBACK(OnShowCookiesButtonClicked), this); + // Stick it in an hbox so it doesn't expand to the whole width. GtkWidget* button_hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(button_hbox), show_cookies_button, @@ -876,13 +906,19 @@ class SecuritySection : public OptionsPageBase { // then turning around and saving them again. bool pref_changing_; + scoped_ptr<AccessibleWidgetHelper> accessible_widget_helper_; + DISALLOW_COPY_AND_ASSIGN(SecuritySection); }; SecuritySection::SecuritySection(Profile* profile) - : OptionsPageBase(profile), pref_changing_(true) { + : OptionsPageBase(profile), + pref_changing_(true) { page_ = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); + accessible_widget_helper_.reset(new AccessibleWidgetHelper( + page_, profile)); + GtkWidget* manage_certificates_label = CreateWrappedLabel( IDS_OPTIONS_CERTIFICATES_LABEL); gtk_misc_set_alignment(GTK_MISC(manage_certificates_label), 0, 0); @@ -909,13 +945,20 @@ SecuritySection::SecuritySection(Profile* profile) rev_checking_enabled_checkbox_ = AddCheckButtonWithWrappedLabel( IDS_OPTIONS_SSL_CHECKREVOCATION, page_, G_CALLBACK(OnRevCheckingEnabledToggled), this); + accessible_widget_helper_->SetWidgetName( + rev_checking_enabled_checkbox_, IDS_OPTIONS_SSL_CHECKREVOCATION); ssl2_enabled_checkbox_ = AddCheckButtonWithWrappedLabel( IDS_OPTIONS_SSL_USESSL2, page_, G_CALLBACK(OnSSL2EnabledToggled), this); + accessible_widget_helper_->SetWidgetName( + ssl2_enabled_checkbox_, IDS_OPTIONS_SSL_USESSL2); ssl3_enabled_checkbox_ = AddCheckButtonWithWrappedLabel( IDS_OPTIONS_SSL_USESSL3, page_, G_CALLBACK(OnSSL3EnabledToggled), this); + accessible_widget_helper_->SetWidgetName( + ssl3_enabled_checkbox_, IDS_OPTIONS_SSL_USESSL3); tls1_enabled_checkbox_ = AddCheckButtonWithWrappedLabel( IDS_OPTIONS_SSL_USETLS1, page_, G_CALLBACK(OnTLS1EnabledToggled), this); - + accessible_widget_helper_->SetWidgetName( + tls1_enabled_checkbox_, IDS_OPTIONS_SSL_USETLS1); rev_checking_enabled_.Init(prefs::kCertRevocationCheckingEnabled, profile->GetPrefs(), this); diff --git a/chrome/browser/gtk/options/content_page_gtk.h b/chrome/browser/gtk/options/content_page_gtk.h index ba70861..b5ed627a 100644 --- a/chrome/browser/gtk/options/content_page_gtk.h +++ b/chrome/browser/gtk/options/content_page_gtk.h @@ -7,9 +7,9 @@ #include <gtk/gtk.h> -#include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/options_page_base.h" #include "chrome/browser/profile.h" +#include "chrome/browser/sync/profile_sync_service.h" #include "chrome/common/pref_member.h" class ContentPageGtk : public OptionsPageBase, diff --git a/chrome/browser/gtk/options/general_page_gtk.cc b/chrome/browser/gtk/options/general_page_gtk.cc index b491c629..d9ae203 100644 --- a/chrome/browser/gtk/options/general_page_gtk.cc +++ b/chrome/browser/gtk/options/general_page_gtk.cc @@ -17,6 +17,7 @@ #include "chrome/browser/session_startup_pref.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/gtk_util.h" +#include "chrome/common/notification_service.h" #include "chrome/common/pref_names.h" #include "chrome/common/pref_service.h" #include "chrome/common/url_constants.h" @@ -293,6 +294,7 @@ GtkWidget* GeneralPageGtk::InitHomepageGroup() { G_CALLBACK(OnNewTabIsHomePageToggled), this); gtk_box_pack_start(GTK_BOX(homepage_hbox), homepage_use_url_radio_, FALSE, FALSE, 0); + homepage_use_url_entry_ = gtk_entry_new(); g_signal_connect(G_OBJECT(homepage_use_url_entry_), "changed", G_CALLBACK(OnHomepageUseUrlEntryChanged), this); @@ -361,6 +363,7 @@ GtkWidget* GeneralPageGtk::InitDefaultBrowserGroup() { l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)).c_str()); g_signal_connect(G_OBJECT(default_browser_use_as_default_button_), "clicked", G_CALLBACK(OnBrowserUseAsDefaultClicked), this); + gtk_box_pack_start(GTK_BOX(vbox), default_browser_use_as_default_button_, FALSE, FALSE, 0); @@ -377,6 +380,7 @@ void GeneralPageGtk::OnStartupRadioToggled(GtkToggleButton* toggle_button, GeneralPageGtk* general_page) { if (general_page->initializing_) return; + if (!gtk_toggle_button_get_active(toggle_button)) { // When selecting a radio button, we get two signals (one for the old radio // being toggled off, one for the new one being toggled on.) Ignore the diff --git a/chrome/browser/gtk/options/options_window_gtk.cc b/chrome/browser/gtk/options/options_window_gtk.cc index 2a12e12..709ee45 100644 --- a/chrome/browser/gtk/options/options_window_gtk.cc +++ b/chrome/browser/gtk/options/options_window_gtk.cc @@ -8,15 +8,19 @@ #include "app/l10n_util.h" #include "base/message_loop.h" +#include "base/scoped_ptr.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_window.h" +#include "chrome/browser/gtk/accessible_widget_helper_gtk.h" #include "chrome/browser/gtk/options/advanced_page_gtk.h" #include "chrome/browser/gtk/options/content_page_gtk.h" #include "chrome/browser/gtk/options/general_page_gtk.h" #include "chrome/browser/profile.h" #include "chrome/browser/window_sizer.h" +#include "chrome/common/accessibility_events.h" #include "chrome/common/gtk_util.h" +#include "chrome/common/notification_service.h" #include "chrome/common/pref_member.h" #include "chrome/common/pref_names.h" #include "chrome/common/pref_service.h" @@ -68,6 +72,8 @@ class OptionsWindowGtk { // The last page the user was on when they opened the Options window. IntegerPrefMember last_selected_page_; + scoped_ptr<AccessibleWidgetHelper> accessibility_widget_helper_; + DISALLOW_COPY_AND_ASSIGN(OptionsWindowGtk); }; @@ -85,6 +91,7 @@ OptionsWindowGtk::OptionsWindowGtk(Profile* profile) general_page_(profile_), content_page_(profile_), advanced_page_(profile_) { + // We don't need to observe changes in this value. last_selected_page_.Init(prefs::kOptionsWindowLastTabIndex, g_browser_process->local_state(), NULL); @@ -105,6 +112,9 @@ OptionsWindowGtk::OptionsWindowGtk(Profile* profile) gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog_)->vbox), gtk_util::kContentAreaSpacing); + accessibility_widget_helper_.reset(new AccessibleWidgetHelper( + dialog_, profile)); + notebook_ = gtk_notebook_new(); #if defined(OS_CHROMEOS) @@ -248,10 +258,30 @@ void ShowOptionsWindow(OptionsPage page, OptionsGroup highlight_group, Profile* profile) { DCHECK(profile); + // If there's already an existing options window, activate it and switch to // the specified page. if (!options_window) { + // Creating and initializing a bunch of controls generates a bunch of + // spurious events as control values change. Temporarily suppress + // accessibility events until the window is created. + profile->PauseAccessibilityEvents(); + + // Create the options window. options_window = new OptionsWindowGtk(profile); + + // Resume accessibility events. + profile->ResumeAccessibilityEvents(); } options_window->ShowOptionsPage(page, highlight_group); + + std::string name = l10n_util::GetStringFUTF8( + IDS_OPTIONS_DIALOG_TITLE, + WideToUTF16(l10n_util::GetString(IDS_PRODUCT_NAME))); + AccessibilityWindowInfo info(profile, name); + + NotificationService::current()->Notify( + NotificationType::ACCESSIBILITY_WINDOW_OPENED, + Source<Profile>(profile), + Details<AccessibilityWindowInfo>(&info)); } |